lock

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Nov 7, 2026 License: Apache-2.0 Imports: 6 Imported by: 0

Documentation

Overview

Package lock is mutual exclusion across processes: "only one replica runs this at a time".

Every correct distributed lock has four properties, and the naive implementation (SETNX, then DEL) has two of them. This one has all four:

  1. A TTL, so a crashed holder does not hold the lock forever.
  2. A unique token, so release only releases YOUR lock. Without it, a holder whose TTL expired mid-work deletes the lock a different replica has since acquired, and then two replicas are inside the critical section.
  3. A compare-and-delete release, done in Lua, because checking the token and deleting it as two commands is the same race one layer down.
  4. Optional refresh, for work that may outlive the TTL.

What it is NOT: a correctness guarantee against a Redis failover. If the primary dies after granting a lock and before replicating it, the replica can grant it again. Use this to prevent duplicate WORK, not to protect an invariant — for invariants, use a database transaction with a row lock.

Index

Constants

This section is empty.

Variables

View Source
var ErrNotAcquired = errors.New("lock: already held")

ErrNotAcquired is returned when the lock is held by somebody else.

View Source
var ErrNotHeld = errors.New("lock: no longer held")

ErrNotHeld is returned when releasing or refreshing a lock this process no longer owns — which means its TTL expired while the work was still running. Treat it as a signal that the TTL is too short, not as a nuisance.

Functions

func Do

func Do(ctx context.Context, l Locker, key string, ttl time.Duration, fn func(ctx context.Context) error) (ran bool, err error)

Do runs fn while holding key, and does nothing when the lock is held elsewhere.

The common use: a cron job running on three replicas where exactly one should do the work. ran is false when another replica had it.

Types

type Lock

type Lock struct {
	Key   string
	Token string
	// contains filtered or unexported fields
}

Lock is one acquired lock.

func (*Lock) Refresh

func (l *Lock) Refresh(ctx context.Context, ttl time.Duration) error

Refresh extends the TTL. Call it periodically from long work, and stop the work if it ever returns ErrNotHeld — at that point another replica may already be running it.

func (*Lock) Release

func (l *Lock) Release(ctx context.Context) error

Release gives the lock up. It is safe to call on an expired lock; that returns ErrNotHeld and deletes nothing.

type Locker

type Locker interface {
	// Acquire takes the lock, or returns ErrNotAcquired immediately.
	Acquire(ctx context.Context, key string, ttl time.Duration) (*Lock, error)
}

Locker acquires locks.

type Memory

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

Memory is a single-process Locker.

Correct for one instance and for tests. Across replicas it enforces nothing — every replica has its own map — so do not reach for it to "get started" on work that will later run on more than one.

func NewMemory

func NewMemory() *Memory

NewMemory returns an in-process locker.

func (*Memory) Acquire

func (m *Memory) Acquire(_ context.Context, key string, ttl time.Duration) (*Lock, error)

Acquire implements Locker.

type Noop

type Noop struct{}

Noop grants every lock. Use it to run a scheduler on exactly one replica by deployment rather than by coordination.

func (Noop) Acquire

func (Noop) Acquire(context.Context, string, time.Duration) (*Lock, error)

Acquire implements Locker.

type RedisLocker

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

RedisLocker is a Redis-backed Locker.

func NewRedis

func NewRedis(client redis.UniversalClient, prefix string) *RedisLocker

NewRedis builds a locker. prefix namespaces the keys; set it to the service name so two services cannot take each other's locks.

func (*RedisLocker) Acquire

func (r *RedisLocker) Acquire(ctx context.Context, key string, ttl time.Duration) (*Lock, error)

Acquire implements Locker.

Jump to

Keyboard shortcuts

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