ある環境下のCatalystでDBIx::Class::Schema::Loaderが落ちるのを修正
久々にCatalystアプリをApache2/worker+ModPerl2な環境で動かしてみたところ、
という例外が発生するようになってしまいました。
DBIx::Class::ResultSet::find(): no sth generated via sql (DBD::mysql::db STORE failed: handle 2 is owned by thread 926fb18 not current thread 9bf0448 (handles can't be shared between threads and your driver may need a CLONE method added) at /usr/lib/perl5/5.8/lib/DBIx/Class/Storage/DBI.pm line 524.
DBIC周りの変更と言えば、DBIx::Class::Schema::Loaderを使ってSchemaクラスをダンプしていたのを起動時にロードするようにしたことでした。環境がApache2/worker + ModPerl2だと言えば、エラーメッセージと併せるとだいたいの検討は付くのではないでしょうか。
DBIx::Class::Storage::DBIのソースを見ると、現在のPIDと接続したときのPIDとが異なっている場合に、DBI->{InactiveDestroy}がセットされた上で切断されるようになっていました。
Apacheは起動時のプロセスIDと起動後のプロセスのIDが変わってしまうので、それに引っかかっているようです。
と原因は分かりましたが、ここからどうすれば良いか分からずネットを巡回。するとGetting the most from DBIx::Class::Loader - SolvedIssues - Catalystというのを見つけました。スキーマをロードした後に一度切断すれば良いようです。ただ、DBIx::Class::Schema::Loaderでどうすれば良いのか分からなかったので、ロードメソッドを上書きしました。(ちょっと汚い)
package MyApp::Schema; use base qw(DBIx::Class::Schema::Loader); # patch for multi thread sub _invoke_loader { my $self = shift; my $retval = $self->SUPER::_invoke_loader(@_); $self->loader->schema->storage->disconnect; return $retval; } __PACKAGE__->loader_options( relationships => 1 );
追記
検証はしませんが、MyApp::Mode::DBICにて
sub COMPONENT { my $class = shift; my $self = $class->next::method(@_); $self->schema->storage->disconnect; return $self; }
でいけるかもしれません。@see Catalyst::Manual::ExtendingCatalyst
更に追記
id:hirataraさんが検証してくださいまして、上記のCOMPONENTの定義でいけたそうです。どうもありがとうございました。