lock

package
v0.38.0 Latest Latest
Warning

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

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

Documentation

Index

Constants

View Source
const (
	// DefaultTTL is the lease duration when WithTTL is not given.
	DefaultTTL = 30 * time.Second
	// DefaultRetryInterval is the polling cadence of a waiting Acquire.
	DefaultRetryInterval = 100 * time.Millisecond
	// MinAutoRenewTTL is the shortest lease supported by the renewal watchdog.
	// It leaves three renewal intervals inside one lease lifetime.
	MinAutoRenewTTL = 30 * time.Millisecond
)

Default values applied when the corresponding Option is omitted.

Variables

View Source
var (
	// ErrNotAcquired indicates the lock is held by someone else and the
	// acquisition gave up (immediately, or after the WithWait window).
	ErrNotAcquired = errors.New("lock not acquired")
	// ErrNotHeld indicates a release or refresh on a lease that is no longer
	// owned — it expired, was released already, or was taken over.
	ErrNotHeld = errors.New("lock not held")
	// ErrAutoRenewTTLTooShort indicates the requested lease is too short for
	// the watchdog to renew without a tight spin loop.
	ErrAutoRenewTTLTooShort = errors.New("lock auto-renew TTL too short")
)

Functions

func WithLock

func WithLock(ctx context.Context, locker Locker, name string, fn func(ctx context.Context) error, opts ...Option) (err error)

WithLock runs fn while holding the named lock: it acquires (auto-renewal on by default, so fn may safely outlive the TTL), cancels fn's context if the lease is lost, and always releases afterwards — even when fn panics — on a context that survives request cancellation. It returns the joined error of fn and the release — a successful fn still yields an error when the lease was lost mid-run, because the exclusive section can no longer be trusted.

Types

type Lock

type Lock interface {
	// Release relinquishes the lease. It returns ErrNotHeld when the lease has
	// already expired or been released — a signal that mutual exclusion may
	// have been violated in the meantime.
	Release(ctx context.Context) error
	// Refresh extends the lease by the acquisition TTL from now. It returns
	// ErrNotHeld once the lease is no longer owned. With auto-renewal enabled
	// the lease refreshes itself and calling Refresh is unnecessary.
	Refresh(ctx context.Context) error
	// FencingToken returns the lease's monotonically increasing sequence
	// number (unique and ordered per lock name). Pass it to the protected
	// resource so a delayed writer holding a stale lease can be rejected.
	FencingToken() int64
	// Done returns a channel closed once the lease is known to be lost.
	// Loss is detected by the auto-renewal watchdog, so without WithAutoRenew
	// the channel never closes.
	Done() <-chan struct{}
}

Lock is a held lease returned by a successful acquisition.

type Locker

type Locker interface {
	// Acquire obtains the named lock, retrying until the WithWait window is
	// exhausted (no waiting by default). It returns ErrNotAcquired when the
	// lock stays held by someone else, and fails closed on backend errors.
	Acquire(ctx context.Context, name string, opts ...Option) (Lock, error)
	// TryAcquire attempts a single non-blocking acquisition, returning
	// ErrNotAcquired immediately when the lock is held — the natural guard for
	// "only one replica runs this job" cron patterns.
	TryAcquire(ctx context.Context, name string, opts ...Option) (Lock, error)
}

Locker acquires named distributed locks. Locks are lease-based: every acquisition carries a TTL that auto-expires if the holder crashes, and only the holder (identified by a random ownership token) can release or extend its lease. Mutual exclusion is therefore cooperative — a process pause that outlives the TTL can let a second holder in, so guard state that must never be corrupted with Lock.FencingToken, idempotency, or database constraints.

func NewMemoryLocker

func NewMemoryLocker() Locker

NewMemoryLocker creates an empty in-process locker.

func NewRedisLocker

func NewRedisLocker(client *redis.Client) Locker

NewRedisLocker creates a Redis-backed locker.

type MemoryLocker

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

MemoryLocker implements Locker with in-process state. It provides NO cross-replica mutual exclusion — it suits single-instance deployments and tests; multi-node deployments must use RedisLocker. Semantics (TTL expiry, ownership tokens, fencing tokens, waiting) match RedisLocker exactly so behavior does not change between environments.

func (*MemoryLocker) Acquire

func (m *MemoryLocker) Acquire(ctx context.Context, name string, opts ...Option) (Lock, error)

func (*MemoryLocker) TryAcquire

func (m *MemoryLocker) TryAcquire(ctx context.Context, name string, opts ...Option) (Lock, error)

type Option

type Option func(*acquireConfig)

Option customizes a single acquisition.

func WithAutoRenew

func WithAutoRenew(enabled bool) Option

WithAutoRenew toggles the background watchdog that refreshes the lease at a third of its TTL, so a healthy holder never expires mid-work while a crashed one still frees the lock within one TTL. Auto-renewal requires a TTL of at least MinAutoRenewTTL. It is off by default for bare Acquire / TryAcquire; WithLock turns it on unless explicitly disabled.

func WithRetryInterval

func WithRetryInterval(interval time.Duration) Option

WithRetryInterval sets the polling cadence used while waiting for a held lock. Non-positive values fall back to DefaultRetryInterval.

func WithTTL

func WithTTL(ttl time.Duration) Option

WithTTL sets the lease duration. The lease auto-expires this long after the acquisition (or the last refresh), bounding how long a crashed holder can block others. Non-positive values fall back to DefaultTTL. When auto-renewal is enabled, values below MinAutoRenewTTL fail with ErrAutoRenewTTLTooShort.

func WithWait

func WithWait(wait time.Duration) Option

WithWait allows Acquire to keep retrying for up to wait before giving up with ErrNotAcquired. The default is no waiting: a held lock fails the acquisition immediately.

type RedisLocker

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

RedisLocker implements Locker on a single Redis instance with the atomic Lua acquisition + token-guarded release/refresh pattern, giving cross-replica mutual exclusion to every node sharing the Redis. It is not a Redlock implementation: quorum locking over independent Redis nodes is out of scope, and the lease-based caveats documented on Locker apply.

func (*RedisLocker) Acquire

func (r *RedisLocker) Acquire(ctx context.Context, name string, opts ...Option) (Lock, error)

func (*RedisLocker) TryAcquire

func (r *RedisLocker) TryAcquire(ctx context.Context, name string, opts ...Option) (Lock, error)

Jump to

Keyboard shortcuts

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