Documentation
¶
Overview ¶
Package memory implements distributedlock.Locker over a map and a mutex.
It is not distributed ¶
The name of the parent package is about the interface, not about this implementation. Mutual exclusion here extends exactly as far as one *Locker value: two replicas each hold their own map, so both acquire the same key at the same moment and neither learns of the other. Nothing detects that, and nothing reports it — the second holder's Acquire succeeds, and whatever the lock was protecting runs twice.
Two Lockers built in the same process do not exclude each other either, for the same reason. The lock's scope is the value, not the key.
So it is the right choice for tests, for a single-replica deployment, and as a readable statement of the semantics the other providers implement. It is the wrong choice anywhere the answer to "what happens if this runs twice" is not "nothing" — a scheduled job, a leader election, an exactly-once batch. Those want the redis or postgres provider, where the state lives outside any one process.
Semantics ¶
TTLs are enforced by this process's clock, and lazily: an expired key is reclaimed when it is acquired again, and by an opportunistic sweep on each Acquire. Release and Refresh check the ownership token as well as the expiry, so a caller cannot release a key whose lock has already lapsed and been taken by someone else — that reports distributedlock.ErrLockNotHeld.
Nothing here fails from unavailability: Ping always succeeds, there is no circuit breaker, and no network call can time out mid-hold. Close drops every held lock at once, after which outstanding handles report ErrLockNotHeld.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Locker ¶
type Locker struct {
// contains filtered or unexported fields
}
Locker is a single-process distributedlock.Locker. It uses a sync.Mutex over an in-memory map and lazy expiration on each Acquire — there is no background goroutine. It is intended for tests, single-replica deployments, and as a clear reference implementation of the lock semantics.
func (*Locker) Acquire ¶
func (l *Locker) Acquire(ctx context.Context, key string, ttl time.Duration) (distributedlock.Lock, error)
Acquire implements distributedlock.Locker.
type Option ¶
type Option func(*options)
Option configures the in-memory Locker this package constructs. The zero configuration works: an absent logger logs nowhere, an absent tracer provider traces nowhere, an absent metrics provider records nothing, and an absent clock reads the wall clock.
func WithClock ¶
WithClock swaps the source of time this Locker makes its TTL decisions against. An absent clock reads the wall clock.
Expiry is the whole of this backend's semantics — whether a key is still held, whether a release still owns it, what a refresh extends to — and all of it ran off time.Now(), so a test for any of it had to sleep through a real TTL. The siblings that talk to Redis and Postgres delegate expiry to the server; this one implements it, which is exactly why it is the one that needs a clock.
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.