redis-lock
A small, correct distributed lock for Go, built on Redis.

Use it when only one worker should act at a time: a cron that must run on a
single instance, a job processed exactly once, a record updated without a race.
Why not just SETNX + DEL?
The naive lock is unsafe:
- Worker A acquires the lock with a TTL.
- A's work runs longer than the TTL — the lock expires.
- Worker B acquires the now-free lock.
- A finishes and runs
DEL — deleting B's lock.
This library stores a unique token per lock and releases/extends with a Lua
script that acts only if the token still matches. A holder whose lock
expired can never delete or extend someone else's lock.
Install
go get github.com/YusufDrymz/redis-lock
Usage
locker := redislock.New(redisClient)
lock, err := locker.TryLock(ctx, "cron:report", 30*time.Second)
if errors.Is(err, redislock.ErrNotAcquired) {
return // someone else holds it
}
defer lock.Unlock(ctx)
// long job? renew while you work:
lock.Extend(ctx, 30*time.Second)
Lock blocks (with jittered retries) until the key is free or the context ends:
lock, err := locker.Lock(ctx, "cron:report", 30*time.Second)
Try it
The tests need no Redis — they run against an in-memory fake (miniredis), so
you can clone and run them with zero setup:
go test ./...
To run the example or use the lock for real you need a Redis instance. The
quickest way is Docker:
docker run --rm -p 6379:6379 redis
go run ./examples
API
| Method |
Description |
New(client, ...Option) |
Locker over a redis.UniversalClient |
TryLock(ctx, key, ttl) |
Acquire once, or ErrNotAcquired |
Lock(ctx, key, ttl) |
Block until acquired or ctx done |
(*Lock) Unlock(ctx) |
Release if still owned, else ErrNotHeld |
(*Lock) Extend(ctx, ttl) |
Renew TTL if still owned |
(*Lock) Token() |
The lock's unique token |
WithRetryInterval(d) tunes the blocking retry cadence (default 100ms).
Caveats
This is a single-instance lock and relies on Redis key expiry. It is correct for
the common "only one worker at a time" case. It is not a Redlock
multi-node consensus lock, and like all TTL locks it cannot by itself prevent a
paused process from acting after its lock expired — pair it with fencing tokens
if you need that guarantee.
🇹🇷 Türkçe
Go için küçük ve doğru bir Redis distributed lock. Sıfır gereksiz bağımlılık
(yalnızca go-redis).
Neden SETNX + DEL yetmez? İşin TTL'i aşarsa lock expire olur, başka bir
worker onu alır, sen DEL yapınca onun lock'unu silersin. Bu kütüphane her
lock'a benzersiz bir token verir; release/extend işlemini token hâlâ
eşleşiyorsa çalışan bir Lua script ile yapar. Süresi dolmuş bir sahip,
başkasının lock'unu asla silemez/uzatamaz.
Kurulum: go get github.com/YusufDrymz/redis-lock
locker := redislock.New(redisClient)
lock, err := locker.TryLock(ctx, "cron:report", 30*time.Second)
if errors.Is(err, redislock.ErrNotAcquired) { return }
defer lock.Unlock(ctx)
lock.Extend(ctx, 30*time.Second) // uzun iş için yenile
Not: Bu tek-instance bir lock'tur (Redlock değil). TTL tabanlı lock'ların doğası
gereği, duraklayan bir process'in lock süresi dolduktan sonra iş yapmasını tek
başına engelleyemez; bu garanti gerekiyorsa fencing token ile birlikte kullan.
License
MIT — see LICENSE.