2015/10/09

ActiveLdap を使っている時に PG がエラーを吐いて RSpec が落ちる

RSpec を使って Rails の並列リクエスト込みのテストを書いていると、

 PG::ConnectionBad: PQsocket() can't get socket descriptor
とか言われてテストがコケる。
テストだけじゃなくて Webrick でもエラーを吐く時は吐く。
実行する環境が Mac だとどうやっても通って Linux だとどうやっても通らない。

結論から言うと、 ActiveLDAP にタイムアウトを付けていると ActiveLDAP が fork するのでなんやかんや PG のソケットが足りなくなるから PG 側がエラーを吐いて死ぬっぽい。
fork が Process.fork してるのでたぶん OS 依存なバグになってややこしかった形。
対症療法としては activeldap の config の  timeout を 0 にして無効化すれば発生しなくなる。

環境

具体的に試した環境としては

OKな環境(Mac)

  • Rails : 4.2.4
  • Ruby : 2.2.2, 2.1.5, 2.0.0-p645
  • DB
    • PostgreSQL :  9.2.13, 9.3, 9.4 on Docker (1.8.0) on VirtualBox (5.0.0) on Mac OSX Yosemite
    • MySQL : 5.7 on Docker (1.8.0) on VirtualBox (5.0.0) on Mac OSX Yosemite
  • OpenLDAP : 2.4.39 on Docker (1.8.0) on VirtualBox (5.0.0) on Mac OSX Yosemite
  • ActiveLDAP :  4.0.4
  • pg :  0.18.2, 0.18.1, 0.18.0, 0.17.1, 0.17.0
  • RSpec 実行OS Mac OSX Yosemite
OK な環境(Linux)
  • Rails 4.2.4
  • Ruby : 2.2.2
  • MySQL : 5.7 on Docker (1.8.0) on VirtualBox (5.0.0) on Mac OSX Yosemite
  • OpenLDAP : 2.4.39 on Docker (1.8.0) on VirtualBox (5.0.0) on Mac OSX Yosemite
  • ActiveLDAP :  4.0.4
  • pg :  0.18.2, 0.18.1, 0.18.0, 0.17.1, 0.17.0
  • RSpec 実行OS : CentOS 7, Fedora 20
NG な環境(Linux)
  • Rails 4.2.4
  • Ruby : 2.2.2, 2.1.5, 2.0.0-p645
  • PostgreSQL : 9.2.13, 9.3, 9.4 on Docker (1.8.0) on VirtualBox (5.0.0) on Mac OSX Yosemite
  • OpenLDAP : 2.4.39 on Docker (1.8.0) on VirtualBox (5.0.0) on Mac OSX Yosemite
  • ActiveLDAP :  4.0.4
  • pg : 0.18.2, 0.18.1, 0.18.0, 0.17.1, 0.17.0
  • RSpec 実行OS : CentOS 7, CentOS 7 and Fedora 20 on VirtualBox, boot2docker 1.8.0
まとめると
  • Mac なら DB を PostgreSQL にしても MySQL にしても何でもOK
  • Linux だと DB を PostgreSQL にするとたまに再現
  • Linux だと DB を MySQL にすると再現せず
  • postgresql-server や postgresql-devel のバージョンを変えても問題は解決せず

原因調査の流れ

まずは gem のバージョンを変える。なるべく最新へ。解決せず。
gem のバージョンを過去のバージョンにもする。解決せず。
次に PostgreSQL も最新にする。解決せず。
クライアント側の postgresql-devel も最新に。 解決せず。
とりあえず pg を読む。
 PG::ConnectionBad: PQsocket() can't get socket descriptor
は 'pg' の gem の native extension で、PostgreSQL への socket が取れない時に投げられるらしい。
socket が取れないとのことなので pool を増やしたりもしたけれど解決せず。
MySQL にしてみる。 Linux でも動く。どうやら問題はDBだとアタリを付ける(結果的にははずれ)
動かす Linux の kernel を上げたりする。(CentOS でも Fedora でも動かず。 2.x でも 3.x でもダメ)
Linux を物理マシンに乗っけるも動かず。
試しに rails new したやつで並列にリクエストをしても捌く。どうやら原因はデフォルトの gem では無いらしい。
そうこうしてると Linux 側でコアダンプ吐いて Ruby が死ぬ。 stack trace を見ると ActiveLDAP の様子。
ということで覗いて見ると Process.fork とかしてるらしい
fork した先で PG の connection を使いまくって PG が正しいソケット取れないのかな、と推測してとりあえず timeout を切る。
これで解決。再現せず。長かった……

とりあえず怪しいのは ActiveLDAP の fork 方法か Ruby の Linux 側の fork 。原因を追求したい気もするけれど今はちょっと放置。

0 件のコメント:

コメントを投稿