Single-writer guard for the on-disk KB.
The disk record store and the disk KV index have no cross-process file locking:
two JVMs appending to the same logs tear them. This namespace takes an OS advisory
FileLock on <dir>/.vaelii.lock when a disk backend opens, and fails fast if
another live JVM already holds it. This is the single-writer contract
(docs/storage.md) — one process holds the KB.
The lock is exclusive and ref-counted per canonical directory (the record store and
the index share one dir, so both acquire and the last release drops it). The OS
releases it when the JVM exits, so a crash leaves no stale lock to reap. Set
vaelii.disk.lock=false (system property) to disable in a trusted single-host
scenario.
Single-writer guard for the on-disk KB. The disk record store and the disk KV index have no cross-process file locking: two JVMs appending to the same logs tear them. This namespace takes an OS advisory `FileLock` on `<dir>/.vaelii.lock` when a disk backend opens, and fails fast if another live JVM already holds it. This is the single-writer contract (docs/storage.md) — one process holds the KB. The lock is exclusive and ref-counted per canonical directory (the record store and the index share one dir, so both acquire and the last release drops it). The OS releases it when the JVM exits, so a crash leaves no stale lock to reap. Set `vaelii.disk.lock=false` (system property) to disable in a trusted single-host scenario.
(acquire! dir)Acquire the single-writer lock for dir. Reentrant + ref-counted per canonical
dir. Returns :acquired, :reentrant, or :disabled; throws ex-info (:type :disk-locked) when another JVM holds it, or (:type :unreleased) when this JVM took
the lock and could not give it back.
The map is consulted before the switch. A reentrant acquire follows the entry, not
vaelii.disk.lock: a property toggled between the first acquire and this one would
otherwise answer :disabled here without counting, leaving the ref count one short of
its releases and dropping the OS lock while a holder still expects it.
Acquire the single-writer lock for `dir`. Reentrant + ref-counted per canonical dir. Returns :acquired, :reentrant, or :disabled; throws `ex-info` (`:type :disk-locked`) when another JVM holds it, or (`:type :unreleased`) when this JVM took the lock and could not give it back. **The map is consulted before the switch.** A reentrant acquire follows the entry, not `vaelii.disk.lock`: a property toggled between the first acquire and this one would otherwise answer `:disabled` here without counting, leaving the ref count one short of its releases and dropping the OS lock while a holder still expects it.
(disabled?)Is locking switched off (vaelii.disk.lock)? Read at acquire time and nowhere
else, because the property can be toggled at runtime and the three operations would
otherwise disagree about one directory: a lock taken while it was on, then read by a
held? / release! that finds it off, is an OS lock this JVM holds for good with the
map saying the directory is free. So an entry in held is the stored answer — it
exists only for a directory this JVM actually locked — and held? and release! follow
the entry.
Is locking switched off (`vaelii.disk.lock`)? **Read at acquire time and nowhere else**, because the property can be toggled at runtime and the three operations would otherwise disagree about one directory: a lock taken while it was on, then read by a `held?` / `release!` that finds it off, is an OS lock this JVM holds for good with the map saying the directory is free. So an *entry* in `held` is the stored answer — it exists only for a directory this JVM actually locked — and `held?` and `release!` follow the entry.
(held? dir)True when this JVM holds dir's lock (or locking is disabled). The map is read
first: a directory locked while vaelii.disk.lock was on is still locked after it is
turned off, and this answers for the OS lock rather than for the switch.
True when this JVM holds `dir`'s lock (or locking is disabled). The map is read first: a directory locked while `vaelii.disk.lock` was on is still locked after it is turned off, and this answers for the OS lock rather than for the switch.
(release! dir)Drop one ref on dir's lock; release the OS lock when the last ref goes. No-op on a
directory this JVM does not hold — the map is the authority, so a vaelii.disk.lock
toggled off after the acquire cannot strand the OS lock here.
A failure to release keeps the entry. Both steps are attempted and both are logged,
and if either throws the entry stays, marked with the failure: dropping it would say the
directory is free while this JVM still holds the descriptor and, on a failed .release,
the OS lock itself — so a re-acquire! here would take neither, hand out a directory two
writers believe they own, and report the previous holder off the file, which is the
one thing worse than refusing. acquire! refuses such a directory by name (:type :unreleased) instead. Only the process exiting drops what is still held.
Drop one ref on `dir`'s lock; release the OS lock when the last ref goes. No-op on a directory this JVM does not hold — the map is the authority, so a `vaelii.disk.lock` toggled off after the acquire cannot strand the OS lock here. **A failure to release keeps the entry.** Both steps are attempted and both are logged, and if either throws the entry stays, marked with the failure: dropping it would say the directory is free while this JVM still holds the descriptor and, on a failed `.release`, the OS lock itself — so a re-`acquire!` here would take neither, hand out a directory two writers believe they own, and report the *previous* holder off the file, which is the one thing worse than refusing. `acquire!` refuses such a directory by name (`:type :unreleased`) instead. Only the process exiting drops what is still held.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |