Skip to content

Commit 0a9ca9f

Browse files
authored
Tweak gc to keep up to --storage-keep-hint-slots=5 newest slots with unique hints, even if those slots are old (#34)
## PRs in the Stack - ➡ #34 (The stack is managed by [git-grok](https://github.com/dimikot/git-grok).)
1 parent e8385b7 commit 0a9ca9f

File tree

4 files changed

+110
-10
lines changed

4 files changed

+110
-10
lines changed

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,17 @@ ones, so rsync can run efficiently.
6060
# Default: /mnt
6161
storage-dir: ''
6262

63-
# Remove slots created earlier than this many seconds ago.
63+
# Remove slots created earlier than this many seconds ago. The exception is
64+
# the newest slot (it's always kept), and also up to --storage-keep-hint-slots
65+
# slots related to unique hints.
6466
# Default: 14400 (4 hours).
6567
storage-max-age-sec: ''
6668

69+
# Defines the number of unique hints, for which ci-storage will keep at
70+
# least one newest slot, even if is past --storage-max-age-sec.
71+
# Default: 5.
72+
storage-keep-hint-slots: ''
73+
6774
# Id of the slot to store to or load from. Use "*" to load a smart-random
6875
# slot (e.g. most recent or best in terms of layer compatibility) and skip
6976
# if it does not exist.

action.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ inputs:
1414
description: "Storage directory on the remote host. Notice that, when building the final directory on the storage host, owner and repo are always appended, so the path will be {storage-dir}/{owner}/{repo}/{slug(local-dir)} or {storage-dir}/{owner}/{repo}/{slug(local-dir)}.{layer-name}. Default: /mnt"
1515
required: false
1616
storage-max-age-sec:
17-
description: "Remove slots created earlier than this many seconds ago. Default: 14400 (4 hours)."
17+
description: "Remove slots created earlier than this many seconds ago. The exception is the newest slot (it's always kept), and also up to --storage-keep-hint-slots slots related to unique hints. Default: 3600 (1 hour)."
18+
required: false
19+
storage-keep-hint-slots:
20+
description: "Defines the number of unique hints, for which ci-storage will keep at least one newest slot, even if is past --storage-max-age-sec. Default: 5."
1821
required: false
1922
slot-id:
2023
description: 'Id of the slot to store to or load from; use "*" to load a smart-random slot (e.g. most recent or best in terms of layer compatibility) and skip if it does not exist. Default: $GITHUB_RUN_ID (which is friendly to "Re-run failed jobs").'
@@ -58,6 +61,7 @@ runs:
5861
storage_host="${{ inputs.storage-host || '' }}"
5962
storage_dir="${{ inputs.storage-dir || '/mnt' }}/${{ github.repository }}"
6063
storage_max_age_sec="${{ inputs.storage-max-age-sec || '' }}"
64+
storage_keep_hint_slots="${{ inputs.storage-keep-hint-slots || '' }}"
6165
slot_id="${{ inputs.slot-id }}"
6266
local_dir="${{ inputs.local-dir || '.' }}"
6367
hint="${{ inputs.hint || '' }}"
@@ -103,6 +107,7 @@ runs:
103107
--storage-host="$storage_host"
104108
--storage-dir="$storage_dir"
105109
--storage-max-age-sec="$storage_max_age_sec"
110+
--storage-keep-hint-slots="$storage_keep_hint_slots"
106111
--slot-id="$slot_id"
107112
--local-dir="$local_dir"
108113
--hint="$hint"

ci-storage

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import typing
1717

1818
STORAGE_MAX_AGE_SEC_DEFAULT = 3600
1919
STORAGE_MAX_AGE_SEC_BAK = 60
20+
STORAGE_KEEP_HINT_SLOTS_DEFAULT = 5
2021
STORAGE_DIR_DEFAULT = "~/ci-storage"
2122
META_FILE = ".ci-storage.meta"
2223
EMPTY_DIR = ".ci-storage.empty-dir"
@@ -76,7 +77,14 @@ def main():
7677
type=str,
7778
default=str(STORAGE_MAX_AGE_SEC_DEFAULT),
7879
required=False,
79-
help="Remove slots created earlier than this many seconds ago.",
80+
help="Remove slots created earlier than this many seconds ago. The exception is the newest slot (it's always kept), and also up to --storage-keep-hint-slots slots related to unique hints.",
81+
)
82+
parser.add_argument(
83+
"--storage-keep-hint-slots",
84+
type=str,
85+
default=str(STORAGE_KEEP_HINT_SLOTS_DEFAULT),
86+
required=False,
87+
help="Defines the number of unique hints, for which ci-storage will keep at least one newest slot, even if is past --storage-max-age-sec.",
8088
)
8189
parser.add_argument(
8290
"--slot-id",
@@ -131,6 +139,9 @@ def main():
131139
storage_max_age_sec: int = int(
132140
args.storage_max_age_sec or str(STORAGE_MAX_AGE_SEC_DEFAULT)
133141
)
142+
storage_keep_hint_slots: int = int(
143+
args.storage_keep_hint_slots or str(STORAGE_KEEP_HINT_SLOTS_DEFAULT)
144+
)
134145
slot_ids: list[str] = " ".join(args.slot_id).split()
135146
local_dir: str = re.sub(r"/+$", "", args.local_dir)
136147
hints: list[str] = [
@@ -175,6 +186,7 @@ def main():
175186
storage_host=storage_host,
176187
storage_dir=storage_dir,
177188
storage_max_age_sec=storage_max_age_sec,
189+
storage_keep_hint_slots=storage_keep_hint_slots,
178190
)
179191
elif action == "load":
180192
if not slot_ids:
@@ -432,12 +444,13 @@ def action_maintenance(
432444
storage_host: str | None,
433445
storage_dir: str,
434446
storage_max_age_sec: int,
447+
storage_keep_hint_slots: int,
435448
):
436449
print(
437450
check_output_script(
438451
host=storage_host,
439452
script=SCRIPTS["MAINTENANCE"],
440-
args=[storage_dir, str(storage_max_age_sec)],
453+
args=[storage_dir, str(storage_max_age_sec), str(storage_keep_hint_slots)],
441454
indent=True,
442455
),
443456
end="",
@@ -933,7 +946,8 @@ SLOT_INFOS = textwrap.dedent(
933946
age_sec => time() - $inode_ctime,
934947
dir => $dir,
935948
meta => $meta,
936-
is_garbage => $slot_id =~ /\./ ? 1 : 0,
949+
meta_hints => $meta =~ /^hints=(.*)/m ? [grep(/./s, split(/\s+/s, $1))] : [],
950+
is_tmp_or_bak => $slot_id =~ /\./ ? 1 : 0,
937951
is_bak => $slot_id =~ /\.bak\.\d+$/s ? 1 : 0,
938952
};
939953
} else {
@@ -969,7 +983,7 @@ SCRIPTS = {
969983
}
970984
%(SLOT_INFOS)s
971985
my @slot_infos =
972-
grep { !$_->{is_garbage} }
986+
grep { !$_->{is_tmp_or_bak} }
973987
slot_infos($storage_dir);
974988
if (@slot_infos) {
975989
my $newest_dir = $slot_infos[0]{dir};
@@ -1028,6 +1042,7 @@ SCRIPTS = {
10281042
*STDERR->autoflush(1);
10291043
my $storage_dir = $ARGV[0] or die("storage_dir argument required\n");
10301044
my $storage_max_age_sec = $ARGV[1] or die("storage_max_age_sec argument required\n");
1045+
my $storage_keep_hint_slots = $ARGV[2] or die("storage_keep_hint_slots argument required\n");
10311046
length($storage_dir) >= 3 or die("storage_dir is suspiciously short\n");
10321047
my $lock_file = "$storage_dir/maintenance.lock";
10331048
open(my $lock, ">>", $lock_file) or die("open $lock_file: $!\n");
@@ -1037,22 +1052,44 @@ SCRIPTS = {
10371052
}
10381053
%(SLOT_INFOS)s
10391054
my @slot_infos = slot_infos($storage_dir);
1040-
my $slot_dir_newest = (map { $_->{dir} } grep { !$_->{is_garbage} } @slot_infos)[0];
1055+
my $slot_dir_newest = (map { $_->{dir} } grep { !$_->{is_tmp_or_bak} } @slot_infos)[0];
1056+
my %%slot_dir_newest_per_hint =
1057+
map { $_->{meta_hints}[0], $_->{dir} }
1058+
grep { !$_->{is_tmp_or_bak} && defined($_->{meta_hints}[0]) }
1059+
reverse(@slot_infos);
10411060
my @rm_dirs = ();
1061+
my $kept_per_hint_slots = 0;
10421062
foreach my $info (@slot_infos) {
10431063
my $dir = $info->{dir};
10441064
my $age_sec = $info->{age_sec};
10451065
my $is_bak = $info->{is_bak};
1046-
if (defined($slot_dir_newest) && $dir eq $slot_dir_newest) {
1047-
# Never delete the latest slot, even if it is old.
1066+
my $hint = $info->{meta_hints}[0];
1067+
my $suffix = (defined($hint) ? "hint=$hint, " : "") . "age=${age_sec}s";
1068+
if (
1069+
defined($slot_dir_newest) &&
1070+
$dir eq $slot_dir_newest
1071+
) {
1072+
print("keeping $dir, the newest slot overall ($suffix)\n");
1073+
next;
1074+
}
1075+
if (
1076+
defined($hint) &&
1077+
defined($slot_dir_newest_per_hint{$hint}) &&
1078+
$dir eq $slot_dir_newest_per_hint{$hint} &&
1079+
$kept_per_hint_slots < $storage_keep_hint_slots
1080+
) {
1081+
print("keeping $dir, the newest slot with this hint ($suffix)\n");
1082+
$kept_per_hint_slots++;
10481083
next;
10491084
}
10501085
if (
10511086
$age_sec > $storage_max_age_sec ||
10521087
$is_bak && $age_sec > %(STORAGE_MAX_AGE_SEC_BAK)d
10531088
) {
10541089
push(@rm_dirs, $dir);
1055-
print("will remove $dir (age: $age_sec sec) in background\n");
1090+
print("will remove $dir in background ($suffix)\n");
1091+
} else {
1092+
print("keeping $dir, new enough ($suffix)\n");
10561093
}
10571094
}
10581095
if (!@rm_dirs) {
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/bin/bash
2+
source ./common.sh
3+
4+
ci-storage \
5+
--slot-id=myslot-a1 \
6+
--hint="a" \
7+
store
8+
ci-storage \
9+
--slot-id=myslot-a2 \
10+
--hint="a" \
11+
store
12+
13+
ci-storage \
14+
--slot-id=myslot-b1 \
15+
--hint="b" \
16+
store
17+
ci-storage \
18+
--slot-id=myslot-b2 \
19+
--hint="b" \
20+
store
21+
22+
ci-storage \
23+
--slot-id=myslot-c1 \
24+
--hint="c" \
25+
store
26+
ci-storage \
27+
--slot-id=myslot-c2 \
28+
--hint="c" \
29+
store
30+
31+
ci-storage \
32+
--slot-id=myslot-d1 \
33+
--hint="d" \
34+
store
35+
ci-storage \
36+
--slot-id=myslot-d2 \
37+
--hint="d" \
38+
store
39+
40+
ci-storage \
41+
--slot-id=myslot-x \
42+
--hint="x" \
43+
--storage-keep-hint-slots=2 \
44+
store
45+
46+
grep -qF 'myslot-x, the newest slot overall' "$OUT"
47+
grep -qF 'myslot-d2, the newest slot with this hint (hint=d' "$OUT"
48+
grep -qF 'myslot-c2, the newest slot with this hint (hint=c' "$OUT"
49+
grep -qF 'myslot-b2, new enough' "$OUT"
50+
grep -qF 'myslot-a2, new enough' "$OUT"
51+

0 commit comments

Comments
 (0)