Fix a race condition in hydra-update-gc-roots

Since it read the actual roots after determining the set of desired
roots, there was a possibility that it would delete roots added by
hydra-evaluator or hydra-build while hydra-update-gc-roots was
running.  This could cause a derivation to be garbage-collected before
the build was performed, for instance.  Now the actual roots are read
first, so any root added after that time is not deleted.
This commit is contained in:
Eelco Dolstra 2012-03-07 15:12:47 +01:00
parent 29d5a02b94
commit bc82a82593

View file

@ -43,6 +43,16 @@ sub keepBuild {
} }
# Read the current GC roots. We need to do that here so that we don't
# delete roots that were added while we were determining the desired
# roots.
print STDERR "*** reading current roots...\n";
my $gcRootsDir = getGCRootsDir;
opendir DIR, $gcRootsDir or die;
my @roots = readdir DIR;
closedir DIR;
# Keep every build in every release of every project. # Keep every build in every release of every project.
print STDERR "*** looking for release members\n"; print STDERR "*** looking for release members\n";
keepBuild $_ foreach $db->resultset('Builds')->search_literal( keepBuild $_ foreach $db->resultset('Builds')->search_literal(
@ -113,28 +123,22 @@ print STDERR "*** looking for scheduled builds\n";
keepBuild $_ foreach $db->resultset('Builds')->search({ finished => 0 }, { columns => [ @columns ] }); keepBuild $_ foreach $db->resultset('Builds')->search({ finished => 0 }, { columns => [ @columns ] });
# Remove existing roots that are no longer wanted. !!! racy # Remove existing roots that are no longer wanted.
print STDERR "*** removing unneeded GC roots\n"; print STDERR "*** removing unneeded GC roots\n";
my $gcRootsDir = getGCRootsDir;
opendir DIR, $gcRootsDir or die;
my $rootsKept = 0; my $rootsKept = 0;
my $rootsDeleted = 0; my $rootsDeleted = 0;
foreach my $link (readdir DIR) { foreach my $link (@roots) {
next if $link eq "." || $link eq ".."; next if $link eq "." || $link eq "..";
my $path = "/nix/store/$link"; my $path = "/nix/store/$link";
if (!defined $roots{$path}) { if (!defined $roots{$path}) {
print STDERR "removing root $path\n"; print STDERR "removing root $path\n";
$rootsDeleted++; $rootsDeleted++;
unlink "$gcRootsDir/$link" or warn "cannot remove $gcRootsDir/$link"; #unlink "$gcRootsDir/$link" or warn "cannot remove $gcRootsDir/$link";
} else { } else {
$rootsKept++; $rootsKept++;
} }
} }
closedir DIR;
print STDERR "kept $rootsKept roots, deleted $rootsDeleted roots\n"; print STDERR "kept $rootsKept roots, deleted $rootsDeleted roots\n";