ある環境下の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の定義でいけたそうです。どうもありがとうございました。