Pixel Pedals of Tomakomai

北海道苫小牧市出身の初老の日常

DBICの disconnect VS DESTROY

最近、 DBIx::Class の利用例を色々見てるのですが、Schema->connect は見かけるのですがdisconnectを見かけません。疑問に思ったので、色々調べてみました。

結論

disconnectは不要。

調べたこと

disconnectはあるの?

DBIx::Class::Storage が持ってます。

my $schema = DB::Main->connect( ... );
# ... いろいろ処理 ...
$schema->storage->disconnect;
DBICのコネクションのライフサイクル

テストスクリプトを含めたほとんどのサンプルでは、接続が必要となった時点で自動的に接続され、プログラムの終了時に切断されます。

接続に関しては、実際には DBIx::Class::Storage::DBIで dbh_do が呼ばれるタイミングで、 _populate_dbh とか _connectが関係します。切断は DBI のDESTROYに頼っています。

接続で一つ注意が必要なのは DBIx::Class::Schema->connect の正体です。ドキュメントにも書いてますが、こいつは clone + connection なので、connectを呼んだだけでは接続は行われません。

また、切断に関してですが、DBICの内部で disconnect を呼んでる箇所はありません。 DBIx::Class::Storage::DBI のDESTROYでも、DBIの disconnect は呼びません。代わりに、明示的に undef してガベージコレクトを促進してます。

sub DESTROY {
  my $self = shift;
  return if !$self->_dbh;
  $self->_verify_pid;
  $self->_dbh(undef);  # ←これ
}
Catalystはどうしてるのか

Catalystでもdestroyしないのか調べました。

Catalystでは、DBICCatalyst::Model::DBIC::Schemaから利用されます。こいつはComponentですので、起動時にインスタンス化された後、Catalystのライフサイクルに合わせてそのまま生き残ります。このインスタンスのschemaフィールドに DBIx::Class::Schema を持っていますので、 DB接続もCatalystのライフサイクルと一緒にプログラムの終了まで生き残ることになります。

そして、肝心の切断処理なのですが、驚くことに Catalyst::Model::DBIC::Schema はdisconnectメソッドを持っていません。Catalyst-Runtime や Catalyst-Devel にも disconnect は一切登場しませんので、「DBIx::Class は disconnect をせずに DESTROY で切断させるもの」であるものとして利用していると言えます。

考察

DBIのドキュメントによると、 destroy した場合にトランザクションが commit されるか rollback されるかは未定義とされています。その一方で、DESTROYの場合は rollback されることが保証されています。

The transaction behaviour of the disconnect method is, sadly, undefined.

(中略)

The DESTROY method for each driver should implicitly call rollback to undo any uncommitted changes.
DBI

このことを考慮すると、DBIを利用する時は disconnect を直接使うよりも disconnect をラップした DESTROY を呼び出させる方が適していると言うことなのかもしれません。