Data-ObjectDriver
view release on metacpan or search on metacpan
lib/Data/ObjectDriver/Driver/BaseCache.pm view on Meta::CPAN
# $Id$
package Data::ObjectDriver::Driver::BaseCache;
use strict;
use warnings;
use base qw( Data::ObjectDriver Class::Accessor::Fast
Class::Data::Inheritable );
use Carp ();
__PACKAGE__->mk_accessors(qw( cache fallback txn_buffer));
__PACKAGE__->mk_classdata(qw( Disabled ));
sub deflate { $_[1] }
sub inflate { $_[2] }
# subclasses must override these:
sub add_to_cache { Carp::croak("NOT IMPLEMENTED") }
sub update_cache { Carp::croak("NOT IMPLEMENTED") }
sub remove_from_cache { Carp::croak("NOT IMPLEMENTED") }
sub get_from_cache { Carp::croak("NOT IMPLEMENTED") }
sub init {
my $driver = shift;
$driver->SUPER::init(@_);
my %param = @_;
$driver->cache($param{cache})
or Carp::croak("cache is required");
$driver->fallback($param{fallback})
or Carp::croak("fallback is required");
$driver->txn_buffer([]);
$driver;
}
sub begin_work {
my $driver = shift;
my $rv = $driver->fallback->begin_work(@_);
$driver->SUPER::begin_work(@_);
return $rv;
}
sub commit {
my $driver = shift;
return unless $driver->txn_active;
my $rv = $driver->fallback->commit(@_);
$driver->debug(sprintf("%14s", "COMMIT(" . scalar(@{$driver->txn_buffer}) . ")") . ": driver=$driver");
while (my $cb = shift @{$driver->txn_buffer}) {
$cb->();
}
$driver->SUPER::commit(@_);
return $rv;
}
sub rollback {
my $driver = shift;
return unless $driver->txn_active;
my $rv = $driver->fallback->rollback(@_);
$driver->debug(sprintf("%14s", "ROLLBACK(" . scalar(@{$driver->txn_buffer}) . ")") . ": driver=$driver");
$driver->txn_buffer([]);
$driver->SUPER::rollback(@_);
return $rv;
}
sub cache_object {
my $driver = shift;
my($obj) = @_;
return $driver->fallback->cache_object($obj)
if $driver->Disabled;
## If it's already cached in this layer, assume it's already cached in
## all layers below this, as well.
unless (exists $obj->{__cached} && $obj->{__cached}{ref $driver}) {
$driver->modify_cache(sub {
$driver->add_to_cache(
lib/Data/ObjectDriver/Driver/BaseCache.pm view on Meta::CPAN
}
}
\@got;
}
## We fallback by default
sub fetch_data {
my $driver = shift;
my ($obj) = @_;
return $driver->fallback->fetch_data($obj);
}
sub search {
my $driver = shift;
return $driver->fallback->search(@_)
if $driver->Disabled;
my($class, $terms, $args) = @_;
## If the caller has asked only for certain columns, assume that
## he knows what he's doing, and fall back to the backend.
return $driver->fallback->search(@_)
if $args->{fetchonly};
## Tell the fallback driver to fetch only the primary columns,
## then run the search using the fallback.
local $args->{fetchonly} = $class->primary_key_tuple;
## Disable triggers for this load. We don't want the post_load trigger
## being called twice.
local $args->{no_triggers} = 1;
my @objs = $driver->fallback->search($class, $terms, $args);
my $windowed = (!wantarray) && $args->{window_size};
if ( $windowed ) {
my @window;
my $window_size = $args->{window_size};
my $iter = sub {
my $d = $driver;
while ( (!@window) && @objs ) {
my $objs = $driver->lookup_multi(
$class,
[ map { $_->primary_key }
splice( @objs, 0, $window_size ) ]
);
# A small possibility exists that we may fetch
# some IDs here that no longer exist; grep these out
@window = grep { defined $_ } @$objs if $objs;
}
return @window ? shift @window : undef;
};
return Data::ObjectDriver::Iterator->new($iter, sub { @objs = (); @window = () });
} else {
## Load all of the objects using a lookup_multi, which is fast from
## cache.
my $objs = $driver->lookup_multi($class, [ map { $_->primary_key } @objs ]);
return $driver->list_or_iterator($objs);
}
}
sub update {
my $driver = shift;
my($obj) = @_;
return $driver->fallback->update($obj)
if $driver->Disabled;
my $ret = $driver->fallback->update(@_);
my $key = $driver->cache_key(ref($obj), $obj->primary_key);
$driver->modify_cache(sub {
$driver->uncache_object($obj);
});
return $ret;
}
sub replace {
my $driver = shift;
my($obj) = @_;
return $driver->fallback->replace($obj)
if $driver->Disabled;
# Collect this logic before $obj changes on the next line via 'replace'
my $has_pk = ref $obj && $obj->has_primary_key;
my $ret = $driver->fallback->replace($obj);
if ($has_pk) {
my $key = $driver->cache_key(ref($obj), $obj->primary_key);
$driver->modify_cache(sub {
$driver->update_cache($key, $driver->deflate($obj));
});
}
return $ret;
}
sub remove {
my $driver = shift;
my($obj) = @_;
return $driver->fallback->remove(@_)
if $driver->Disabled;
if ($_[2] && $_[2]->{nofetch}) {
## since direct_remove isn't an object method, it can't benefit
## from inheritance, we're forced to keep things a bit obfuscated here
## (I'd rather have a : sub direct_remove { die "unavailable" } in the driver
Carp::croak("nofetch option isn't compatible with a cache driver");
}
if (ref $obj) {
$driver->uncache_object($obj);
}
$driver->fallback->remove(@_);
}
sub uncache_object {
my $driver = shift;
my($obj) = @_;
my $key = $driver->cache_key(ref($obj), $obj->primary_key);
return $driver->modify_cache(sub {
delete $obj->{__cached};
$driver->remove_from_cache($key);
$driver->fallback->uncache_object($obj);
});
}
sub cache_key {
( run in 0.705 second using v1.01-cache-2.11-cpan-39bf76dae61 )