C#/。Netの世界には、透過キャッシュを含むNHibernateやActiveRecordなどのORMがあります。データベースの更新は透過的にキャッシュに複製され、オブジェクトは利用可能な場合はキャッシュから直接取得されます(多くの場合memcachedを使用)。
透過的なキャッシュがDBIx::Classを使用するPerlで利用できるようには見えません。私は何か見落としてますか?それは一般的なニーズのようですが、CPANやGoogleで何も見つからなかったのには驚いています。
C#/。Netの世界には、透過キャッシュを含むNHibernateやActiveRecordなどのORMがあります。データベースの更新は透過的にキャッシュに複製され、オブジェクトは利用可能な場合はキャッシュから直接取得されます(多くの場合memcachedを使用)。
透過的なキャッシュがDBIx::Classを使用するPerlで利用できるようには見えません。私は何か見落としてますか?それは一般的なニーズのようですが、CPANやGoogleで何も見つからなかったのには驚いています。
半透過的に DBIx::Class::Cursor::Cached (DBIC のような mst から) があります。ただし、接続またはスキーマ オブジェクトに Cache オブジェクトを提供する必要があります。残念ながら、非常に文書化されていないようです。
クックブックには、DBIC で Tie::Cache を使用する例があり、DBIx::Class::ResultSet で (get|set|clear)_cache 関数もありますが、おそらく必要なものではないでしょう。
CHIでキャッシングを追加する簡単な方法を次に示します。私は実際にこれを試していないので、特に DBIC 結果セットのシリアル化に関して、私が考慮していない落とし穴があるかもしれません。
package My::Table;
use strict;
use warnings;
use base 'DBIx::Class';
use Storable 'freeze';
use CHI;
$Storable::canonical = 1;
__PACKAGE__->load_components(qw/Core/);
__PACKAGE__->table('mytable');
# ....
my $CACHE = CHI->new( driver => 'Memory' );
sub search {
my $self = shift;
my $key = freeze( \@_ ); # make cache key from params
if ( my $rs = $CACHE->get( $key ) ) {
return $rs;
}
# Note: there are issues with context propagation here
my $rs = $self->next::method( @_ );
$CACHE->set( $key => $rs );
return $rs;
}
sub update {
my $self = shift;
my @keys = $self->find_all_cache_items_affected_by_this_update( @_ );
$CACHE->remove( $_ ) for @keys;
$self->next::method( @_ );
}
少し不格好ですが、良い出発点だと思います。すべての DBIx::Class テーブル クラスの基底クラスでこの種のことを行う場合、透過的なキャッシングを非常に簡単に構築できるはずです。
私はDBIx::Classベースのモデルでこれと同じニーズに遭遇しました。ここで回答を確認した後、私が探しているソリューションであるものは実際には見つかりません。この問題に苦労した後、私は自分のビジネスレイヤーがキャッシュを処理する必要があると考え始めているので、DBIx::Classをビジネスロジックを実装しない永続レイヤーとして扱います。
たとえば、理想的なキャッシュを使用した現在のコードは次のようになります。
my $network = SL::Model::App->resultset('Network')->search({ ip => '127.0.0.1' });
また、$ networkオブジェクトは、DBIx::Classスキーマの初期化中に構成した$memcachedキャッシュから提供されます。
新しいコードは次のようになります。
my $network = SL::Network->find_by_ip_or_create({ ip => '127.0.0.1' });
一方、近くのモジュールでは:
package SL::Network;
...
use SL::Model::App;
use SL::Cache;
our $cache = SL::Cache->new;
sub find_by_ip_or_create {
my ($class, $args) = @_;
my $network;
unless ($network = $cache->get('network|' . $args->{ip}) {
$network = SL::Model::App->resultset('Network')->find_or_create({ wan_ip => $args->{ip}});
$cache->set('network|' . $args->{ip} => DBIx::Class::Schema->freeze($network));
}
return $network;
}
あなたはその考えを理解します。
に「検索」メソッドを追加する代わりに、それを追加したいと思いますMy::Table
。
DBIx::Class::ResultSet
次のように、によって提供される ->search メソッドを拡張することもできます。
package Schema::ResultSet::My::Table;
use base 'DBIx::Class::ResultSet';
sub search {
my ( $self, $args ) = ( shift, shift );
# do what you want here with the args passed to ->search
return $self->next::method( $args, @_ );
}
また、ResultSet をサブクラス化できる可能性が非常に高いため、この変更された ( cached ) 検索をすべての ResultSet に提供できるため、キャッシュ コードをすべてのテーブルの 1 か所に保持できます。
ただし、これはまだテストしていません。
上記の例を機能させるには、スキーマ クラスの名前のファイルをディレクトリ"../Schema/ResultSet/"
に配置し、Schema.pm に含まれていることを確認します。"load_namespaces();"
これにより、そこに配置したオーバーロードされたすべてのクラスが適切に自動ロードされます ( Catalyst のインストールは自動的にそれを行いましたが、覚えていません)。