Unknown::Programming このページをアンテナに追加 RSSフィード

2008-10-08 事情

DBD::mysqlのmysql_auto_reconnectが真だとDB再接続時にDBICのon_connect_doが実行されない件

ハマったメモ。

さてまた今回も若干適当な記事だけども、メモということでお許し願いたい。

ってか誰も言及してないっぽいんだけどもしかしてこの現象うちだけ?

とにかくタイトルの通りで、検証時の各モジュールのVERSIONは以下の通り。


DBD::mysql-v4.005
DBIx::Class-v0.08100

前回のDBD::mysqlでSegmentation faultの件は無事解決したものの、今度はDB再接続時に文字化け発動でゲンナリしてました。

でログとか見てるとどうもDBICのon_connect_doで指定しているSET NAMES UTF8がDB再接続時に動いてないことが判明。

myapp_server.plだとちゃんとSET NAMES呼ばれるんだけどmod_perlだと何故か呼ばれない。

ってことでmod_perlに関係有りそうな部分をひたすら調べていたところ、mysql_auto_reconnectに行き着きました。


# DBD::mysql-v4.005

    if ($this && ($ENV{MOD_PERL} || $ENV{GATEWAY_INTERFACE})) {
        $this->{mysql_auto_reconnect} = 1;
    }

mod_perlな環境の場合、問答無用で真になるこのフラグ

とにもかくにもコイツが真だと、DBの接続が死んでるにも関わらずDBIx::Class::Storage::DBI::connectedが何故か真を返す。

因みにDB再接続の検証コードは以下のような感じでいける。


    my $schema  = ...;
    my $schema2 = ...;

    if ( $schema->storage->connected ) {
        say 'kill before connect';
    }
    else {
        say 'kill before disconnect';
    }

    $schema2->storage->dbh->do( 'kill '.$schema->storage->dbh->{mysql_thread_id} );
    
    if ( $schema->storage->connected ) {
        say 'kill after connect';
    }
    else {
        say 'kill after disconnect';
    }

__END__


# mysql_auto_reconnect = 0の場合
kill before connect
kill after disconnect


# mysql_auto_reconnect = 1の場合
kill before connect
kill after connect   # <-なぜかconnectになる

内部ではmysql_auto_reconnectが設定されているのでDBD::mysql的には再接続処理が行われるが、DBIC的には接続が繋がったままだと認識してるのでDBICにおける再接続処理、つまりon_connect_doなどが呼ばれない。

なのでSET NAMES UTF8が呼ばれない、呼ばれないので文字化け。という流れでございます。

んーmysql_auto_reconnectの挙動としては外側のインターフェイスに再接続を意識させないという意味では正しい動きなのかもしれないけど、でもそのせいでon_connect_doが動かないのもなんだか微妙な気がする・・・。

あ!ちなみにMySQLデフォルトのキャラセットをUTF8にしとけばそもそもSET NAMESする必要いないだろ的な身も蓋も無いことは言わないで下さい><

色々事情がありまして、その環境ではデフォルトのキャラセットがEUC-JPなのです。

でまあ解決方法だけどんー。どうしようか迷い中。

とりあえず独自Storageを用意して



08/10/09追記:下記のコードだとlast_insert_idとかがコケるorz


package MyApp::DBIC::Storage;
use strict;
use warnings;
use base qw{DBIx::Class::Storage::DBI};

sub _connect {
    my $self = shift;
    my $dbh = $self->SUPER::_connect(@_);
    $dbh->{mysql_auto_reconnect} = 0 if $dbh;
    return $dbh;
}

1;

DBIx::Class::Storage::DBI::mysqlから派生しないとダメ!正しくは下記


package MyApp::DBIC::Storage;
use strict;
use warnings;
use base qw{DBIx::Class::Storage::DBI::mysql}; # mysqlから派生すること!

sub _connect {
    my $self = shift;
    my $dbh = $self->SUPER::_connect(@_);
    $dbh->{mysql_auto_reconnect} = 0 if $dbh;
    return $dbh;
}

1;

:追記終わり。



でmyapp.ymlにstorage_typeを指定


Model::Schema:
  storage_type: MyApp::DBIC::Storage
  connect_info:
    - dbi:mysql:myapp
    - user
    - passwd
    - on_connect_do:
        - 'SET NAMES utf8'

でやりました。

逆にmysql_auto_reconnectを使いたい場合の実装はどうなるだろう・・・。DBIconnectedメソッドを呼んで接続確認するとかかな?ま、いいや。

さてさてここまで書いておいて実は全然違う原因とかだったら泣けるけど><

とにかく一応コレでOKのはず。



ということでid:pepponさん、もしかしたらこの現象、ほっとくといつの間にか文字化けっていうのが丁度MySQL再接続のタイミングと一致してそうな感じがするのでうちと同じ症状かもしれないので良ければ試してみてはどうでしょう?

ま、全然関係ないかもしれませんが、その時はスミマセン><

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

Connection: close