lock

package
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Sep 19, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package lock builds a config-selected core/interfaces.Locker — a DistributedLock (keyed, token-fenced, best-effort) plus Hold, the managed acquire-with-watchdog helper the single-active-query guard (Epic 6) depends on.

NewFromConfig chooses a backend from a Kind string — local (in-process, dev / replica=1) or redis (cross-pod, staging/prod) — mirroring the foundation/logger.NewFromConfig and clients/cache factory pattern, and wraps it in a Locker (NewLocker). The redis backend reuses the live go-redis client from the cache (clients/cache/redis.Cache.Client), so no second connection pool is opened.

Hold acquires a key and keeps its lease alive with a background watchdog; the context it returns is cancelled the instant the lease is lost, so a losing pod's in-flight query is stopped rather than left streaming under a lock it no longer owns. See ConversationLockKey for the ws:conv:{id}:lock convention.

Cross-cutting concerns are composed by the decorators subpackage (resilience: retry, circuit-breaker, timeout; observability: logging, metrics, tracing), wired by NewFromConfig from the Config collaborators.

Scope boundary: this package ships the interfaces, both backends, the factory, the decorator stack, the generated mocks, and Hold. The composition-root provider (config + shared redis client + observability/resilience deps → Locker) and the query-workflow call site are out of scope here; no provider reads lock config yet.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ConversationLockKey

func ConversationLockKey(conversationID string) string

ConversationLockKey renders the Redis key that guards a conversation's single active query: ws:conv:{conversationID}:lock. The caller passes conversationID.

func NewFromConfig

func NewFromConfig(
	cfg *Config,
	client *goredis.Client,
) (interfaces.Locker, error)

NewFromConfig builds the configured Locker: it selects a DistributedLock backend by Kind, wraps it in the resilience + observability decorator stack (from the Config collaborators — each optional/nil-safe), and returns a Locker whose Hold renews on RenewInterval over the decorated backend. The client is required only for KindRedis (the shared go-redis client reused from the cache); it is ignored for KindLocal. It fails loudly on an unknown kind, a non-positive TTL or RenewInterval, a RenewInterval that exceeds TTL/3, or a redis kind with no client.

func NewLocker

func NewLocker(
	lock interfaces.DistributedLock,
	renewInterval time.Duration,
) interfaces.Locker

NewLocker wraps a DistributedLock with the renewal cadence Hold uses, yielding a Locker. Exposed so callers (and tests) can compose Hold over any DistributedLock — e.g. a mock or a backend built directly; NewFromConfig builds one over the config-selected backend.

Types

type Config

type Config struct {
	// Logger drives the logging decorator; nil skips it.
	Logger interfaces.Logger

	// Metrics drives the metrics decorator; nil skips it.
	Metrics interfaces.Metrics

	// Tracer drives the tracing decorator; nil skips it.
	Tracer interfaces.Tracer

	// Retrier drives the retry decorator (transient-error retries); nil skips it.
	Retrier interfaces.Retrier

	// CircuitBreaker drives the circuit-breaker decorator; nil skips it.
	CircuitBreaker interfaces.CircuitBreaker

	// Name labels the lock in metrics, spans, and logs (default "lock").
	Name string

	// Kind selects the backend (local | redis).
	Kind Kind

	// TTL is the lease duration applied on Acquire and extended by Renew.
	TTL time.Duration

	// RenewInterval is the watchdog's renew cadence in Hold.
	RenewInterval time.Duration

	// OpTimeout bounds each backend operation via the timeout decorator; zero
	// skips it.
	OpTimeout time.Duration
}

Config selects and tunes the lock. Kind picks the backend; TTL is the lease duration a holder gets on Acquire (and Renew extends); RenewInterval is how often Hold's watchdog renews. RenewInterval must be ≤ TTL/3 so a couple of missed renew ticks cannot expire a live lease.

type Kind

type Kind int

Kind selects which DistributedLock backend NewFromConfig builds.

const (
	// KindLocal is the in-process backend: correct for a single replica (dev /
	// replica=1), no external dependency.
	KindLocal Kind = iota

	// KindRedis is the cross-pod backend over a shared Redis client
	// (staging/prod, multi-replica).
	KindRedis
)

func (Kind) String

func (k Kind) String() string

String returns the config token for the kind.

Directories

Path Synopsis
Package decorators composes cross-cutting concerns — resilience (retry, circuit-breaker, timeout) and observability (logging, metrics, tracing) — around any core/interfaces.DistributedLock, without touching the backend.
Package decorators composes cross-cutting concerns — resilience (retry, circuit-breaker, timeout) and observability (logging, metrics, tracing) — around any core/interfaces.DistributedLock, without touching the backend.
Package local provides an in-process DistributedLock backend for single-pod deployments (dev / replica=1), where cross-process coordination is unnecessary.
Package local provides an in-process DistributedLock backend for single-pod deployments (dev / replica=1), where cross-process coordination is unnecessary.
Package redis provides a cross-pod DistributedLock backend over Redis, for multi-replica deployments (staging/prod).
Package redis provides a cross-pod DistributedLock backend over Redis, for multi-replica deployments (staging/prod).

Jump to

Keyboard shortcuts

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