redislock

Distributed Redis locking for Go, with fencing tokens to guard against stale writes after a lost lease.

Library
Go
vv0.10.0
1,767stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
46/100Fair
Development Activity28
Maintenance8
Community48
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
83/100Excellent
Architecture85
Code Quality88
Innovation78
Learning Curve80

redislock is a small Go library that implements distributed locking on top of Redis. It wraps a redis.Scripter-compatible client (single node, Sentinel, or Cluster) and exposes Obtain/ObtainMulti to acquire a lock across one or more keys, Refresh to extend its TTL, Release to give it up, and TTL to inspect remaining time. All of the check-and-set logic runs as embedded Lua scripts, so acquisition, refresh, and release are atomic on the Redis server rather than split across multiple round trips.

Beyond the basic lock/unlock cycle, redislock adds two mechanisms aimed at real distributed-systems failure modes: pluggable retry strategies (NoRetry, LinearBackoff, ExponentialBackoff, LimitRetry) for handling contention, and fencing tokens via Options.FenceKey — a strictly increasing counter that lets a protected resource reject writes from a lock holder that paused long enough to lose its lease without noticing. The library deliberately omits a built-in watchdog goroutine, leaving refresh cadence and failure handling (retry, cancel, alert) as an explicit application concern.

What You Get

  • Obtain/ObtainMulti for acquiring a lock on one or several Redis keys atomically
  • Lock.Refresh, Lock.Release, and Lock.TTL for managing an acquired lock’s lifecycle
  • Composable retry strategies (NoRetry, LinearBackoff, ExponentialBackoff, LimitRetry) for contended locks
  • Fencing tokens (Options.FenceKey / Lock.FenceToken) to reject stale writes from a lock holder that lost its lease
  • A minimal RedisClient interface so any go-redis client (standalone, Sentinel, Cluster) can back the locker

Common Use Cases

  • Ensuring only one replica of a scheduled job or worker pool runs a given task at a time
  • Guarding a critical section (e.g. a per-tenant migration) shared across multiple service instances
  • Extending a lock’s TTL with a periodic watchdog ticker while a long-running job executes
  • Stamping writes with a fencing token so a protected resource can refuse updates from a lock holder that lost and regained the lock unexpectedly

Under The Hood

Architecture The library is organized around two small files: redislock.go defines Client (a thin wrapper over a RedisClient/redis.Scripter) and Lock (the handle returned by Obtain), while retry.go defines the RetryStrategy interface and its implementations. Every stateful operation — obtain, refresh, release, TTL — is implemented as an embedded Lua script (obtain.lua, refresh.lua, release.lua, pttl.lua via go:embed) run through redis.Script, so the read-check-write sequence for each operation is atomic at the Redis server rather than split across round trips from the client. Retry orchestration is centralized in a single withRetry helper that derives a context deadline from the lock’s TTL when the caller’s context has none, so an unbounded retry strategy combined with a background context still cannot loop forever. The obtain.lua script additionally threads an optional fence key through KEYS so a Redis Cluster deployment can co-locate the fence counter with the lock keys via a shared hashtag. Nothing in the package spawns goroutines on the caller’s behalf (the README explicitly leaves refreshing/watchdog behavior to the application), which keeps the surface area and failure modes easy to reason about.

Tech Stack Written in Go (module targets go1.25) with a single runtime dependency on github.com/redis/go-redis/v9, plus its transitive xxhash and go.uber.org/atomic dependencies. Locking logic lives in four small Lua scripts embedded at compile time rather than shipped as separate assets. CI (GitHub Actions, using the maintainer’s shared bsm/misc composite actions) runs linting via golangci-lint (configured in .golangci.yml) and a test job against a live redis:8-alpine service container — there is no mocking layer standing in for Redis.

Code Quality Tests are plain Go testing (redislock_test.go, retry_test.go, over 700 lines combined) run against a real Redis instance rather than a fake, covering acquisition, custom tokens, fencing (including reentrant fencing), retry success/failure/context-deadline paths, concurrent obtain, refresh, and every release-failure variant (expired, not-owned, not-held). example_test.go supplies five runnable godoc examples (basic usage, retry, fencing, watchdog refresh, custom deadline) that double as documentation and as executable tests. Errors are explicit sentinel values (ErrNotObtained, ErrLockNotHeld) rather than swallowed, errcheck is enabled in the linter config with only two narrow, justified exclusions, and public API doc comments are consistently present. No obvious gaps in error handling or test coverage were found.

What Makes It Unique The fencing-token support is the standout: rather than only offering mutual exclusion, redislock implements Martin Kleppmann’s fencing-token pattern directly — minting a monotonically increasing counter per lock acquisition that the protected resource, not the lock library, is expected to check and reject stale writes against. Most Redis lock libraries stop at Redlock-style mutual exclusion and leave the GC-pause/clock-skew correctness gap unaddressed; this one documents the gap explicitly (including cluster-slot placement caveats for the fence key and monotonicity limits across Sentinel/Cluster failover) and gives callers the primitive to close it themselves.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search