Add stat-based cache expiration to file-based GitwebCache::SimpleFileCache. Contrary to the way other caching interfaces such as Cache::Cache and CHI do it, the time cache element expires in is _global_ value associated with cache instance, and is not local property of cache entry. (Currently cache entry does not store any metadata associated with entry... which means that there is no need for serialization / marshalling / freezing and thawing.) Default expire time is -1, which means never expire. To check if cache entry is expired, GitwebCache::SimpleFileCache compares difference between mtime (last modify time) of a cache file and current time with (global) time to expire. It is done using CHI-compatibile is_valid() method. Add test checking that expire time of 0 (expire now) works correctly. While at it show diagnostic if there were parse errors in gitweb/cache.pm (TO BE MOVED TO PREVIOUS COMMIT). Signed-off-by: Jakub Narebski <jnareb@xxxxxxxxx> --- The main difference from the way J.H. did it in his patch adding output caching to gitweb is that here I uses imply stat, and not open/stat/close. Now that cahcing engine supports cache expiration, we can add caching support to gitweb. gitweb/cache.pm | 47 ++++++++++++++++++++++++++++++++++++++- t/t9503/test_cache_interface.pl | 11 ++++++++- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/gitweb/cache.pm b/gitweb/cache.pm index ea544b0..12a7a78 100644 --- a/gitweb/cache.pm +++ b/gitweb/cache.pm @@ -50,6 +50,10 @@ our $DEFAULT_CACHE_ROOT = "cache"; # The number of subdirectories deep to cache object item. This should be # large enough that no cache directory has more than a few hundred objects. # Defaults to 2 unless explicitly set. +# * 'default_expires_in' (Cache::Cache compatibile), +# 'expires_in' (CHI compatibile) [seconds] +# The expiration time for objects place in the cache. +# Defaults to $EXPIRES_NEVER if not explicitly set. sub new { my ($proto, $p_options_hash_ref) = @_; @@ -57,19 +61,24 @@ sub new { my $self = {}; $self = bless($self, $class); - my ($root, $depth, $ns); + my ($root, $depth, $ns, $expires_in); if (defined $p_options_hash_ref) { $root = $p_options_hash_ref->{'cache_root'}; $depth = $p_options_hash_ref->{'cache_depth'}; $ns = $p_options_hash_ref->{'namespace'}; + $expires_in = + $p_options_hash_ref->{'default_expires_in'} || + $p_options_hash_ref->{'expires_in'}; } $root = $DEFAULT_CACHE_ROOT unless defined($root); $depth = $DEFAULT_CACHE_DEPTH unless defined($depth); $ns = '' unless defined($ns); + $expires_in = -1 unless defined($expires_in); # <0 means never $self->set_root($root); $self->set_depth($depth); $self->set_namespace($ns); + $self->set_expires_in($expires_in); return $self; } @@ -115,6 +124,20 @@ sub set_namespace { $self->{'_Namespace'} = $namespace; } +sub get_expires_in { + my ($self) = @_; + + return $self->{'_Expires_In'}; +} + + +sub set_expires_in { + my ($self, $expires_in) = @_; + + $self->{'_Expires_In'} = $expires_in; +} + + # ---------------------------------------------------------------------- # (private) utility functions and methods @@ -282,6 +305,27 @@ sub remove { $self->delete_key($self->get_namespace(), $p_key); } +# exists in cache and is not expired +sub is_valid { + my ($self, $p_key) = @_; + + # should there be namespace variant of this function? + my $path = $self->_path_to_key($self->get_namespace(), $p_key); + + # does file exists in cache? + return 0 unless -f $path; + + # expire time can be set to never + my $expires_in = $self->get_expires_in(); + return 1 unless (defined $expires_in && $expires_in >= 0); + + # is file expired? + my $mtime = (stat(_))[9]; + my $now = time(); + + return (($now - $mtime) < $expires_in); +} + # Getting and setting sub set { @@ -293,6 +337,7 @@ sub set { sub get { my ($self, $p_key) = @_; + return undef unless $self->is_valid($p_key); my $data = $self->restore($self->get_namespace(), $p_key) or return undef; diff --git a/t/t9503/test_cache_interface.pl b/t/t9503/test_cache_interface.pl index 0b6628b..3644ca8 100755 --- a/t/t9503/test_cache_interface.pl +++ b/t/t9503/test_cache_interface.pl @@ -16,7 +16,8 @@ unless (-f "$cache_pm") { # it is currently not a proper Perl module, so we use 'do FILE' #ok(eval { do "$cache_pm"; 1 or die $!; }, "loading gitweb/cache.pm"); my $return = do "$cache_pm"; -ok(!$@, "parse gitweb/cache.pm"); +ok(!$@, "parse gitweb/cache.pm") + or diag("parse error:\n", $@); ok(defined $return, "do gitweb/cache.pm"); ok($return, "run gitweb/cache.pm"); # instead of: BEGIN { use_ok('GitwebCache::SimpleFileCache') } @@ -74,4 +75,12 @@ is($cache->compute($key, \&get_value), $value, 'compute 2nd time (get)'); is($cache->compute($key, \&get_value), $value, 'compute 3rd time (get)'); cmp_ok($call_count, '==', 1, 'get_value() is called once'); +# Test cache expiration for 'expire now' +# +$cache->set_expires_in(0); +is($cache->get_expires_in(), 0, '"expires in" is set to now (0)'); +$cache->set($key, $value); +ok(!defined($cache->get($key)), 'cache is expired'); + + done_testing(); -- 1.6.6.1 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html