chiark / gitweb /
git-cache-proxy: Periodically run `git gc --quiet'
[chiark-utils.git] / scripts / git-cache-proxy
index d798f2bab30fbbdfc4a940bc5d7882db8c5963ab..7dd6f3ec531d53553633905a2c491bd8ffb43645 100755 (executable)
@@ -64,6 +64,7 @@ our $us = 'git-cache-proxy';
 
 our $debug = 0;
 our $housekeepingeverydays = 1;
+our $gcintervaldays = 10;
 our $treeexpiredays = 21;
 our $fetchtimeout = 3600;
 our $maxfetchtimeout = 7200;
@@ -176,6 +177,7 @@ for (;;) {
                       | serve-timeout
                       | tree-expire-days
                       | housekeeping-interval-days
+                      | gc-interval-days
                       )=(\d+)$//x) {
            my $vn = $1;
            $vn =~ y/-//d;
@@ -298,6 +300,13 @@ sub readcommand () {
     servinfo "locking";
 }
 
+sub update_gcstamp ($) {
+    my ($gitdir) = (@_);
+    my $gcdone = "$gitdir/cache-proxy-gc.stamp";
+    open GCSTAMP, '>', $gcdone or fail "create $gcdone: $!";
+    close GCSTAMP;
+}
+
 sub clonefetch () {
     lockfile \*LOCK, $lock, LOCK_EX;
 
@@ -366,6 +375,7 @@ sub clonefetch () {
         alarm 0;
 
        if (!$exists) {
+           update_gcstamp($tmpd);
            rename $tmpd, $gitd or fail "rename fresh $tmpd to $gitd: $!";
            $exists = 1;
        }
@@ -396,14 +406,42 @@ sub housekeeping () {
     logm 'info', "housekeeping started";
     foreach $lock (<[a-z]*\\.lock>) {
        my $subdir = $lock;  $subdir =~ s/\\.lock$//;
+       my $gcdone = "$subdir\\.git/cache-proxy-gc.stamp";
        if (!lstat $lock) {
            $! == ENOENT or hkfail "$lock: lstat: $!";
            next;
        }
        my ($mode_what,$mode_locknb,$mode_action);
        if (-M _ <= $treeexpiredays) {
-           logm 'debug', "housekeeping: subdirs $subdir: touched recently";
-           next;
+           if (!lstat "$gcdone") {
+               $! == ENOENT or hkfail "$gcdone: lstat: $!";
+               logm 'debug',
+ "housekeeping: subdirs $subdir: touched recently, never gc'd!";
+           } elsif (-M _ <= $gcintervaldays) {
+               logm 'debug',
+ "housekeeping: subdirs $subdir: touched recently, gc'd recently";
+               next;
+           } else {
+               logm 'debug',
+ "housekeeping: subdirs $subdir: touched recently, needs gc";
+           }
+           $mode_what = 'garbage collecting';
+           $mode_locknb = 0;
+           $mode_action = sub {
+               my $gclog = "$subdir/gc.log";
+               unlink $gclog or $!==ENOENT or hkfail "remove $gclog: $!";
+               my $r = system qw(sh -ec),
+                   'cd "$1"; shift; exec "$@"', 'x', "$subdir\\.git",
+                   qw(git gc --quiet);
+               if ($r) {
+                   logm 'err',
+                       "housekeeping: subdirs $subdir: gc failed (status $r)";
+               } else {
+                   update_gcstamp("$subdir\\.git");
+                   logm 'debug',
+                       "housekeeping: subdirs $subdir: gc done";
+               }
+           };
        } else {
            $mode_what = 'cleaning';
            $mode_locknb = LOCK_NB;