DBIx::Class 是否具有透明缓存?

发布于 2024-08-01 21:17:35 字数 283 浏览 4 评论 0原文

在 C#/.Net 世界中,有些 ORM(例如 NHibernate 或 ActiveRecord)包含透明缓存:数据库更新透明地复制到缓存,对象可用时直接从缓存检索,等等(通常使用 memcached)。

Perl 中的 DBIx::Class 似乎无法提供透明缓存。 我错过了什么? 这似乎是一个常见的需求,我很惊讶我在 CPAN 或 Google 上找不到任何相关内容。

In the C#/.Net world, there are ORMs such as NHibernate or ActiveRecord that includes transparent caching: database updates are transparently replicated to the cache, objects are retrieved directly from the cache when available, etc (often with memcached).

It doesn't look like transparent caching is available in Perl with DBIx::Class. Did I miss something? That seems like a common need, I'm surprised I couldn't find anything on it on CPAN or Google.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

混浊又暗下来 2024-08-08 21:17:35

半透明的是 DBIx::Class::Cursor::Cached (来自 mst,如 DBIC)。 不过,您需要为连接或架构对象提供一个缓存对象。 不幸的是,似乎非常无证。

Cookbook 确实有一个在 DBIC 上使用 Tie::Cache 的示例,并且 DBIx::Class::ResultSet 上也有 (get|set|clear)_cache 函数,但它们可能并不完全是您所需要的。

Semi-transparently there is DBIx::Class::Cursor::Cached (from mst, like DBIC). You need to provide a Cache object to your connections or schema objects though. Seems very undocumented unfortunately.

The Cookbook does have an example for using Tie::Cache on DBIC, and there are also the (get|set|clear)_cache functions on DBIx::Class::ResultSet, but they are probably not exactly what you need.

小耗子 2024-08-08 21:17:35

这是一种使用 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 表类的基类中执行此类操作,那么您应该能够非常轻松地构建透明缓存。

Here's a simple way that you could add caching with CHI. I haven't actually tried this, so there may be pitfalls I haven't considered, especially with regard to the serialization of DBIC result sets.

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( @_ );
}

It's a bit clunky, but I think it's a good starting point. If you do this type of thing in a base class for all your DBIx::Class table classes, you should be able to build in transparent caching pretty easily.

怼怹恏 2024-08-08 21:17:35

我在基于 DBIx::Class 的模型中遇到了同样的需求,在查看此处的答案后,我并没有真正看到任何我正在寻找的解决方案。 在解决这个问题之后,我开始认为我的业务层应该处理缓存,因此我将 DBIx::Class 视为不实现业务逻辑的持久层。

例如,我当前具有理想缓存的代码将是这样的:

my $network = SL::Model::App->resultset('Network')->search({ ip => '127.0.0.1' });

$network 对象是从我在期间配置的 $memcached 缓存提供的
DBIx::Class 模式初始化

新代码将是:

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;

}

您明白了。

I have come across this same need with my DBIx::Class based model, and after reviewing the answers here I don't really see anything that is the solution I'm looking for. After struggling with this issue, I'm starting to think that my business layer should handle the caching, so that I treat DBIx::Class as a persistence layer that does not implement business logic.

For example, my current code with ideal caching would be something like this:

my $network = SL::Model::App->resultset('Network')->search({ ip => '127.0.0.1' });

And the $network object is served from the $memcached cache that I configured during
DBIx::Class schema initialization

The new code would be:

my $network = SL::Network->find_by_ip_or_create({ ip => '127.0.0.1' });

Meanwhile, in a nearby module:

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;

}

You get the idea.

十雾 2024-08-08 21:17:35

我想补充一点,除了在 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 进行子类化,以便可以向所有 ResultSet 提供此更改(缓存)搜索,从而将所有表的缓存代码保留在一个位置,
恕我直言,这会不会那么混乱。

不过,我还没有测试过。

要使上面的示例正常工作,请将其放入目录 “../Schema/ResultSet/” 中具有模式类名称的文件中,并确保您的 Schema.pm 包含 “load_namespaces();” 它将很好地自动加载您放置在那里的所有重载类(我认为我的 Catalyst 安装会自动执行此操作,但我不记得了)。

DBIx::Class::ResultSet

I would like to add that, instead of adding a 'search' method in My::Table,

one can also enhance the ->search method provided by DBIx::Class::ResultSet, like so:

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, @_ );
}

Also, you can very likely subclass ResultSet so you can provide this altered ( cached ) search to all ResultSets, thus keeping the caching code in one place for all tables,
which would be a lot less messy imho.

This I have not tested yet, though.

To make the above example work, put it in a file with the name of your schema class, in the directory "../Schema/ResultSet/", and make sure your Schema.pm contains "load_namespaces();" which will nicely auto-load all your overloaded classes that you put there ( I think my Catalyst install did that automatically, but I do not recall ).

DBIx::Class::ResultSet

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文