lease

package
v0.3.3 Latest Latest
Warning

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

Go to latest
Published: Nov 1, 2026 License: MIT Imports: 25 Imported by: 0

Documentation

Overview

Package lease supplies typed, time-limited ownership with explicit loss and cleanup.

Index

Constants

View Source
const (
	MinDuration = 10 * time.Millisecond
	MaxDuration = 24 * time.Hour
)

Duration bounds keep expiry finite and leave room for client validity margins.

View Source
const KeyFeature keyspace.Feature = "lease"

KeyFeature is the physical address family owned by this adapter.

View Source
const MaxSemaphoreSlots = 1024

MaxSemaphoreSlots bounds the leases one semaphore resource can hold.

View Source
const OwnerBytes = 32

Variables

View Source
var ErrExported = safeerror.Sentinel(fault.Closed, "lease ownership exported")

ErrExported marks a guard whose ownership was handed to another process.

View Source
var ErrLost = safeerror.Sentinel(fault.Conflict, "lease ownership lost")

ErrLost means ownership could no longer be confirmed within its validity window.

View Source
var ErrReleased = safeerror.Sentinel(fault.Closed, "lease released")

ErrReleased marks explicit release of a guard's work context.

Functions

func Module

func Module(name foundation.ProviderID, key foundation.Key[*Manager], config Config, requires []foundation.ProviderID, backend func(foundation.Resolver) (Backend, error)) foundation.Module

Module builds one manager per application. requires identifies the provider(s) owning the borrowed backend, ensuring leases drain before those adapters close.

func ValidateDuration

func ValidateDuration(ttl time.Duration) error

ValidateDuration rejects durations outside MinDuration through MaxDuration with fault.Invalid.

func ValidateOperation

func ValidateOperation(ctx context.Context, key Key, owner Owner, ttl time.Duration) error

ValidateOperation shares the adapter input boundary. Release passes MinDuration.

Types

type Backend

type Backend interface {
	// LeaseAcquire sets an absent key to owner for ttl and reports whether ownership was acquired.
	LeaseAcquire(context.Context, Key, Owner, time.Duration) (bool, error)
	// LeaseRenew extends ttl only while owner still owns an unexpired key.
	LeaseRenew(context.Context, Key, Owner, time.Duration) (bool, error)
	// LeaseRelease removes key only while owner still owns it and reports whether it was removed.
	LeaseRelease(context.Context, Key, Owner) (bool, error)
}

Backend atomically acquires an absent key, or renews/releases only its current owner. Expired ownership is absent. Errors must never be treated as contention. Implementations must honor cancellation, perform no implicit mutation retries, and bound stored owner reads. TTL begins when the authority accepts a mutation. Renew cannot recreate expired ownership. Callers own adapter lifecycle.

type Config

type Config struct {
	Namespace                               keyspace.Namespace
	MaxActive, MaxDeclarations, MaxKeyBytes int
	OperationTimeout, MaxWait, PollInterval time.Duration
}

Config bounds all acquisitions, waiters, live guards and owned callbacks in one manager. A scoped callback retains its slot until it actually exits, even after ownership loss. OperationTimeout also bounds each independent cleanup attempt.

func DefaultConfig

func DefaultConfig(namespace keyspace.Namespace) Config

DefaultConfig returns the default manager bounds for namespace.

func (Config) Validate

func (c Config) Validate() error

Validate checks the namespace and positive operation, capacity, key, and polling bounds.

type Declaration

type Declaration[K any] struct {
	// contains filtered or unexported fields
}

Declaration retains the resource key type. Reuse one declared value per family.

func Define

func Define[K any](name Name, codec keyspace.Codec[K]) Declaration[K]

Define constructs a typed lease family with the supplied key codec.

func (Declaration[K]) Bind

func (d Declaration[K]) Bind(m *Manager) (Leases[K], error)

Bind rejects a distinct declaration with the same family name, even for the same K.

func (Declaration[K]) Name

func (d Declaration[K]) Name() Name

Name returns the family name, or an empty name for a zero declaration.

func (Declaration[K]) Validate

func (d Declaration[K]) Validate() error

Validate checks the family name and key codec; a zero declaration is invalid.

type ForceBackend

type ForceBackend interface {
	// LeaseForceRelease removes key regardless of its owner and reports whether it existed.
	LeaseForceRelease(context.Context, Key) (bool, error)
}

ForceBackend is an optional administrative capability that removes a lease address regardless of its owner, including metadata that ordinary operations reject as corrupt (for example a Redis lease key without expiry). It is never used implicitly; ordinary acquisition, renewal and release stay owner-checked.

type Guard

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

Guard represents one ownership interval. It has no finalizer: release it explicitly, cancel its parent, or close its manager. Expiry also owns cleanup. Context cancellation is cooperative; external writes require resource fencing when stale-process exclusion is necessary. Guards cannot be copied usefully.

func (*Guard) Context

func (g *Guard) Context() context.Context

Context preserves caller values and is canceled on release, expiry, renewal failure, caller cancellation or manager shutdown. A zero guard returns nil.

func (*Guard) Done

func (g *Guard) Done() <-chan struct{}

Done closes after renewal work and owner cleanup finish. A nil guard returns nil.

func (*Guard) Err

func (g *Guard) Err() error

Err rechecks validity synchronously, including after a process scheduling pause.

func (*Guard) Proof

func (g *Guard) Proof() (Proof, error)

Proof exposes this guard's ownership to an adapter, without allowing callers to construct an arbitrary key/token pair. Ordinary domain work needs Context.

func (*Guard) Release

func (g *Guard) Release(ctx context.Context) error

Release cancels protected work and waits for one owner-conditional cleanup. Caller cancellation only stops waiting. Repeated/concurrent releases share the same attempt and result. Ownership loss remains available from Err separately.

func (*Guard) Renew

func (g *Guard) Renew(ctx context.Context) error

Renew compares the opaque owner atomically. It cannot revive a locally expired interval, even if the authority accepted a late response. Only one renewal per guard can run; concurrent attempts fail with Conflict instead of queuing.

type Key

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

Key is an opaque adapter address. It cannot be confused with a cache entry.

func NewKey

func NewKey(namespace keyspace.Namespace, name Name, logical string) (Key, error)

NewKey is for adapters. Applications acquire through a typed bound declaration.

func (Key) Namespace

func (k Key) Namespace() keyspace.Namespace

Namespace returns the application and environment scope of the key.

func (Key) String

func (k Key) String() string

String returns the physical lease address.

func (Key) Validate

func (k Key) Validate() error

Validate checks that the key contains a valid namespaced address.

type Leases

type Leases[K any] struct {
	// contains filtered or unexported fields
}

Leases only accepts its declaration's concrete resource key type.

func (Leases[K]) Acquire

func (l Leases[K]) Acquire(ctx context.Context, key K, ttl, wait time.Duration) (*Guard, bool, error)

Acquire waits only on confirmed contention, up to wait. A zero wait tries once. Wait expiration returns context.DeadlineExceeded; backend uncertainty is never retried. The guard's context retains the original caller lifetime, not the acquisition timeout.

func (Leases[K]) Export

func (l Leases[K]) Export(g *Guard) (Token[K], error)

Export hands a live explicit guard's ownership out of this process. The guard stops renewing and ends with ErrExported WITHOUT releasing the authority key; the importing process must Restore the token before the lease's TTL expires and becomes responsible for releasing it. The token is single-use (see Restore). The guard must belong to l's family.

func (Leases[K]) ForceRelease

func (l Leases[K]) ForceRelease(ctx context.Context, key K) (bool, error)

ForceRelease administratively removes key's lease whatever its owner. Use it to repair corrupt or non-expiring lease metadata, or to break ownership after confirming the owning process is gone. It reports whether a lease existed. A current holder is not notified: it loses ownership at its next renewal or protected write, so resources that need stale-process exclusion still need their own fencing. The backend must implement ForceBackend.

func (Leases[K]) Restore

func (l Leases[K]) Restore(ctx context.Context, token Token[K]) (*Guard, error)

Restore resumes ownership from an exported token. Tokens are single-use: the authority atomically replaces the token's owner secret with a fresh one and renews the lease, so restoring the same token again (a retried job or a redelivered message) returns ErrLost instead of creating a second holder. It returns an explicit guard (no automatic heartbeat) under the same rules as Acquire. A token whose lease expired, was released, was replaced or was already restored returns ErrLost. The backend must implement TransferBackend.

func (Leases[K]) TryAcquire

func (l Leases[K]) TryAcquire(ctx context.Context, key K, ttl time.Duration) (*Guard, bool, error)

TryAcquire attempts once. Contention returns (nil,false,nil). A returned guard owns its finite lifetime and must be released. It renews only when Renew is called.

func (Leases[K]) With

func (l Leases[K]) With(ctx context.Context, key K, ttl, wait time.Duration, fn func(context.Context) error) (bool, error)

With runs an owned callback while automatically renewing. It returns whether the callback ran, plus callback/loss/cleanup errors. Cancellation or renewal uncertainty cancels the supplied context; return promptly when it is canceled. With always waits for the callback's actual exit before releasing its slot.

func (Leases[K]) WithProof

func (l Leases[K]) WithProof(ctx context.Context, key K, ttl, wait time.Duration, fn func(context.Context, Proof) error) (bool, error)

WithProof is With for feature/adaptor integration. The callback receives a live proof that its backend must check atomically with any protected write. It shares With's heartbeat, cancellation, callback isolation, limits and cleanup semantics.

type Manager

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

Manager owns bounded acquisitions, guards and callbacks, borrowing its Backend. Construction is pure. Close cancels work and waits for actual cleanup; it never closes the borrowed adapter. Use Module to order cleanup before the adapter.

func NewManager

func NewManager(backend Backend, config Config) (*Manager, error)

NewManager validates configuration and constructs a manager borrowing backend without performing I/O.

func (*Manager) BorrowedBackend

func (m *Manager) BorrowedBackend() Backend

BorrowedBackend lets another framework feature use the SAME authority. It does not transfer adapter ownership; close dependent features before that adapter.

func (*Manager) Close

func (m *Manager) Close(ctx context.Context) error

Close starts cancellation once. A canceled caller stops waiting, while owned cleanup continues. Noncooperative domain callbacks delay Done; they are not abandoned. Do not call Close synchronously from one of this manager's own callbacks.

func (*Manager) Done

func (m *Manager) Done() <-chan struct{}

Done closes when shutdown has drained all owned work and cleanup. A nil manager returns nil.

func (*Manager) Namespace

func (m *Manager) Namespace() keyspace.Namespace

Namespace identifies the authority scope shared by feature integrations.

func (*Manager) PollInterval

func (m *Manager) PollInterval() time.Duration

PollInterval is the configured contention poll interval, shared by features that wait on this manager's leases (for example coordinated cache fills).

func (*Manager) Stats

func (m *Manager) Stats() Stats

Stats returns a synchronized snapshot of reserved slots and shutdown state.

func (*Manager) ValidateScope

func (m *Manager) ValidateScope(ttl, wait time.Duration) error

ValidateScope checks construction-time duration/wait limits without reserving capacity or performing I/O. Operations also check lifecycle and cancellation.

type Name

type Name string

Name identifies a lease family; it is distinct from cache family names.

type Owner

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

Owner is an opaque, random adapter token. It is never exposed by application guards.

func NewOwner

func NewOwner() (Owner, error)

NewOwner creates an owner token using cryptographic randomness.

func ParseOwner

func ParseOwner(data []byte) (Owner, error)

ParseOwner copies and validates an owner token of exactly OwnerBytes bytes.

func (Owner) Bytes

func (o Owner) Bytes() []byte

Bytes returns an owned copy of the secret owner token.

func (Owner) GoString

func (o Owner) GoString() string

GoString returns a redacted description of the owner.

func (Owner) String

func (o Owner) String() string

String returns a redacted description of the owner.

func (Owner) Validate

func (o Owner) Validate() error

Validate rejects the all-zero owner token with fault.Invalid.

type Proof

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

Proof is an opaque adapter capability for one live lease owner. An adapter must validate it locally AND atomically compare Key/Owner at its authority when applying a protected mutation. Validate alone cannot fence a subsequent write. Proof cannot outlive its guard and must not be serialized into durable work.

func (Proof) GoString

func (p Proof) GoString() string

GoString returns a redacted description of the proof.

func (Proof) Key

func (p Proof) Key() Key

Key is the complete namespaced lease address for atomic authority checks.

func (Proof) Owner

func (p Proof) Owner() Owner

Owner is the opaque token for adapter wire encoding; it is not a fencing sequence.

func (Proof) String

func (p Proof) String() string

String returns a redacted description of the proof.

func (Proof) Validate

func (p Proof) Validate() error

Validate checks the guard's current local validity without performing an authority check.

type Semaphore

type Semaphore[K any] struct {
	// contains filtered or unexported fields
}

Semaphore retains its declaration's concrete resource key type.

func (Semaphore[K]) Acquire

func (s Semaphore[K]) Acquire(ctx context.Context, key K, ttl, wait time.Duration) (*Guard, bool, error)

Acquire takes any free slot, waiting up to wait while all slots are held. The first attempt tries every slot once in random order (a zero wait stops there); each later poll tries at most 8 random slots. Like Leases.Acquire, the guard does not renew automatically; contention returns (nil, false, nil) and backend errors stop immediately without being treated as contention.

func (Semaphore[K]) Slots

func (s Semaphore[K]) Slots() int

Slots is the declared number of concurrent holders per resource key.

func (Semaphore[K]) With

func (s Semaphore[K]) With(ctx context.Context, key K, ttl, wait time.Duration, fn func(context.Context) error) (bool, error)

With runs fn while holding a slot, renewing it like Leases.With. It reports whether fn ran. Contention within wait is (false, nil).

type SemaphoreDeclaration

type SemaphoreDeclaration[K any] struct {
	// contains filtered or unexported fields
}

SemaphoreDeclaration is a typed distributed concurrency limit: at most Slots holders of the same resource key at once across every process sharing the lease authority. Each holder owns one ordinary lease, so expiry, renewal, loss and cleanup follow the lease contract. Reuse one declared value.

func DefineSemaphore

func DefineSemaphore[K any](name Name, codec keyspace.Codec[K], slots int) SemaphoreDeclaration[K]

DefineSemaphore declares a family limiting each resource key to slots holders.

func (SemaphoreDeclaration[K]) Bind

func (d SemaphoreDeclaration[K]) Bind(m *Manager) (Semaphore[K], error)

Bind shares the manager's declaration ownership, limits and lifecycle.

func (SemaphoreDeclaration[K]) Name

func (d SemaphoreDeclaration[K]) Name() Name

Name returns the semaphore lease family name.

func (SemaphoreDeclaration[K]) Validate

func (d SemaphoreDeclaration[K]) Validate() error

Validate checks the slot count and underlying lease declaration.

type Stats

type Stats struct {
	Active  int
	Closing bool
}

Stats reports all reserved slots, including waiters and callbacks still exiting.

type Token

type Token[K any] struct {
	// contains filtered or unexported fields
}

Token is a transferable handle to one held lease of family K: its complete address, opaque owner and duration. It carries the owner secret, so treat it as a credential: formatting redacts it; only MarshalText exposes it. The type parameter keeps a token from being restored into another family's handle.

func (Token[K]) Format

func (t Token[K]) Format(s fmt.State, _ rune)

Format writes a redacted description without exposing the owner secret.

func (Token[K]) GoString

func (t Token[K]) GoString() string

GoString returns a redacted description of the transferable token.

func (Token[K]) MarshalText

func (t Token[K]) MarshalText() ([]byte, error)

MarshalText encodes the token, including its owner secret, for transfer.

func (Token[K]) String

func (Token[K]) String() string

String returns a redacted description of the transferable token.

func (*Token[K]) UnmarshalText

func (t *Token[K]) UnmarshalText(data []byte) error

UnmarshalText decodes and validates a token produced by MarshalText.

type TransferBackend

type TransferBackend interface {
	// LeaseTransfer atomically replaces from with to and renews ttl only while from owns key.
	LeaseTransfer(ctx context.Context, key Key, from, to Owner, ttl time.Duration) (bool, error)
}

TransferBackend optionally hands a live lease to a new owner atomically: while from still owns key, the owner becomes to and the lease is renewed for ttl; otherwise nothing changes and it returns false. Restore requires it so every exported token is single-use. Memory and Redis implement it.

Directories

Path Synopsis
Package memory implements explicit single-process leases.
Package memory implements explicit single-process leases.

Jump to

Keyboard shortcuts

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