Documentation
¶
Overview ¶
Package redis implements distributedlock.Locker with a single Redis key per lock.
What it guarantees ¶
Acquire is a SETNX of a prefixed key to a freshly generated ownership token, with the TTL as the key's expiry — so the TTL is enforced by Redis rather than by the holder, and a process that dies mid-hold releases the lock when the key expires rather than when somebody notices.
Release and Refresh are Lua scripts that compare the token before acting, which is what stops one caller releasing or extending a lock that has already expired and been taken by another. Either reports distributedlock.ErrLockNotHeld when the token no longer matches. A held lock whose TTL lapses is indistinguishable, from inside, from one still held: the handle's TTL is what was asked for, not what remains, so work that may outlive its TTL has to Refresh.
What it does not guarantee ¶
Exclusivity rests on that one key, on one Redis. This is not Redlock — nothing here takes a quorum across independent nodes — so the lock is only as durable as the key. Redis replication is asynchronous, so a failover that promotes a replica which has not yet received the SET can hand the same lock to a second caller, and neither caller is told. Where two simultaneous holders would be a correctness failure rather than a wasted duplicate, the lock is not the last line of defense; the work underneath it also needs to be idempotent, or fenced by something the database enforces.
Contention and unavailability are different answers. A key already held reports ErrLockNotAcquired and counts as a healthy round trip — the backend answered. A Redis that cannot be reached trips the circuit breaker, and calls made while it is open report circuitbreaking.ErrCircuitBroken rather than a failure to acquire, so a caller cannot read an outage as contention.
There is no waiting. Acquire either takes the lock or returns immediately; a caller that wants to queue composes retry itself, or uses the parent package's ScopedLocker, which does that polling for it.
Keys are prefixed (default "lock:"), so what is written to Redis is not the key the caller named. Spans carry both.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
Username string `env:"USERNAME" json:"username,omitempty" yaml:"username,omitempty"`
Password string `env:"PASSWORD" json:"password,omitempty" yaml:"password,omitempty"`
KeyPrefix string `env:"KEY_PREFIX" envDefault:"lock:" json:"keyPrefix,omitempty" yaml:"keyPrefix,omitempty"`
Addresses []string `env:"ADDRESSES" json:"addresses,omitempty" yaml:"addresses,omitempty"`
}
Config configures a Redis-backed distributed locker.
type Locker ¶
type Locker struct {
// contains filtered or unexported fields
}
func NewRedisLocker ¶
func NewRedisLocker( cfg *Config, cb circuitbreaking.CircuitBreaker, opts ...Option, ) (*Locker, error)
NewRedisLocker constructs a new Redis-backed distributedlock.Locker.
type Option ¶
type Option func(*options)
Option configures the redis-backed Locker this package constructs. The zero configuration works: an absent logger logs nowhere, an absent tracer provider traces nowhere, and an absent metrics provider records nothing.
func WithMetricsProvider ¶
WithMetricsProvider attaches a metrics provider for the locker's counters and latency histogram.
func WithTracerProvider ¶
WithTracerProvider attaches a tracer provider, enabling spans on every lock operation.