[RFC PATCH 04/10] gitweb/cache.pm - Stat-based cache expiration

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]