Skip to content

Commit 5844716

Browse files
committed
Implement GC minimum age gate
Use the recently added `lastUsageTime` store path attribute to allow users to constrain the GC to only deleting paths which have been last used n days ago. This is useful for ensuring that e.g. periodic GCs aren't too aggressive.
1 parent a511f44 commit 5844716

File tree

7 files changed

+69
-2
lines changed

7 files changed

+69
-2
lines changed

doc/manual/rl-next/gc-age-gate.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
synopsis: "Store GCs can now be restricted with a minimum age"
3+
prs: [14725]
4+
issues: [7572]
5+
---
6+
7+
Nix store GCs invoked using either `nix-collect-garbage` or `nix store gc` may
8+
now be restricted to only deleting paths older than a certain minimum age, i.e.
9+
whose most recent usage is more than *n* days in the past. "Usage" is
10+
intentionally defined ambiguously, however in general all operations which
11+
produce/require the presence of a given store path count as "usage".
12+
13+
14+
Example usage:
15+
16+
```bash
17+
# Delete store paths older than 7 days
18+
nix store gc --older-than 7d
19+
20+
# Alternatively:
21+
nix-collect-garbage --path-older-than 7d
22+
```

doc/manual/source/command-ref/nix-collect-garbage.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
# Synopsis
66

7-
`nix-collect-garbage` [`--delete-old`] [`-d`] [`--delete-older-than` *period*] [`--max-freed` *bytes*] [`--dry-run`]
7+
`nix-collect-garbage` [`--delete-old`] [`-d`] [`--delete-older-than` *period*] [`--delete-paths-older-than` *period*] [`--max-freed` *bytes*] [`--dry-run`]
88

99
# Description
1010

@@ -62,7 +62,17 @@ These options are for deleting old [profiles] prior to deleting unreachable [sto
6262
This is the equivalent of invoking [`nix-env --delete-generations <period>`](@docroot@/command-ref/nix-env/delete-generations.md#generations-time) on each found profile.
6363
See the documentation of that command for additional information about the *period* argument.
6464

65-
- <span id="opt-max-freed">[`--max-freed`](#opt-max-freed)</span> *bytes*
65+
- <span id="opt-paths-delete-older-than">[`--delete-paths-older-than`](#opt-delete-paths-older-than)</span> *period*
66+
67+
Only delete store paths older than the specified amount, i.e. restrict the GC
68+
to only deleting store paths whose most recent usage is more than *N* days in
69+
the past. *period* is a value such as `30d`, which would mean 30 days.
70+
71+
"Usage" is intentionally defined ambiguously, however in general all
72+
operations which produce/require the presence of a given store path count as
73+
"usage".
74+
75+
- <span id="opt-max-freed">[`--max-freed`](#opt-max-freed)</span> *bytes*
6676

6777
<!-- duplication from https://github.com/NixOS/nix/blob/442a2623e48357ff72c77bb11cf2cf06d94d2f90/doc/manual/source/command-ref/nix-store/gc.md?plain=1#L39-L44 -->
6878

src/libstore/gc.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,12 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
742742
if (options.action == GCOptions::gcDeleteSpecific && !options.pathsToDelete.count(*path))
743743
return;
744744

745+
/* If this path is too young, bail out */
746+
if (options.olderThan.has_value() && options.olderThan.value() < queryPathInfo(*path)->lastUsageTime) {
747+
debug("not deleting '%s' because it's too young", printStorePath(*path));
748+
return markAlive();
749+
}
750+
745751
{
746752
auto hashPart = path->hashPart();
747753
auto shared(_shared.lock());

src/libstore/include/nix/store/gc-store.hh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ struct GCOptions
5555
* Stop after at least `maxFreed` bytes have been freed.
5656
*/
5757
uint64_t maxFreed{std::numeric_limits<uint64_t>::max()};
58+
59+
/**
60+
* Only delete paths older than a certain point in time.
61+
*/
62+
std::optional<time_t> olderThan;
5863
};
5964

6065
struct GCResults

src/nix/nix-collect-garbage/nix-collect-garbage.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ static int main_nix_collect_garbage(int argc, char ** argv)
7878
else if (*arg == "--delete-older-than") {
7979
removeOld = true;
8080
deleteOlderThan = getArg(*arg, arg, end);
81+
} else if (*arg == "--delete-paths-older-than") {
82+
options.olderThan = parseOlderThanTimeSpec(getArg(*arg, arg, end));
8183
} else if (*arg == "--dry-run")
8284
dryRun = true;
8385
else if (*arg == "--max-freed")

src/nix/store-gc.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
#include "nix/store/store-api.hh"
55
#include "nix/store/store-cast.hh"
66
#include "nix/store/gc-store.hh"
7+
#include "nix/store/profiles.hh"
78

89
using namespace nix;
910

1011
struct CmdStoreGC : StoreCommand, MixDryRun
1112
{
1213
GCOptions options;
14+
std::optional<std::string> olderThan;
1315

1416
CmdStoreGC()
1517
{
@@ -19,6 +21,14 @@ struct CmdStoreGC : StoreCommand, MixDryRun
1921
.labels = {"n"},
2022
.handler = {&options.maxFreed},
2123
});
24+
addFlag({
25+
.longName = "older-than",
26+
.description = "Only delete paths older than the specified age. *age* "
27+
"must be in the format *N*`d`, where *N* denotes a number "
28+
"of days.",
29+
.labels = {"age"},
30+
.handler = {&olderThan},
31+
});
2232
}
2333

2434
std::string description() override
@@ -38,6 +48,7 @@ struct CmdStoreGC : StoreCommand, MixDryRun
3848
auto & gcStore = require<GcStore>(*store);
3949

4050
options.action = dryRun ? GCOptions::gcReturnDead : GCOptions::gcDeleteDead;
51+
options.olderThan = olderThan.transform(parseOlderThanTimeSpec);
4152
GCResults results;
4253
PrintFreed freed(options.action == GCOptions::gcDeleteDead, results);
4354
gcStore.collectGarbage(options, results);

src/nix/store-gc.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,19 @@ R""(
1414
# nix store gc --max 1G
1515
```
1616

17+
* Delete store paths older than 7 days:
18+
19+
```console
20+
# nix store gc --older-than 7d
21+
```
22+
1723
# Description
1824

1925
This command deletes unreachable paths in the Nix store.
2026

27+
`--older-than Nd` restricts the GC to only deleting store paths whose most
28+
recent usage is more than *N* days in the past. "Usage" is intentionally defined
29+
ambiguously, however in general all operations which produce/require the
30+
presence of a given store path count as "usage".
31+
2132
)""

0 commit comments

Comments
 (0)