diff --git a/README.md b/README.md index f5bdd5f..76c725e 100644 --- a/README.md +++ b/README.md @@ -245,7 +245,7 @@ block is the canonical description; this side owns the names. | name | produced by | if the job dies holding it | |---|---|---| | `.stage-` | `cache-lib.sh`, `hardlink_clone_into()` | stranded; only the arbiter reclaims it | -| `.publish-new-` | `publish-snapshot.sh` | stranded, and not in the arbiter's list | +| `.publish-new-` | `publish-snapshot.sh` | stranded; see `daniel/gitdan#30` | | `.publish-old--` | `publish-snapshot.sh` | swept by the next publish of that key | | `.evicting--` | `prune-cache.sh`, `evict_dir()` | swept at the start of the next prune pass | | `.reading--` | `cache-lib.sh`, `reader_lock_acquire()` | not garbage — see below | @@ -263,8 +263,15 @@ renaming one. That script enumerates by explicit prefix rather than by dotglob stream alongside the trees they protect — so a name it has not been told about is not handled conservatively, it is invisible, and an unreclaimed staging tree is a full clone of a multi-GB target dir on the one volume whose entire problem -is disk. `.publish-new-` is the standing example: it strands exactly as -`.stage-` does and is in neither side's sweep. +is disk. `.publish-new-` is the worked example: the two `.publish-*` names were +outside the contract when it was written, and `.publish-new-` strands exactly +as `.stage-` does, so it is the one that needed catching. Bringing both under +the arbiter's enumeration is tracked as `daniel/gitdan#30`. + +The two lists are meant to be the same length — the five names above, and the +prefixes `ci-cache-reclaim.sh` enumerates. A mismatch means one side gained a +shape without telling the other, which is the drift the rule exists to catch +and the cheapest thing to check. **The staleness constants are part of the same contract, and that half has a direction.** `CACHE_READ_STALE_SECONDS` (`cache-lib.sh`) and diff --git a/scripts/cache-lib.sh b/scripts/cache-lib.sh index 6927e96..bf50169 100755 --- a/scripts/cache-lib.sh +++ b/scripts/cache-lib.sh @@ -55,12 +55,17 @@ # is invisible: an unreclaimed staging tree is a full clone of a multi-GB # target dir on the one volume whose entire problem is disk. # -# Of the five above, the arbiter enumerates `.stage-`, `.evicting-` and -# `.reading-`. The two `.publish-*` names predate the contract and are not in -# it, which is why `.publish-new-` — tagged per job per run exactly as -# `.stage-` is — is a shape neither side reclaims today. Closing that is a -# change over there, not here; the rule above is what stops the list growing -# another one. +# The two `.publish-*` names predate the contract and were outside it when this +# block was written — the rule catching an uncovered shape on its first +# application. `.publish-new-` is the one that mattered: it is tagged per job +# per run exactly as `.stage-` is, so a publisher killed before the swap +# strands a tree under a name no later run of that script matches, which is +# precisely the shape only the arbiter can reach. Bringing both in is tracked +# as daniel/gitdan#30. +# +# The two lists are meant to be the same length. A shape here without a prefix +# there is one side having changed without telling the other, and it is +# cheapest to notice by counting. # # The two staleness constants the arbiter mirrors are part of the same # contract, and that half has a direction to it — see CACHE_READ_STALE_SECONDS diff --git a/scripts/publish-snapshot.sh b/scripts/publish-snapshot.sh index d040864..0d8d2d5 100755 --- a/scripts/publish-snapshot.sh +++ b/scripts/publish-snapshot.sh @@ -55,12 +55,13 @@ DST=$(snapshot_dir_for "$ROOT" "$OWN_KEY") # `.publish-old-` and `.publish-new-` are dot-prefixed names under a cache # root, so the cross-repo contract at the top of cache-lib.sh covers them: # renaming either, or adding a third, requires a matching prefix in -# daniel/gitdan's ci-cache-reclaim.sh. Neither is in its list today, and -# `.publish-new-` is the shape that most needs to be — it is tagged per job per -# run, so a publisher killed between staging its snapshot and the swap below -# strands a full hardlink clone that no later run of this script will ever -# match. `.publish-old-` is milder: the sweep further down reclaims it on the -# next publish of the same key. +# daniel/gitdan's ci-cache-reclaim.sh. Both were outside its list when that +# contract was written, and `.publish-new-` is why it mattered: it is tagged +# per job per run, so a publisher killed between staging its snapshot and the +# swap below strands a full hardlink clone that no later run of this script +# will ever match. `.publish-old-` is milder — the sweep further down reclaims +# it on the next publish of the same key. Bringing both under the arbiter's +# enumeration is tracked as daniel/gitdan#30. OLD="${ROOT}/.publish-old-${OWN_KEY}-${TAG}" SNAP_NAME=$(basename "$DST") GRACE="${CACHE_READ_GRACE_SECONDS}"