This adds a gcTryDeleteSpecific operation. This is similar to
gcDeleteSpecific, but will not fail if any of the given paths cannot
be deleted. Paths that could not be deleted are reported in the new
`kept` field of struct GCResults.
This also changes the behaviour of gcDeleteSpecific, such that it will
now continue deleting paths even if it fails to delete one along the
way, and only throw an error once deletion of all the given paths has
been attempted. This seems reasonable to me, because it makes its
behaviour somewhat less surprising -- previously, if some paths were
deletable and others weren't, the deletable ones would be deleted iff
they preceded the live ones in lexical sort order.
This also fixes a regression introduced in
8614cf1334, whereby nix-store --delete
failed to delete paths if they had any dependents -- even if none of
the dependents had GC roots.
The gcTryDeleteSpecific operation is surfaced via additional flags for
the `nix store delete` and `nix-store --delete` commands.
This makes custom garbage-collection logic a lot easier to implement
and experiment with:
- Paths known to be large can be thrown at `nix store delete` without
having to manually filter out those that are still reachable from a
root, e.g.
`nix store delete /nix/store/*mbrola-voices*`
- The --delete-closure option allows extending this to paths that are
not large themselves but do have a large closure size, e.g.
`nix store delete /nix/store/*nixos-system-gamingpc*`
Having an option for this is not strictly necessary, but convenient
because it doesn't require the user to add an extra `nix-store
-qR` (or `nix path-info -r`) into their command, nor to rewrite
their command to use `--stdin` if the closure ends up too large to
fit on a command line.
- Other heuristics like atime-based deletion can be applied more
easily, because `nix store delete` once again takes over the task of
working out which paths can't be deleted.
Change-Id: If345407fe7b11bdb3a8fdc04b0d56c32ab3d5928
80 lines
2.1 KiB
Bash
80 lines
2.1 KiB
Bash
source common.sh
|
|
|
|
clearStore
|
|
|
|
createAndRootPaths() {
|
|
drvPath=$(nix-instantiate dependencies.nix)
|
|
outPath=$(nix-store -rvv "$drvPath")
|
|
|
|
# Set a GC root.
|
|
rm -f "$NIX_STATE_DIR"/gcroots/foo
|
|
ln -sf $outPath "$NIX_STATE_DIR"/gcroots/foo
|
|
}
|
|
|
|
createAndRootPaths
|
|
|
|
[ "$(nix-store -q --roots $outPath)" = "$NIX_STATE_DIR/gcroots/foo -> $outPath" ]
|
|
|
|
nix-store --gc --print-roots | grep $outPath
|
|
nix-store --gc --print-live | grep $outPath
|
|
nix-store --gc --print-dead | grep $drvPath
|
|
if nix-store --gc --print-dead | grep -E $outPath$; then false; fi
|
|
|
|
nix-store --gc --print-dead
|
|
|
|
input2=$(readLink $outPath/reference-to-input-2)
|
|
if nix-store --delete $input2; then false; fi
|
|
test -e $input2
|
|
|
|
if nix-store --delete $outPath; then false; fi
|
|
test -e $outPath
|
|
|
|
for i in $NIX_STORE_DIR/*; do
|
|
if [[ $i =~ /trash ]]; then continue; fi # compat with old daemon
|
|
touch $i.lock
|
|
touch $i.chroot
|
|
done
|
|
|
|
nix-collect-garbage
|
|
|
|
# Check that the root and its dependencies haven't been deleted.
|
|
cat $outPath/foobar
|
|
cat $outPath/reference-to-input-2/bar
|
|
|
|
# Check that the derivation has been GC'd.
|
|
if test -e $drvPath; then false; fi
|
|
|
|
rm "$NIX_STATE_DIR"/gcroots/foo
|
|
|
|
# Deleting the dependency should now be possible, and delete the referrer as well
|
|
nix-store --delete $input2
|
|
! test -e $outPath
|
|
! test -e $input2
|
|
|
|
createAndRootPaths
|
|
rm "$NIX_STATE_DIR"/gcroots/foo
|
|
nix-collect-garbage
|
|
|
|
# Check that the store is empty.
|
|
rmdir $NIX_STORE_DIR/.links
|
|
rmdir $NIX_STORE_DIR
|
|
|
|
createAndRootPaths
|
|
input0=$(< $outPath/reference-to-input-2/input0)
|
|
input2=$(readLink $outPath/reference-to-input-2)
|
|
fodDrv=$(nix-store -qR $drvPath | grep fod-input.drv)
|
|
fodOut=$(nix-store -q --outputs $fodDrv)
|
|
# path is still live but command should still succeed because of --skip-live
|
|
nix-store --delete --skip-live $(readLink $outPath/reference-to-input-2)
|
|
|
|
rm "$NIX_STATE_DIR"/gcroots/foo
|
|
# with the dependent unrooted, we should be able to remove input2...
|
|
nix-store --delete --delete-closure $input2
|
|
# which should remove input0, since only input2 and top depended on it and we passed --delete-closure
|
|
! test -e $input0
|
|
# but fod should be unaffected, since it's not part of input-2's closure
|
|
test -e $fodOut
|
|
|
|
|
|
nix-collect-garbage
|