зеркало из
				https://github.com/iharh/notes.git
				synced 2025-11-04 07:36:08 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			180 строки
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			180 строки
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
https://nixos.org/guides/nix-pills/garbage-collector.html
 | 
						|
 | 
						|
Today we stop packaging and look at a mandatory nix component, the garbage collector.
 | 
						|
When using nix tools, often derivations are built.
 | 
						|
This include both .drv files and out paths.
 | 
						|
These artifacts go in the nix store, but we've never cared about deleting them until now.
 | 
						|
 | 
						|
 | 
						|
# 11.1. How does it work
 | 
						|
 | 
						|
Other package managers, like dpkg, have ways of removing unused software.
 | 
						|
Nix is much more precise in its garbage ollection compared to these other systems.
 | 
						|
 | 
						|
I bet with dpkg, rpm or similar traditional packaging systems, you end up having some unnecessary packages installed or dangling files.
 | 
						|
With nix this does not happen.
 | 
						|
 | 
						|
How do we determine whether a store path is still needed?
 | 
						|
The same way programming languages with a garbage collector decide whether an object is still alive.
 | 
						|
 | 
						|
Programming languages with a garbage collector have an important concept in order to keep track of live objects:
 | 
						|
    GC roots.
 | 
						|
A GC root is an object that is always alive (unless explicitly removed as GC root).
 | 
						|
All objects recursively referred to by a GC root are live.
 | 
						|
 | 
						|
Therefore, the garbage collection process starts from GC roots, and recursively mark referenced objects as live.
 | 
						|
All other objects can be collected and deleted.
 | 
						|
 | 
						|
In Nix there's this same concept.
 | 
						|
Instead of being objects, of course, GC roots are store paths.
 | 
						|
    https://nixos.org/manual/nix/stable/#ssec-gc-roots
 | 
						|
The implementation is very simple and transparent to the user.
 | 
						|
GC roots are stored under /nix/var/nix/gcroots. If there's a symlink to a store path, then that store path is a GC root.
 | 
						|
 | 
						|
Nix allows this directory to have subdirectories: it will simply recurse directories in search of symlinks to store paths.
 | 
						|
 | 
						|
So we have a list of GC roots.
 | 
						|
At this point, deleting dead store paths is as easy as you can imagine.
 | 
						|
We have the list of all live store paths, hence the rest of the store paths are dead.
 | 
						|
 | 
						|
In particular, Nix first moves dead store paths to /nix/store/trash which is an atomic operation.
 | 
						|
Afterwards, the trash is emptied.
 | 
						|
 | 
						|
 | 
						|
# 11.2. Playing with the GC
 | 
						|
 | 
						|
Before playing with the GC, first run the nix garbage collector once, so that we have a clean playground for our experiments:
 | 
						|
 | 
						|
$ nix-collect-garbage
 | 
						|
    finding garbage collector roots...
 | 
						|
    [...]
 | 
						|
    deleting unused links...
 | 
						|
    note: currently hard linking saves -0.00 MiB
 | 
						|
    1169 store paths deleted, 228.43 MiB freed
 | 
						|
 | 
						|
Perfect, if you run it again it won't find anything new to delete, as expected.
 | 
						|
 | 
						|
What's left in the nix store is everything being referenced from the GC roots.
 | 
						|
 | 
						|
Let's install for a moment bsd-games:
 | 
						|
 | 
						|
$ nix-env -iA nixpkgs.bsdgames
 | 
						|
$ readlink -f `which fortune`
 | 
						|
    /nix/store/b3lxx3d3ggxcggvjw5n0m1ya1gcrmbyn-bsd-games-2.17/bin/fortune
 | 
						|
$ nix-store -q --roots `which fortune`
 | 
						|
    /nix/var/nix/profiles/default-9-link
 | 
						|
$ nix-env --list-generations
 | 
						|
    [...]
 | 
						|
    9   2014-08-20 12:44:14   (current)
 | 
						|
 | 
						|
The nix-store command can be used to query the GC roots that refer to a given derivation.
 | 
						|
In this case, our current user environment does refer to bsd-games.
 | 
						|
 | 
						|
Now remove it, collect garbage and note that bsd-games is still in the nix store:
 | 
						|
 | 
						|
$ nix-env -e bsd-games
 | 
						|
    uninstalling `bsd-games-2.17'
 | 
						|
$ nix-collect-garbage
 | 
						|
    [...]
 | 
						|
$ ls /nix/store/b3lxx3d3ggxcggvjw5n0m1ya1gcrmbyn-bsd-games-2.17
 | 
						|
    bin  share
 | 
						|
 | 
						|
This is because the old generation is still in the nix store because it's a GC root. As we'll see below, all profiles and their generations are GC roots.
 | 
						|
 | 
						|
Removing a GC root is simple. Let's try deleting the generation that refers to bsd-games, collect garbage, and note that now bsd-games is no longer in the nix store:
 | 
						|
 | 
						|
$ rm /nix/var/nix/profiles/default-9-link
 | 
						|
$ nix-env --list-generations
 | 
						|
[...]
 | 
						|
   8   2014-07-28 10:23:24
 | 
						|
  10   2014-08-20 12:47:16   (current)
 | 
						|
$ nix-collect-garbage
 | 
						|
    [...]
 | 
						|
$ ls /nix/store/b3lxx3d3ggxcggvjw5n0m1ya1gcrmbyn-bsd-games-2.17
 | 
						|
    ls: cannot access /nix/store/b3lxx3d3ggxcggvjw5n0m1ya1gcrmbyn-bsd-games-2.17: No such file or directory
 | 
						|
 | 
						|
Note: nix-env --list-generations does not rely on any particular metadata.
 | 
						|
It is able to list generations based solely on the file names under the profiles directory.
 | 
						|
 | 
						|
However we removed the link from /nix/var/nix/profiles, not from /nix/var/nix/gcroots.
 | 
						|
Turns out, that /nix/var/nix/gcroots/profiles is a symlink to /nix/var/nix/profiles.
 | 
						|
That is very handy.
 | 
						|
It means any profile and its generations are GC roots.
 | 
						|
 | 
						|
It's as simple as that, anything under /nix/var/nix/gcroots is a GC root.
 | 
						|
And anything not being garbage collected is because it's referred from one of the GC roots.
 | 
						|
 | 
						|
 | 
						|
# 11.3. Indirect roots
 | 
						|
 | 
						|
Remember that building the GNU hello world package with nix-build produces a result symlink in the current directory.
 | 
						|
Despite the collected garbage done above, the hello program is still working: therefore it has not been garbage collected.
 | 
						|
Clearly, since there's no other derivation that depends upon the GNU hello world package, it must be a GC root.
 | 
						|
 | 
						|
In fact, nix-build automatically adds the result symlink as a GC root.
 | 
						|
Yes, not the built derivation, but the symlink. These GC roots are added under /nix/var/nix/gcroots/auto.
 | 
						|
 | 
						|
$ ls -l /nix/var/nix/gcroots/auto/
 | 
						|
    total 8
 | 
						|
    drwxr-xr-x 2 nix nix 4096 Aug 20 10:24 ./
 | 
						|
    drwxr-xr-x 3 nix nix 4096 Jul 24 10:38 ../
 | 
						|
    lrwxrwxrwx 1 nix nix   16 Jul 31 10:51 xlgz5x2ppa0m72z5qfc78b8wlciwvgiz -> /home/nix/result/
 | 
						|
 | 
						|
Don't care about the name of the symlink.
 | 
						|
What's important is that a symlink exists that point to /home/nix/result.
 | 
						|
This is called an "indirect GC root".
 | 
						|
That is, the GC root is effectively specified outside of /nix/var/nix/gcroots.
 | 
						|
Whatever result points to, it will not be garbage collected.
 | 
						|
 | 
						|
How do we remove the derivation then? There are two possibilities:
 | 
						|
    * Remove the indirect GC root from /nix/var/nix/gcroots/auto.
 | 
						|
    * Remove the result symlink. 
 | 
						|
 | 
						|
In the first case, the derivation will be deleted from the nix store, and result becomes a dangling symlink.
 | 
						|
In the second case, the derivation is removed as well as the indirect root in /nix/var/nix/gcroots/auto.
 | 
						|
 | 
						|
Running nix-collect-garbage after deleting the GC root or the indirect GC root, will remove the derivation from the store.
 | 
						|
 | 
						|
 | 
						|
# 11.4. Cleanup everything
 | 
						|
 | 
						|
What's the main source of software duplication in the nix store?
 | 
						|
Clearly, GC roots due to nix-build and profile generations.
 | 
						|
Doing a nix-build results in a GC root for a build that somehow will refer to a specific version of glibc, and other libraries.
 | 
						|
After an upgrade, if that build is not deleted by the user, it will not be garbage collected.
 | 
						|
Thus the old dependencies referred to by the build will not be deleted either.
 | 
						|
 | 
						|
Same goes for profiles.
 | 
						|
Manipulating the nix-env profile will create further generations.
 | 
						|
Old generations refer to old software, thus increasing duplication in the nix store after an upgrade.
 | 
						|
 | 
						|
What are the basic steps for upgrading and removing everything old, including old generations?
 | 
						|
In other words, do an upgrade similar to other systems, where they forget everything about the older state:
 | 
						|
 | 
						|
$ nix-channel --update
 | 
						|
$ nix-env -u --always
 | 
						|
$ rm /nix/var/nix/gcroots/auto/*
 | 
						|
$ nix-collect-garbage -d
 | 
						|
 | 
						|
First, we download a new version of the nixpkgs channel, which holds the description of all the software.
 | 
						|
Then we upgrade our installed packages with nix-env -u.
 | 
						|
That will bring us into a fresh new generation with all updated software.
 | 
						|
 | 
						|
Then we remove all the indirect roots generated by nix-build:
 | 
						|
    beware, this will result in dangling symlinks.
 | 
						|
You may be smarter and also remove the target of those symlinks.
 | 
						|
 | 
						|
Finally, the -d option of nix-collect-garbage is used to delete old generations of all profiles, then collect garbage.
 | 
						|
After this, you lose the ability to rollback to any previous generation.
 | 
						|
So make sure the new generation is working well before running the command.
 | 
						|
 | 
						|
 | 
						|
# 11.5. Conclusion
 | 
						|
 | 
						|
Garbage collection in Nix is a powerful mechanism to cleanup your system.
 | 
						|
The nix-store commands allow us to know why a certain derivation is in the nix store.
 | 
						|
 | 
						|
Cleaning up everything down to the oldest bit of software after an upgrade seems a bit contrived,
 | 
						|
but that's the price of having multiple generations, multiple profiles, multiple versions of software, thus rollbacks etc..
 | 
						|
The price of having many possibilities. 
 |