Simplified distributed locking implementation using Redis. For more information, please see examples.
import (
"context"
"fmt"
"log"
"time"
"github.com/bsm/redislock"
"github.com/redis/go-redis/v9"
)
func main() {
// Connect to redis.
client := redis.NewClient(&redis.Options{
Network: "tcp",
Addr: "127.0.0.1:6379",
})
defer client.Close()
// Create a new lock client.
locker := redislock.New(client)
ctx := context.Background()
// Try to obtain lock.
lock, err := locker.Obtain(ctx, "my-key", 100*time.Millisecond, nil)
if err == redislock.ErrNotObtained {
fmt.Println("Could not obtain lock!")
} else if err != nil {
log.Fatalln(err)
}
// Don't forget to defer Release.
defer lock.Release(ctx)
fmt.Println("I have a lock!")
// Sleep and check the remaining TTL.
time.Sleep(50 * time.Millisecond)
if ttl, err := lock.TTL(ctx); err != nil {
log.Fatalln(err)
} else if ttl > 0 {
fmt.Println("Yay, I still have my lock!")
}
// Extend my lock.
if err := lock.Refresh(ctx, 100*time.Millisecond, nil); err != nil {
log.Fatalln(err)
}
// Sleep a little longer, then check.
time.Sleep(100 * time.Millisecond)
if ttl, err := lock.TTL(ctx); err != nil {
log.Fatalln(err)
} else if ttl == 0 {
fmt.Println("Now, my lock has expired!")
}
}If you want a long lock, you can use watchdog to refresh in background atomatically. Set an interval for watchdog shorter than TTL, it would refresh the lock before expiration, therefore your lock won't be released until you release it explicitly.
lock, err := locker.Obtain(ctx, "my-key", 100*time.Millisecond, &redislock.Options{
Watchdog: redislock.NewTickWatchdog(50*time.Millisecond),
})Sometimes you need statistics for monitoring, telemetry, debugging, etc.
stats := redislock.GetStats()If you want prometheus metrics, see redislock-prometheus.
Full documentation is available on GoDoc
