Skip Menu |

This queue is for tickets about the Cache-LRU CPAN distribution.

Report information
The Basics
Id: 109197
Status: new
Priority: 0/
Queue: Cache-LRU

People
Owner: Nobody in particular
Requestors: ZARQUON [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: (no value)
Fixed in: (no value)



Subject: Patch to enable manual management of the cache
In some situations it's desirable to manage when the cache is sized to the max length. Here's a patch with a test to enable that.
Subject: manual_size_management.patch
diff --git a/lib/Cache/LRU.pm b/lib/Cache/LRU.pm index 31be5ec..8597ff5 100644 --- a/lib/Cache/LRU.pm +++ b/lib/Cache/LRU.pm @@ -23,7 +23,6 @@ sub new { sub set { my ($self, $key, $value) = @_; - my $entries = $self->{_entries}; if (my $old_value_ref = $entries->{$key}) { @@ -34,15 +33,19 @@ sub set { my $value_ref = \$value; Scalar::Util::weaken($entries->{$key} = $value_ref); $self->_update_fifo($key, $value_ref); + $self->_auto_expire; + $value; +} +sub _auto_expire { + my ($self) = @_; + my $entries = $self->{_entries}; # expire the oldest entry if full while (scalar(keys %$entries) > $self->{size}) { my $exp_key = shift(@{$self->{_fifo}})->[0]; delete $entries->{$exp_key} - unless $entries->{$exp_key}; + unless $entries->{$exp_key}; } - - $value; } sub remove { @@ -90,6 +93,13 @@ sub _update_fifo { } } +sub force_update { + my ($self, $real_size) = @_; + return if $self->{size} != 'inf'; + local $self->{size} = $real_size; + $self->_auto_expire; +} + 1; __END__ @@ -137,6 +147,13 @@ Removes data associated to the given key and returns the old value, if any. Removes all entries from the cache. +=head2 $cache->force_update($real_size) + +In the case you want to manually manage the cache length yourself, set +$cache->{size} to be "inf" and pass in $real_size into this method at +a point in the code of your chosing (e.g. if $cache is a package +variable, maybe in DESTROY). + =head1 AUTHOR Kazuho Oku diff --git a/t/manual_management.t b/t/manual_management.t new file mode 100644 index 0000000..0ddd989 --- /dev/null +++ b/t/manual_management.t @@ -0,0 +1,61 @@ +use strict; +use warnings; + +use Test::More; + +BEGIN { + use_ok("Cache::LRU"); +}; + +my $cache = Cache::LRU->new( + size => "inf", +); + +my $real_size = 3; + +ok ! defined $cache->get('a'); + +is $cache->set(a => 1), 1; +is $cache->get('a'), 1; + +is $cache->set(b => 2), 2; +is $cache->get('a'), 1; +is $cache->get('b'), 2; + +is $cache->set(c => 3), 3; +is $cache->get('a'), 1; +is $cache->get('b'), 2; +is $cache->get('c'), 3; + +is $cache->set(b => 4), 4; +is $cache->get('a'), 1; +is $cache->get('b'), 4; +is $cache->get('c'), 3; + +my $keep; +is +($keep = $cache->get('a')), 1; # the order is now a => c => b +is $cache->set(d => 5), 5; +is $cache->get('a'), 1; +$cache->force_update($real_size); +ok ! defined $cache->get('b'); +is $cache->get('c'), 3; +is $cache->get('d'), 5; # the order is now d => c => a + +is $cache->set('e', 6), 6; +$cache->force_update($real_size); +ok ! defined $cache->get('a'); +ok ! defined $cache->get('b'); +is $cache->get('c'), 3; +is $cache->get('d'), 5; +is $cache->get('e'), 6; + +is $cache->remove('d'), 5; +is $cache->get('c'), 3; +ok ! defined $cache->get('d'); +is $cache->get('e'), 6; + +$cache->clear; +ok ! defined $cache->get('c'); +ok ! defined $cache->get('e'); + +done_testing;
Actually I think the patch is misconceived. However the way the code works does not allow subclassable expiry logic, which there could be a good reason for. Here's a patch for that instead (very similar).
Subject: lru.diff
diff --git a/lib/Cache/LRU.pm b/lib/Cache/LRU.pm index 31be5ec..9fb3674 100644 --- a/lib/Cache/LRU.pm +++ b/lib/Cache/LRU.pm @@ -23,7 +23,6 @@ sub new { sub set { my ($self, $key, $value) = @_; - my $entries = $self->{_entries}; if (my $old_value_ref = $entries->{$key}) { @@ -34,15 +33,19 @@ sub set { my $value_ref = \$value; Scalar::Util::weaken($entries->{$key} = $value_ref); $self->_update_fifo($key, $value_ref); + $self->_auto_expire; + $value; +} +sub _auto_expire { + my ($self) = @_; + my $entries = $self->{_entries}; # expire the oldest entry if full while (scalar(keys %$entries) > $self->{size}) { my $exp_key = shift(@{$self->{_fifo}})->[0]; delete $entries->{$exp_key} - unless $entries->{$exp_key}; + unless $entries->{$exp_key}; } - - $value; } sub remove { @@ -137,6 +140,31 @@ Removes data associated to the given key and returns the old value, if any. Removes all entries from the cache. +=head2 MANUAL EXPIRY MANAGEMENT + +WARNING: This is probably a bad idea, but it might be right for you +(beware). + +In the case you want to manually manage the cache expiry (e.g. in a +DESTROY mehtod), you can subclass Cache::LRU. + + package MyCache::LRU::Manual; + use parent 'Cache::LRU'; + + sub _force_expiry { + my ($self, $real_size) = @_; + return if $self->{size} != 'inf'; + local $self->{size} = $real_size; + $self->_auto_expire; + } + 1; + +Then in your code something like: + + sub expire_cache { + $_[0]->cache->_force_expiry( $self->cache_size ); + } + =head1 AUTHOR Kazuho Oku diff --git a/t/manual_management.t b/t/manual_management.t new file mode 100644 index 0000000..0ddd989 --- /dev/null +++ b/t/manual_management.t @@ -0,0 +1,61 @@ +use strict; +use warnings; + +use Test::More; + +BEGIN { + use_ok("Cache::LRU"); +}; + +my $cache = Cache::LRU->new( + size => "inf", +); + +my $real_size = 3; + +ok ! defined $cache->get('a'); + +is $cache->set(a => 1), 1; +is $cache->get('a'), 1; + +is $cache->set(b => 2), 2; +is $cache->get('a'), 1; +is $cache->get('b'), 2; + +is $cache->set(c => 3), 3; +is $cache->get('a'), 1; +is $cache->get('b'), 2; +is $cache->get('c'), 3; + +is $cache->set(b => 4), 4; +is $cache->get('a'), 1; +is $cache->get('b'), 4; +is $cache->get('c'), 3; + +my $keep; +is +($keep = $cache->get('a')), 1; # the order is now a => c => b +is $cache->set(d => 5), 5; +is $cache->get('a'), 1; +$cache->force_update($real_size); +ok ! defined $cache->get('b'); +is $cache->get('c'), 3; +is $cache->get('d'), 5; # the order is now d => c => a + +is $cache->set('e', 6), 6; +$cache->force_update($real_size); +ok ! defined $cache->get('a'); +ok ! defined $cache->get('b'); +is $cache->get('c'), 3; +is $cache->get('d'), 5; +is $cache->get('e'), 6; + +is $cache->remove('d'), 5; +is $cache->get('c'), 3; +ok ! defined $cache->get('d'); +is $cache->get('e'), 6; + +$cache->clear; +ok ! defined $cache->get('c'); +ok ! defined $cache->get('e'); + +done_testing;