lease

package
v0.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 4, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package lease elects the one process currently serving as a resident for a durable codeaf store. The lock is only coordination; the journal remains the source of truth and another process may take the role as soon as it is free.

Index

Constants

View Source
const (

	// StuckAfter is how long a holder may go without stamping a completed pass
	// before the role is considered abandoned. It is several resident poll
	// intervals over, deliberately: a busy pass, a slow model call and a paused
	// laptop all take longer than one interval, and taking the role away from a
	// process that is merely working would be far worse than waiting.
	StuckAfter = 5 * time.Minute
)

Variables

This section is empty.

Functions

func LockPath

func LockPath(store string) (string, error)

LockPath is where the lock for one store lives. It is keyed to the store file, not to the directory holding it: two databases that happen to share a directory are two stores, they need two residents, and a directory-wide lock made the second one wait on a brain that was serving somebody else's journal.

func NoteResidentTick

func NoteResidentTick(store string, at time.Time) error

NoteResidentTick stamps a completed resident pass onto the lock the calling process holds. It is deliberately stateless and deliberately fussy about who may write: only the holder stamps its own liveness, so a second process cannot make a wedged resident look alive.

Types

type Build

type Build struct {
	ModTime  time.Time `json:"mod_time,omitempty"`
	Size     int64     `json:"size,omitempty"`
	Revision string    `json:"revision,omitempty"`
}

Build is the identity of a running binary, kept deliberately small.

The obvious signal is the shared build revision, and it rides along here because it is the only part a human reading the lock file can act on. It cannot be the deciding one: a `go build` of a tree with uncommitted work stamps the revision of the commit underneath it, or nothing at all, so the rebuild that actually caused a handover to be needed is the one case where two binaries share a revision. Two revisions also do not order — deciding which of them is newer needs the repository, which a lock file does not have.

The executable's own mtime has none of those problems. It always exists, a rebuild always moves it forward, and it compares with a single operator. So mtime decides, size disambiguates a same-second rebuild, and the revision is a label.

func LocalBuild

func LocalBuild() Build

LocalBuild stamps the binary this process is running. Everything it reads can fail on an exotic platform, and every failure degrades to the zero value — which the comparison below reads as "would not claim to be newer".

func (Build) NewerThan

func (b Build) NewerThan(other Build) bool

NewerThan asks whether this build should be allowed to displace another. It answers no whenever it cannot answer yes: an unstamped holder is an older binary that predates handover entirely, and taking the role from it on a guess would be the same mistake as treating a silent heartbeat as a dead process. Those residents are reclaimed by the stale-heartbeat path instead.

type Resident

type Resident struct {
	PID        int       `json:"pid"`
	Host       string    `json:"host"`
	Surface    string    `json:"surface"`
	AcquiredAt time.Time `json:"acquired_at"`
	// Store is the database this holder is the resident for. It is written so a
	// process that finds the role taken can say which store it was taken for —
	// a lock that names nothing is exactly what made a whole grid of headless
	// runs sit at nodes:0 for their entire wall with no way to see why. An empty
	// value means an older build wrote the payload.
	Store string `json:"store,omitempty"`
	// LastTick is when the holder last finished a resident pass. Zero means the
	// holder never said — an flock proves a process is alive, never that it is
	// still doing the work — and silence is deliberately read as unknown rather
	// than as dead, so a surface that does not stamp is never taken from.
	LastTick time.Time `json:"last_tick,omitempty"`

	// Build identifies the binary the holder is running. A missing stamp means
	// the holder is an older build that never wrote one, and — exactly as with
	// LastTick — silence is read as unknown rather than as old.
	Build Build `json:"build,omitempty"`

	// Stuck is derived at probe time and never serialized: the holder is alive,
	// has stamped a pass at some point, and has not stamped one since.
	Stuck bool `json:"-"`
}

Resident describes the process whose open file descriptor currently holds resident.lock. A payload left behind without a live flock is stale and is deliberately ignored by ProbeResident.

func AcquireResident

func AcquireResident(store, surface string) (release func() error, heldBy *Resident, err error)

AcquireResident attempts to become the resident for one store. On success heldBy is nil and release relinquishes the role. If another live process holds the lock, release is nil and heldBy describes that process.

func ProbeResident

func ProbeResident(store string) (*Resident, error)

ProbeResident reports the live holder of resident.lock. It never trusts the JSON by itself: if a non-blocking flock succeeds, any payload is stale and the resident role is free.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL