kmuto’s blog

はてな社でMackerel CREをやっています。料理と旅行といろんなIT技術

消えたバイナリプロトコル。その謎を解明するため、我々調査隊はOBIの奥地へ飛んだ

PHP Laravel+MySQL(MariaDB)環境において、雑なmysqliやPDO呼び出しではなく、Eloquent ORM経由にしている場合に、「OBIでSQLクエリのスパンが出てこない」という症状が起きていて調査していた。

発生条件自体は絞り込めたが、設定が悪いのかOBIに問題があるのかは結局わかっていない……というか条件の絞り込み自体が怪しいかもしれない。

発生条件の外形的な調査

PHP 7.4+MariaDB+OBIの環境をもとに試していた(元フォルダ)。8000ポートでPHPアプリケーションコンテナが動作し、別コンテナのMariaDBの3306ポートに適宜通信が走る。

  • 過去、EloquentではなくmysqliやPDOを直接利用するコードではOBI経由でSQLクエリスパンを生成できていた
  • ELoquent化したことで生成されなくなった。クエリと結果は正常
  • ELoquentのプリペアドステートメントはSTMT_PREPAREでテンプレートを送り、実行時はバイナリ形式でパラメータのみを送る(COM_STMT_EXECUTE)らしい
  • 試しにLaravelのconfig/database.phpmysql設定にPDO::ATTR_EMULATE_PREPARES => trueを追加したところ、SQLクエリスパンが生成された

プリペアドステートメントを送る方法(特にバイナリプロトコル)に何かありそうだという推測が立つ。

  • PHPコンテナとOBIコンテナを分けている(OBIコンテナはpid: 'host'privileged: trueでPHPコンテナプロセスにタッチできる)
  • コンテナを使わない素に近い状態の真似として、PHPコンテナ内でOBIバイナリも実行してみる(タッチするためにPHPコンテナにprivileged: trueを設定)
  • バイナリプロトコルのままでも、SQLクエリスパンが生成された

う〜ん。コンテナをまたぐところで何かありそうだろうか。OBIはローカルのファイルは見ずに通信内容を見ているだけという理解なのだが。

CoPilotに調査依頼

Geminiのチャットで聞いていたが、プロセスのforkの可能性に固執するなど、実になる回答は得られなかった。

GitHub CoPilotで調査する(モデルはAuto)。しかし、かなり長くやってみたが、良い結果は得られなかった。

<以下Geminiでの要約を当初ここに書いていたが…>

急転直下

PDO::ATTR_EMULATE_PREPARES => trueを追加したところ、SQLクエリスパンが生成された」と前述していたが、macOSのRancherではこれが効果を発揮していたのに対し、Linuxのdocker-ceではこのフラグの有効無効に関わらずOBIで拾えていないことに気付いた。

調査を双方のOSで実施していたのに加え、送り先も同じところだったので完全に見落としていた。テキストプロトコルにしてもダメな理由はまだ突き止められていない。