nipotan method RSSフィード

 | 

2007-10-16

Data::ObjectDriver を使ってみて  Data::ObjectDriver を使ってみて - nipotan method を含むブックマーク はてなブックマーク -  Data::ObjectDriver を使ってみて - nipotan method  Data::ObjectDriver を使ってみて - nipotan method のブックマークコメント

最近 DBIC 人気に、ひどく翳りがあるし、Changes に名前が書かれていたりとかもあるので、とりあえず Data::ObjectDriver をちゃんと使ってみようかなと思った。

最初チョロっと使ってみた感想は、作られたインスタンス最近のゴテゴテした ORM と違ってかなり軽量。

あと、無駄にコネクションを作らない。

そういう点では非常に高速なイメージ


ただ、使い慣れてないうちに使うにはデメリットも目立つ。


一つは、ドキュメントが無さすぎること。


perldoc も熟読してみたが、使い熟すレベルまでのドキュメントは充実していない。

当然、コード嫁って話なわけだが、なかなか膨大なのと、透過的に他のクラスのメソッドを更に他のクラスから呼べるように proxy してたりするので、実装が異なる同名のメソッドが多くて混乱しがち。

一通り追ってはみたが、なかなか結構混乱する。

ついでに、色々とネットで調べても、少なくとも日本語情報はまず見付からない上に、英語情報もあまり見付からない。


二つめは、master + slave 構成を作るのが面倒な気がする。


Data::ObjectDriver::Driver::DBI に、rw_handle() と r_handle() という、読み書き用、読み用のハンドル (dbh) を返すメソッドが存在して、処理内容に応じて適宜ハンドルを選択している風なんだが、デフォルトでは r_handle() は rw_handle() のリファレンスになっている。

slave 用に自分で r_handle() を override して実装しなくてはいけない模様なんだが、r_handle() の実装方法が実に困難 (素の DBI->connect() で作ったハンドラを返せばいいっぽいが正しいかわからないw) なのと、slave が複数台数の場合に、どうラウンドロビンさせるかを考えた時に、r_handle() から slave の datasource の情報を複数使って返すメソッドを定義したりして、そういうアクセサを追加した俺様 Driver::DBI を作らないといけないっぽい。


三つめは、Driver::Cache::Memcached とかを使って、memcached と透過的に (気にすることなく) object を取ったり出来るんだけど、expire されないから、テーブルを直にいじったりすると戻せなくなったり (まぁ、それは自前で実装してもそうだけど)、なんか変な拍子で cache だけ残ったりするんじゃないかと不安になる。


四つめは、どうもパーティショニングしている構成 (PRIMARY KEY が親キーと別の値の複合キー) におけるクラスで、PRIMARY KEY の親キーじゃないほうに auto_increment 属性をつけておいて、INSERT した時 ($obj->save() した時)、採番された値 (mysql_insertid とか) を取ってきた後に、親キーのほうを上書いてしまう問題があるっぽい。

package Bar;

use strict;
use Data::ObjectDriver::Driver::Cache::Memcached;
use Cache::Memcached;
use base qw( Data::ObjectDriver::BaseObject );

__PACKAGE__->install_properties({
    columns     => [qw(id foo_id bar timestamp)],
    datasource  => 'bar',
    primary_key => ['foo_id', 'id'],
    driver      => Data::ObjectDriver::Driver::Cache::Memcached->new(
        cache    => Cache::Memcached->new({servers => ['127.0.0.1:11211']}),
        fallback => Data::ObjectDriver::Driver::SimplePartition->new(
            using => 'Foo',
        ),
    ),
});

1;

これの id が auto_increment になっている。

ちなみに、Driver::SimplePartition を使っているので、Foo クラスが抽象化している foo テーブルには、partition_id というカラムがあり、その値を元にパーティションされた datasource を取ってきて driver を返すようにしている。

package main;

use strict;
use Foo;
use Bar;

my $foo = Foo->lookup(801);
my $bar = Bar->new(
    foo_id => $foo->foo_id,
    bar    => 'jitensya',
);
$bar->save;
warn $bar->id;

こうやると、$bar->id は undef になってしまう。

そのかわり、$bar->foo_id に auto_increment で採番された id の値が入ってしまう。

この例で言えば、希望としては、$bar->foo_id は 801 を返し、id が採番された値を返して欲しい。


Driver::DBI では、一応、これでいいのか的にコメントが書いてあるので、バグになりうる可能性自体は認識されている様子?

        my $pk = $obj->primary_key_tuple; ## but do that only for relation that aren't PK-less
        my $id_col = $pk->[0]; # XXX are we sure we will always use '0' ?
        my $id = $dbd->fetch_id(ref($obj), $dbh, $sth);
        $obj->$id_col($id);

でも、Driver::SimplePartition のドキュメントには、

Your primary key should be a complex primary key (arrayref) with the simple key of the parent object for the first field.

と書いてあって、実際にその通りにしないと、Driver::SimplePartition の中で

my $col = $class->primary_key_tuple->[0];

こうやって PRIMARY KEY の最初のキーが親 (パーティショニングの大元になる情報) としているから、順序を変えると driver が取れなくなってしまう。


このように要求と実装が矛盾しているような状態で、ここで言えば、そもそも $pk->[0] のカラムに auto_increment の 値を入れてしまうのは間違いなんじゃないかと思ってしまう。


じゃあ、パーティショニングしている側では save() した時に都度上書けばいいだろ的な BK しか思い浮かばない。



ということで、Data::ObjectDriver はデフォルトではあまり使い勝手はよくなく、すごく時間がかかりそうです。

というか、何にしてもノウハウの蓄積が欲しいとこです。

 |