lease

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Aug 26, 2026 License: MIT Imports: 13 Imported by: 0

README

lease

CI CodeQL Coverage Mutation Documentation Go Reference Release Go License

lease is a fenced, time-bounded distributed lease primitive for Go 1.26.6 and newer. It provides explicit owners, backend-anchored expiry, renewal, validation, compare-and-release, and monotonically increasing fencing tokens for native Valkey and PostgreSQL backends.

A lease does not stop expired work. Pass Handle.Token() into every protected write and reject tokens lower than the resource's last accepted fence. For reconstructible Valkey cache refreshes, valkey.Store.Guard exposes opaque coordinates from Handle.Snapshot() that pkg/cache can compare atomically with publication.

Five-minute start

Choose a backend guide:

The core shape is:

policy, err := lease.NewPolicy(lease.PolicyOptions{
    TTL: 30 * time.Second, RenewEvery: 10 * time.Second,
    SafetyMargin: 5 * time.Second, Retry: 100 * time.Millisecond,
    Wait: 2 * time.Second, MaxAttempts: 20,
    OperationTimeout: 2 * time.Second,
})
handle, err := client.Acquire(ctx, key, policy)
if err != nil { return err }
managed, err := handle.StartManaged(ctx)
if err != nil { return err }
defer managed.Stop(context.WithoutCancel(ctx))
defer handle.Release(context.WithoutCancel(ctx))

if err := protectedWrite(ctx, handle.Token()); err != nil { return err }

Never infer successful release from cancellation or shutdown. Always inspect the returned error. No acquisition order or starvation guarantee is provided.

Packages

  • root: model, policies, client, handles, managed renewal, observations
  • memory: deterministic process-local reference backend, never distributed
  • valkey: native valkey-go backend using backend-time Lua scripts
  • postgres: native pgx backend and migrations schema
  • leasequeue, leasescheduler, leaseservice: lifecycle integrations
  • leasetest: deterministic clock and cross-backend conformance suite

See the documentation index, security policy, and changelog.

Ecosystem

Use the Golib documentation portal to choose companion packages, supported stacks, recipes, and operations guidance.

Documentation

Overview

Package lease provides time-bounded ownership with monotonic fencing tokens.

Index

Constants

View Source
const (
	// MaxClientWaiters is the largest configurable concurrent acquisition bound.
	MaxClientWaiters uint32 = 1_000_000
	// MaxClientManaged is the largest configurable managed-renewal bound.
	MaxClientManaged uint32 = 100_000
)
View Source
const (
	// MaxTTL bounds one remote lease lifetime.
	MaxTTL = 24 * time.Hour
	// MaxWait bounds acquisition wall-clock waiting.
	MaxWait = time.Hour
	// MaxAttempts bounds backend operations in one acquisition.
	MaxAttempts uint32 = 10_000
	// MaxOperationTimeout bounds one backend call.
	MaxOperationTimeout = time.Minute
)
View Source
const MaxKeyBytes = 256

MaxKeyBytes bounds a complete encoded lease key.

Variables

View Source
var (
	// ErrContended reports that another owner holds the requested lease.
	ErrContended = errors.New("lease: contended")
	// ErrTimeout reports that a bounded acquisition wait elapsed.
	ErrTimeout = errors.New("lease: timeout")
	// ErrCanceled reports that the caller canceled an operation.
	ErrCanceled = errors.New("lease: canceled")
	// ErrLost reports that a formerly owned lease is no longer safely usable.
	ErrLost = errors.New("lease: lost")
	// ErrStaleOwner reports an owner or fencing token that is no longer current.
	ErrStaleOwner = errors.New("lease: stale owner")
	// ErrBackendUnavailable reports a definite backend availability failure.
	ErrBackendUnavailable = errors.New("lease: backend unavailable")
	// ErrInvalidState reports invalid input or an invalid state transition.
	ErrInvalidState = errors.New("lease: invalid state")
	// ErrAmbiguousOutcome reports that a remote mutation may have committed.
	ErrAmbiguousOutcome = errors.New("lease: ambiguous outcome")
)

Functions

func Wrap

func Wrap(err error, operation string) error

Wrap adds a redacted operation name while preserving error classification.

Types

type Backend

type Backend interface {
	TryAcquire(context.Context, Key, string, time.Duration) (Record, error)
	Renew(context.Context, Record, time.Duration) (Record, error)
	Validate(context.Context, Record) (Record, error)
	Release(context.Context, Record) error
}

Backend atomically persists one fenced lease record per key.

func NewObservedBackend

func NewObservedBackend(backend Backend, clock Clock, observers ...Observer) (Backend, error)

NewObservedBackend decorates a backend with at most sixteen observers.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client acquires handles from one lease backend.

func NewClient

func NewClient(backend Backend, options ClientOptions) (*Client, error)

NewClient constructs a lease client with cryptographic production defaults.

func (*Client) Acquire

func (client *Client) Acquire(
	ctx context.Context,
	key Key,
	policy Policy,
) (*Handle, error)

Acquire retries contention within both wait and attempt bounds.

func (*Client) TryAcquire

func (client *Client) TryAcquire(
	ctx context.Context,
	key Key,
	policy Policy,
) (*Handle, error)

TryAcquire performs exactly one atomic acquisition attempt.

type ClientOptions

type ClientOptions struct {
	Clock      Clock
	Owners     OwnerSource
	Sleeper    Sleeper
	Retry      RetrySource
	MaxWaiters uint32
	MaxManaged uint32
}

ClientOptions injects deterministic sources used by a lease client.

type Clock

type Clock interface {
	Now() time.Time
}

Clock supplies time without production global state in deterministic tests.

type Event

type Event struct {
	At        time.Time
	Operation Operation
	Outcome   Outcome
	KeyHash   string
}

Event is safe for default logs, metric labels, and trace attributes.

func (Event) String

func (event Event) String() string

String returns a bounded redaction-safe representation.

type FailureBehavior

type FailureBehavior uint8

FailureBehavior defines ownership admission after backend failure.

const (
	// FailureFailClosed denies admission after every uncertain operation.
	FailureFailClosed FailureBehavior = iota + 1
)

type Handle

type Handle struct {
	// contains filtered or unexported fields
}

Handle is a concurrency-safe local view of remotely fenced ownership.

func (*Handle) AcquiredAt

func (handle *Handle) AcquiredAt() time.Time

AcquiredAt returns the backend-anchored acquisition instant.

func (*Handle) Deadline

func (handle *Handle) Deadline() time.Time

Deadline returns the local safety deadline, not the raw backend expiry.

func (*Handle) Owner

func (handle *Handle) Owner() string

Owner returns the opaque identity used for ownership comparisons.

func (*Handle) Release

func (handle *Handle) Release(ctx context.Context) error

Release idempotently compares owner and token before deactivation.

func (*Handle) Renew

func (handle *Handle) Renew(ctx context.Context) error

Renew atomically compares owner and token before extending the deadline.

func (*Handle) Snapshot

func (handle *Handle) Snapshot() Record

Snapshot returns the latest backend-authenticated ownership record. It is intended for composing protected adapters such as Valkey ownership guards; callers must use State or Validate for local admission decisions.

func (*Handle) StartManaged

func (handle *Handle) StartManaged(ctx context.Context) (*Managed, error)

StartManaged starts at most one caller-owned renewal goroutine.

func (*Handle) State

func (handle *Handle) State() State

State returns the current fail-closed local state.

func (*Handle) Token

func (handle *Handle) Token() Token

Token returns the fencing token for protected-resource writes.

func (*Handle) Validate

func (handle *Handle) Validate(ctx context.Context) error

Validate checks remote ownership and fails local admission closed on error.

type Key

type Key struct {
	// contains filtered or unexported fields
}

Key is a validated, namespaced lease identity.

func NewKey

func NewKey(namespace, name string) (Key, error)

NewKey constructs a bounded key from non-empty namespace and name segments.

func ParseKey

func ParseKey(value string) (Key, error)

ParseKey validates a canonical namespace/name representation.

func (Key) String

func (key Key) String() string

String returns the canonical namespaced representation.

type Loss

type Loss struct {
	At    time.Time
	State State
	Err   error
}

Loss describes why managed renewal stopped admitting ownership-dependent work.

type Managed

type Managed struct {
	// contains filtered or unexported fields
}

Managed owns one bounded renewal goroutine for a handle.

func (*Managed) Loss

func (managed *Managed) Loss() <-chan Loss

Loss returns a channel that yields at most one terminal renewal failure.

func (*Managed) Stop

func (managed *Managed) Stop(ctx context.Context) error

Stop stops renewal and waits for the goroutine. It never implies release.

type Observer

type Observer interface{ Observe(Event) }

Observer consumes a redacted best-effort event outside backend locks. At most one callback per observer runs at once; events are dropped while busy.

type ObserverFunc

type ObserverFunc func(Event)

ObserverFunc adapts a function to Observer.

func (ObserverFunc) Observe

func (observer ObserverFunc) Observe(event Event)

Observe invokes the adapted observer.

type Operation

type Operation string

Operation is a bounded observation operation name.

const (
	// OperationAcquire identifies one acquisition attempt.
	OperationAcquire Operation = "acquire"
	// OperationRenew identifies one renewal attempt.
	OperationRenew Operation = "renew"
	// OperationValidate identifies one validation attempt.
	OperationValidate Operation = "validate"
	// OperationRelease identifies one release attempt.
	OperationRelease Operation = "release"
)

type Outcome

type Outcome string

Outcome is a bounded, identifier-free observation result.

const (
	// OutcomeSuccess reports a successful operation.
	OutcomeSuccess Outcome = "success"
	// OutcomeContended reports expected acquisition contention.
	OutcomeContended Outcome = "contended"
	// OutcomeStale reports a rejected stale owner.
	OutcomeStale Outcome = "stale"
	// OutcomeCanceled reports caller cancellation.
	OutcomeCanceled Outcome = "canceled"
	// OutcomeUnavailable reports a definite backend failure.
	OutcomeUnavailable Outcome = "unavailable"
	// OutcomeAmbiguous reports an uncertain remote mutation.
	OutcomeAmbiguous Outcome = "ambiguous"
	// OutcomeInvalid reports invalid input or state.
	OutcomeInvalid Outcome = "invalid"
)

type OwnerSource

type OwnerSource interface {
	NewOwner() (string, error)
}

OwnerSource creates opaque owner identities.

type Policy

type Policy struct {
	// contains filtered or unexported fields
}

Policy is an immutable, bounded acquisition policy.

func NewPolicy

func NewPolicy(options PolicyOptions) (Policy, error)

NewPolicy validates and copies acquisition options.

func (Policy) FailureBehavior

func (policy Policy) FailureBehavior() FailureBehavior

FailureBehavior returns the immutable fail-closed admission policy.

func (Policy) Jitter

func (policy Policy) Jitter() time.Duration

Jitter returns the maximum retry jitter.

func (Policy) MaxAttempts

func (policy Policy) MaxAttempts() uint32

MaxAttempts returns the total acquisition attempt bound.

func (Policy) OperationTimeout

func (policy Policy) OperationTimeout() time.Duration

OperationTimeout returns the maximum duration of one backend call.

func (Policy) RenewEvery

func (policy Policy) RenewEvery() time.Duration

RenewEvery returns the managed-renewal interval, or zero when disabled.

func (Policy) Retry

func (policy Policy) Retry() time.Duration

Retry returns the retry interval before jitter.

func (Policy) SafetyMargin

func (policy Policy) SafetyMargin() time.Duration

SafetyMargin returns time reserved for delay and uncertainty.

func (Policy) TTL

func (policy Policy) TTL() time.Duration

TTL returns the remote lease lifetime.

func (Policy) Wait

func (policy Policy) Wait() time.Duration

Wait returns the maximum acquisition wait.

type PolicyOptions

type PolicyOptions struct {
	TTL              time.Duration
	Wait             time.Duration
	Retry            time.Duration
	Jitter           time.Duration
	RenewEvery       time.Duration
	SafetyMargin     time.Duration
	MaxAttempts      uint32
	OperationTimeout time.Duration
	FailureBehavior  FailureBehavior
}

PolicyOptions configures immutable acquisition and renewal behavior.

type Record

type Record struct {
	Key        Key
	Owner      string
	Token      Token
	AcquiredAt time.Time
	ExpiresAt  time.Time
}

Record is the backend-authenticated ownership snapshot for a lease.

func (Record) SafeDeadline

func (record Record) SafeDeadline(margin time.Duration) time.Time

SafeDeadline returns the backend-clock expiry after reserving margin. Handle.Deadline is the authoritative local admission bound.

func (Record) UsableAt

func (record Record) UsableAt(now time.Time, margin time.Duration) bool

UsableAt compares a backend-clock time with the backend-clock safe deadline. Handle.State is the authoritative local admission check.

type RetrySource

type RetrySource interface {
	Jitter(time.Duration) time.Duration
}

RetrySource supplies deterministic bounded acquisition jitter.

type Sleeper

type Sleeper interface {
	Sleep(context.Context, time.Duration) error
}

Sleeper performs a cancelable acquisition delay.

type State

type State uint8

State is the local fail-closed lifecycle state of a lease handle.

const (
	// StateActive permits admission before the safety deadline.
	StateActive State = iota + 1
	// StateExpired means the local safety deadline has passed.
	StateExpired
	// StateLost means the backend proved ownership is stale.
	StateLost
	// StateUncertain means a remote operation had no reliable outcome.
	StateUncertain
	// StateReleased means compare-and-release completed successfully.
	StateReleased
)

func (State) String

func (state State) String() string

String returns a stable redaction-safe state name.

type Token

type Token uint64

Token is a monotonically increasing fencing value for one key.

Directories

Path Synopsis
examples
postgres command
Command postgres demonstrates acquiring a PostgreSQL-backed fenced lease.
Command postgres demonstrates acquiring a PostgreSQL-backed fenced lease.
protectedwrite command
Command protectedwrite demonstrates rejecting a stale fencing token.
Command protectedwrite demonstrates rejecting a stale fencing token.
valkey command
Command valkey demonstrates acquiring a Valkey-backed fenced lease.
Command valkey demonstrates acquiring a Valkey-backed fenced lease.
internal
failure
Package failure preserves error identity without rendering sensitive causes.
Package failure preserves error identity without rendering sensitive causes.
guard
Package guard runs callbacks under fail-closed managed lease ownership.
Package guard runs callbacks under fail-closed managed lease ownership.
Package leasequeue integrates fenced leases with queue workers.
Package leasequeue integrates fenced leases with queue workers.
Package leasescheduler provides on-one-server and non-overlap execution.
Package leasescheduler provides on-one-server and non-overlap execution.
Package leaseservice integrates managed leases with service lifecycle hooks.
Package leaseservice integrates managed leases with service lifecycle hooks.
Package leasetest provides deterministic lease conformance utilities.
Package leasetest provides deterministic lease conformance utilities.
Package memory provides a deterministic process-local reference backend.
Package memory provides a deterministic process-local reference backend.
Package postgres provides native durable fenced leases for PostgreSQL.
Package postgres provides native durable fenced leases for PostgreSQL.
Package valkey provides native atomic fenced leases for Valkey.
Package valkey provides native atomic fenced leases for Valkey.

Jump to

Keyboard shortcuts

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