DBICの再接続

DBICでバックエンドとの接続が切れた時などに再接続する際の挙動が、0.07x と 0.08x で異なるようなのでメモ。確認したのは PostgreSQL の場合です。
動作確認スクリプトは末尾に。動作は以下の流れ。

  • connect()
  • resultset から find()
  • バックエンドをkill
  • resultset から find()

0.07006 の場合。

$ perl -IDBIx-Class-0.07006/blib/lib/ dbic.pl
DBIx::Class->VERSION: 0.07006
get ok
19115
killing 19115
FATAL:  terminating connection due to administrator command
get ok

正常に再接続可能。
0.08007 の場合。

$ perl -IDBIx-Class-0.08007/blib/lib/ dbic.pl
DBIx::Class->VERSION: 0.08007
get ok
19121
killing 19121
FATAL:  terminating connection due to administrator command
DBIx::Class::ResultSet::find(): DBI Exception: DBD::Pg::st execute failed: server closed the connection unexpectedly
        This probably means the server terminated abnormally
        before or while processing the request.
 [for Statement "SELECT me.id, me.foo FROM foo me WHERE ( ( me.id = ? ) )" with ParamValues: 1='1'] at dbic.pl line 25

2回目の find で再接続出来なくてエラー(例外)になる。
該当する変更は http://search.cpan.org/src/ASH/DBIx-Class-0.08007/Changes を見る限り、以下の部分かなあ。

0.07999_01 2006-10-05 21:00:00
        - Storage::DBI now uses exceptions instead of ->ping/->{Active} checks

で、これは $schema->storage->ensure_connected() を呼んで、接続状態を確認することで回避できた。
Catalyst から使う場合は、begin か prepare あたりで呼んでやればいいかな。

#!/usr/bin/perl
use strict;
use Perl6::Say;

package MySchema;
use base qw/ DBIx::Class::Schema::Loader /;
__PACKAGE__->loader_options();

package main;
our $schema = init();
get();
kill_backend();
get();

sub init {
    say "DBIx::Class->VERSION: ", DBIx::Class->VERSION;
    MySchema->connect(
        'dbi:Pg:dbname=fujiwara', 'fujiwara', '',
        { RaiseError => 1, AutoCommit => 0, }
    );
}
sub get {
    $schema->storage->ensure_connected() if $ENV{ENSURE};
    eval { $schema->resultset('Foo')->find(1) };
    say $@ ? $@ : "get ok";
    $schema->txn_commit();
}
sub kill_backend {
    my $pid = <>;
    chomp $pid;
    say "killing $pid";
    qx{ kill $pid };
}