2025/11/04 12:36 When Your Hash Becomes a String: Hunting Ruby's Million-to-One Memory Bug

ロボ子、大変なのじゃ!Karafka gemで、ありえないエラーが出たらしいぞ!

エラーですか?どんなエラーでしょう?

`NoMethodError: undefined method 'default' for an instance of String`!文字列に`default`メソッドがないって怒られたらしい。

文字列に`default`メソッドですか…。一体なぜそんなエラーが?

それがの、FFIの内部ハッシュがGCで解放されて、同じメモリアドレスにStringオブジェクトが割り当てられたのが原因らしいのじゃ!

FFIのハッシュがGCで解放…?それは一体どういうことですか?

FFIっていうのは、RubyからCのコードを呼び出す仕組みのことじゃ。その中で、Cの構造体をRubyで扱うために`FFI::Struct`を使うんじゃけど、その内部でハッシュを使っているらしい。

なるほど。そのハッシュがGCで解放されてしまうと?

そう!FFI 1.17.0より前のバージョンには、write barrierっていうGCに対するオブジェクト参照の通知メカニズムが欠けている箇所があって、GCが不要なオブジェクトを解放しちゃうことがあったらしいのじゃ。

write barrierがないと、GCが誤って必要なオブジェクトを解放してしまうことがあるんですね。

そういうこと!で、解放されたメモリ領域に別のオブジェクト(このケースではString)が割り当てられると、Cコードが古いポインタを使ってアクセスした際に型エラーが発生する、と。

つまり、本来ハッシュであるべき場所に文字列が入ってしまって、`default`メソッドを呼び出そうとしてエラーになった、ということですね。

その通り!しかも、このエラー、通常の環境では100万分の1程度の低い確率でしか発生しないらしい。でも、Kubernetesやサーバーレス環境のように再起動が頻繁に行われる環境では、発生頻度が高まるらしいぞ。

再起動が多い環境だと、GCのタイミングが影響して発生しやすくなるんですね。対策としては、FFI gemを1.17.0以降にアップデートすれば良いんですね。

そう!アップデートすればwrite barrierが追加されて、この問題は解決するはずじゃ。しかし、オブジェクトの永続的なアイデンティティがないというRubyのメモリ管理の根本的な性質を考えると、ちょっと怖い話じゃな。

本当にそうですね。表面的な診断だけでなく、もっと深いレイヤーでの検証が必要だと学びました。

まさに!今回の教訓は、問題解決には忍耐と継続的な努力が重要ってことじゃな!

はい、博士!

しかし、まさか文字列がハッシュのフリをするとは…まるで、私がロボ子のフリをするみたいじゃな!

博士、私は博士のフリはできませんよ!
⚠️この記事は生成AIによるコンテンツを含み、ハルシネーションの可能性があります。
