RubyのAWS-SDKとForkでハマったこと

  • このエントリーをはてなブックマークに追加

RubyでKinesis Streamからレコードを取得する処理を書いていたのですが、通信が入れ替わったりして別のShardのShardIteratorが混じってくる現象に悩まされていました。

結論から言うと、AWS-SDKは内部でグローバルなConnection Poolを作ります。親プロセスで1回でもAWS-SDKを使って通信をすると、Forkした後に子供にも内部のConnection Poolがそのまま継承されます。
親で作られたCollectionPool=FDが複数の子供で共有されてしまうので、それぞれのプロセスで通信が混線してしまったという訳です。

解決策としては、子プロセスの最初に次のコードを実行する必要があります。

Seahorse::Client::NetHttp::ConnectionPool.pools.each do |pool|
pool.empty!
end

AWS-SDKのバージョンがv2.9.5以降であれば次のコードで同じコードが実行されます。

Aws.empty_connection_pools!

関連チケットです https://github.com/aws/aws-sdk-ruby/issues/1438

他にも使用していたLevelDBやSequelでもForkSafeではないのでそれぞれ対策が必要です。

LevelDBの場合

こんな感じで書き込みが止まってしまいました。
こっちは色々と掃除しても何故か発生してしまうので、親プロセスで使うなという感じ。

#0  0x00007f1e7929a64d in poll () from /lib64/libc.so.6
#1 0x00007f1e7a06c69c in ?? () from /usr/lib64/libruby.so.2.3
#2 0x00007f1e79cb4dc5 in start_thread () from /lib64/libpthread.so.0
#3 0x00007f1e792a4c9d in clone () from /lib64/libc.so.6
Thread 1 (Thread 0x7f1e7a56b740 (LWP 75934)):
#0 0x00007f1e79cb86d5 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
#1 0x00007f1e6db494ed in leveldb::port::CondVar::Wait() () from /data/app/smart-ad-rtm/vendor/bundle/ruby/2.3/gems/leveldb-0.1.9/ext/leveldb/libleveldb.so
#2 0x00007f1e6db1f4aa in leveldb::DBImpl::MakeRoomForWrite(bool) () from /data/app/smart-ad-rtm/vendor/bundle/ruby/2.3/gems/leveldb-0.1.9/ext/leveldb/libleveldb.so
#3 0x00007f1e6db22d38 in leveldb::DBImpl::Write(leveldb::WriteOptions const&, leveldb::WriteBatch*) () from /data/app/smart-ad-rtm/vendor/bundle/ruby/2.3/gems/leveldb-0.1.9/ext/leveldb/libleveldb.so
#4 0x00007f1e6db1f829 in leveldb::DB::Put(leveldb::WriteOptions const&, leveldb::Slice const&, leveldb::Slice const&) () from /data/app/smart-ad-rtm/vendor/bundle/ruby/2.3/gems/leveldb-0.1.9/ext/leveldb/libleveldb.so
#5 0x00007f1e6db1f869 in leveldb::DBImpl::Put(leveldb::WriteOptions const&, leveldb::Slice const&, leveldb::Slice const&) () from /data/app/smart-ad-rtm/vendor/bundle/ruby/2.3/gems/leveldb-0.1.9/ext/leveldb/libleveldb.so
#6 0x00007f1e6db1c7a8 in leveldb_put () from /data/app/smart-ad-rtm/vendor/bundle/ruby/2.3/gems/leveldb-0.1.9/ext/leveldb/libleveldb.so
#7 0x00007f1e6dd5fcec in ffi_call_unix64 () from /usr/lib64/libffi.so.6

https://github.com/google/leveldb/issues/169#issuecomment-260458097

Sequelの場合

接続を切りましょう

DB.disconnect

http://www.rubydoc.info/github/jeremyevans/sequel/Sequel%2FDatabase%3Adisconnect

まとめ

ライブラリの中身を気にしながらForkを考えるのは難しい。