zit 0.3.33 changes how a repository is locked for writing: the lock is now
a file created with O_CREAT|O_EXCL, the way git and most Unix programs lock,
instead of flock. A writer that has to wait now says what it is waiting
for, and gives up with a message instead of waiting forever. It also gives
repositories made by zit import-git the project code every sync peer
requires.
No artifact, tag namespace, sync message, or on-disk repository format changed.
The write lock is a lock file
Every command that writes a repository first creates .zit.lock beside the
store's .zit file, exclusively, and deletes it when the write has
committed. Only one process can create the file, which the filesystem
guarantees. The file records the holder's process id and host.
The old lock was flock on .zit itself. flock is not part of POSIX, and
on network filesystems it ranges from correct to silently locking nothing.
Exclusive create is defined by POSIX and is atomic on NFSv3 and later. A
blocked flock also waited without a word and without end, which is how a
hung writer could stall every other writer to a repository with no sign of
why.
Now a writer that finds the lock held waits for it, and after a second says:
zit: waiting for the repository lock, held by process 4242 on build-host
If the holder keeps it for a minute, the writer gives up, names the holder, and names the file to delete if no zit process is really using it.
A lock file outlives a crashed holder, which flock did not. A lock that
names a process on this machine that no longer exists is stale, and the next
writer breaks it without asking. Breaking is serialised by a second lock and
removes the file only if it still names the dead process, so a writer can
never remove a lock a live process has just taken. A lock held from another
machine cannot be checked from here, so it is only ever waited for.
A process that already holds the lock and asks for it again is refused at once instead of waiting for itself forever.
zit pack and zit shun --force exclude each other with the same kind of
lock, .zit-tmp-pack.lock. They now also hold the writer lock across their
final check and the rename that installs the new store. Before, a commit
that landed between the check and the rename went into the store the rename
replaced, and was lost.
Keep a repository on a network filesystem only if one process at a time writes it. Lock files are sound there, but network filesystems cache aggressively, and zit makes no attempt to work around that.
import-git records a project code
Every sync peer checks the repository's project code in its handshake and
refuses a repository without one. zit import-git wrote none, so the store
it made verified cleanly and could not be served, cloned by fossil, or
adopted by a forge. It now records a code derived from the new repository's
path, exactly as zit init does.
A store imported by an earlier release still has no code. Import it again to
give it one. zit verify now says when a repository has no project code,
without counting it as damage.
Tests
- Lock unit tests cover exclusion, a refused second acquire in the same process, breaking a dead holder's lock, waiting for and naming a live one, never breaking another host's lock, and a break that finds a newer lock in place of the stale one and leaves it alone.
- The DST lock sweep now follows the new order: take the lock, read the file's length, open, append, commit, release. Writers can die at any step while holding the lock, and the next writer must break a dead holder's lock and never a live one's.
- The shun sweep crashes the rewrite at every filesystem operation, including each lock operation. The next writer and the next rewrite must both get past whatever locks the crash left behind.
- End to end: a commit breaks a lock left by a killed writer.
purge undowaits for a held lock and names the holder while it waits. An import of a two-commit git repository has a 40-hex project code, and two imports get different codes.
Compatibility
- No on-disk repository or wire format changed; Fossil RFC revision 00 remains the target.
- A writer now gives up after waiting a minute for a live lock holder, instead of waiting indefinitely.
.zit.lockand.zit.lock.breakare reserved names, like.zit-cache: they are never tracked and never listed as extras.
Qualification
zig fmt --check, zig build test, e2e, smoke, git-import-e2e,
git-export-e2e, and dst, plus the three-runner CI battery.