mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 22:14:25 +01:00
v1.91.228.0 fix(gstack-lease): take the lease lock with mkdir, not ln -s, so Git Bash on Windows stops waiting out 300 tries (#3330) (#3424)
* fix(gstack-lease): take the lease lock with mkdir, not ln -s, so Git Bash on Windows stops waiting out 300 tries (#3330) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(gstack-lease): count lock retries instead of timing them Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(lease): a dead lock that cannot be renamed aside no longer spins lock() break_lock ignored drop()'s failure and returned 0, so lock() continued past the tries limit forever when a dead holder's .lock could not be renamed aside (a Windows file in use). It now removes the lock in place under .lock.break, and returns 1 when that also fails so the try is counted. touch no longer calls a dead holder 'live' in its exit-3 message. Adds the /ship coverage-audit tests for the lock's failure paths. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(lease): pre-landing review fixes - drop() reports a lock released once it is renamed aside, so a failed delete of the renamed copy never makes unlock or break_lock rm -rf a new taker's lock at .lock. - An ownerless lock is broken only once it is 2 s old, so a live taker whose claim landed after a breaker read the claims keeps the lock it is about to publish. An empty owner, an invalid pid or a stray file counts as ownerless. - touch's prune stays out of the lock (top-level lease files only) and clears leftover renamed-aside locks; a failed claim write leaves no claim. - Docs and CHANGELOG state the mixed-version and dead-break-lock limits instead of claiming exclusion that does not hold. Tests pin each fix (each fails against the previous bin), make the exclusion case wait on a release file, prove the parallel case is mutually exclusive, and check the Git Bash holder keeps its lock. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(lease): second /ship review pass - orphaned() reads the clock before the lock's mtime and requires the mtime unchanged at the end, so a lock made meanwhile is never removed; an mtime a minute or more ahead (clock skew) counts as old. - Claims older than 2 minutes are abandoned and pruned by touch. - A waiter skips the claim and mkdir while the lock is visibly held; the wait is bounded at 300 tries or 30 s; a non-numeric tries value falls back to 300; ten quick retries (not three) before a lock nobody holds is reported as untakeable; pid 1 is a valid holder again. - Tests: deterministic young-lock clock, skew and non-numeric owner shapes, abandoned claims, the deadline, explicit 30 s timeouts. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(lease): third /ship review pass - A claim is honored while its claimant lives; only a claim with no start identity expires after 2 minutes, so a stalled live taker keeps its lock on hosts that record identities (the remaining case is a documented limit). - A late taker never overwrites an owner already in its directory, and does not remove a directory that has one. - A lock whose owner file exists but cannot be read is never ownerless. - Zero-padded owner pids count as dead; --help prints the whole usage; touch prunes only claims whose claimant is gone or recorded no identity. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(lease): scope 'never by age' to locks that name a holder Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
9dc1dfb729
commit
e622075a18
8 files changed
+662
-61
No files matched your search
+24
-9
@@ -120,15 +120,30 @@ lease …`). Stale needs proof: two well-formed identities from the same backend
|
||||
that differ (`ps` is read under `LC_ALL=C TZ=UTC0`). An identity that cannot be
|
||||
read, a malformed record or a backend change keeps the lease. `touch` (publish
|
||||
by rename, prune) and reclaim (reread, then delete only the record judged
|
||||
stale) run under one lock in the lease directory, a symlink naming its holder's
|
||||
pid and start identity, so a renewal is never deleted and a live lease never
|
||||
disappears from a concurrent scan. The lock is broken only when its holder is
|
||||
dead or its pid was reused, never by age, and breakers serialize on a second
|
||||
symlink and reread the lock before removing it. One killed gstack-lease process
|
||||
is always safe; two killed in a row, one holding the lock and the next while
|
||||
breaking it, can still let a reclaim delete a renewal. While a live process holds it,
|
||||
`touch` exits 3 without writing (`gstack-skill-start` reports that the
|
||||
session holds no lease) and a reclaim keeps the lease. `bin/gstack-lease list` shows `alive|dead|stale`, and
|
||||
stale) run under one lock in the lease directory, a `.lock` directory whose
|
||||
`owner` file names its holder's pid and start identity, so a renewal is never
|
||||
deleted and a live lease never disappears from a concurrent scan. The lock is a
|
||||
directory because `mkdir` is atomic on every host, while Git Bash on Windows
|
||||
cannot `ln -s` to a target that does not exist (#3330). A taker publishes a
|
||||
claim before its `mkdir` and renames it in as the owner after, so an ownerless
|
||||
lock is broken only once no claimant lives and the lock is at least 2 s old
|
||||
and unchanged while checked, which keeps a taker between its two steps safe.
|
||||
A claim that records no start identity is abandoned after 2 minutes, so a
|
||||
reused pid cannot keep an orphan forever; an owner that names no valid pid
|
||||
counts as a dead holder, and an owner file that cannot be read keeps the
|
||||
lock. A live holder that never lets go bounds the wait at 300 tries or 30 s. A lock that names a holder is broken only when that holder is dead or
|
||||
its pid was reused, never by age, and breakers serialize on a second lock and
|
||||
reread the lock before removing it. A symlink `.lock` left by an older gstack
|
||||
is honored the same way. Three known limits remain. An older gstack's `ln -s`
|
||||
onto a directory lock links inside it and succeeds, so a process that started
|
||||
before an upgrade of this checkout can share the lock with a newer one. And a
|
||||
breaker killed while holding the second lock, followed by two live breakers
|
||||
clearing it at once, can let a reclaim delete a renewal. On a host without start
|
||||
identities, a taker stalled over 2 minutes between its `mkdir` and its owner
|
||||
rename can be broken and then publish into its successor's lock. While a live process
|
||||
holds the lock, or it cannot be taken at all, `touch` exits 3 without writing
|
||||
(`gstack-skill-start` reports that the session holds no lease) and a reclaim
|
||||
keeps the lease. `bin/gstack-lease list` shows `alive|dead|stale`, and
|
||||
`GSTACK_UPGRADE_FORCE=1` overrides.
|
||||
|
||||
## What the installer does
|
||||
|
||||
Reference in new issue
Block a user