Documentation
¶
Overview ¶
Package lock provides cross-process advisory locking around .agent-memory/meta/lock via github.com/gofrs/flock. The kernel owns lock state, so process death releases the lock automatically — no application stale-recovery code is needed.
See docs/patterns/cross-process-locking.md for the design rationale and docs/spikes/s3-results.md for the empirical validation that motivated this approach.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrLockHeld = errors.New("lock held by another process")
ErrLockHeld is returned by Acquire when the lock cannot be obtained within AcquireOpts.WaitTimeout (or immediately, if WaitTimeout is zero).
Functions ¶
func MetadataPath ¶
MetadataPath returns the sidecar file path where Acquire writes the Metadata JSON for a lock at lockPath.
Types ¶
type AcquireOpts ¶
type AcquireOpts struct {
// WaitTimeout caps how long Acquire blocks waiting for a contended lock:
// 0 → TryLock once and return ErrLockHeld immediately if unavailable.
// >0 → TryLock then poll (every ~10ms) until the lock is available or
// the timeout expires.
WaitTimeout time.Duration
// Owner is the metadata to write into the lock file on successful
// acquisition. Empty fields are filled in: OwnerPID defaults to
// os.Getpid(), AcquiredAt defaults to time.Now().UTC().
Owner Metadata
}
AcquireOpts configures Acquire.
type Lock ¶
type Lock struct {
// contains filtered or unexported fields
}
Lock is a held advisory lock. Release it via Release(); the OS releases the underlying lock automatically on process exit even if Release is not called.
func Acquire ¶
func Acquire(path string, opts AcquireOpts) (*Lock, error)
Acquire opens (creating if missing) the file at path and tries to acquire an exclusive OS advisory lock on it.
If the lock is held by another process and AcquireOpts.WaitTimeout is zero, Acquire returns ErrLockHeld immediately. If WaitTimeout is positive, Acquire polls until the lock is available or the timeout expires.
On success, Acquire writes the owner metadata into the lock file. This is best-effort and never affects correctness.
The lock file persists on disk; only the OS lock is transient. Callers should typically point path at .agent-memory/meta/lock.
type Metadata ¶
type Metadata struct {
OwnerPID int `json:"owner_pid"`
OwnerID string `json:"owner_id"` // e.g. "claude-code-session-abc"
OwnerKind string `json:"owner_kind"` // "agent" | "cli" | "cli-merge-driver" | ...
AcquiredAt time.Time `json:"acquired_at"`
OpID string `json:"op_id,omitempty"`
}
Metadata is the informational JSON written inside the lock file by the current holder. It NEVER gates correctness — the OS lock is ground truth. Stale metadata after a crashed holder is harmless: the next acquirer overwrites it.
func ReadMetadata ¶
ReadMetadata reads the JSON metadata written by the current (or last) lock holder. Best-effort: a missing, empty, or malformed file returns an empty Metadata with no error. The OS lock is ground truth for whether the lock is held; this function exists only for status/debugging.
The metadata lives in a sidecar file (MetadataPath(lockPath)), not in the lock file itself. See the package comment for the rationale.