Skip to content

flowerinthenight/dlock

Repository files navigation

main

dlock is a package for distributed locks. At the moment, available implementations are Kubernetes using the LeaseLock resource, spindle, and Redlock via redsync.

A simple Locker interface is also provided. All lock objects in this package implement this interface.

Usage

LeaseLock

The simplest usage form looks something like:

lock := dlock.NewK8sLock("unique-id", "lock-name")
lock.Lock(context.TODO())
...
lock.Unlock

A sample code is provided for reference. A deployment file is also provided (only tested on GKE). It will deploy two pods that will both try to grab the same lock.

# Deploy to k8s:
$ kubectl create -f k8slock.yaml

# See logs (not the full logs):
$ stern k8slock
main.go:53] [10.28.0.225] attempt to grab lock for a minute...
main.go:53] [10.28.4.52] attempt to grab lock for a minute...
main.go:47] [10.28.4.52] lock acquired by 10.28.4.52
main.go:57] [10.28.4.52] got the lock within that minute!
main.go:64] [10.28.4.52] now, let's attempt to grab the lock until termination
main.go:47] [10.28.4.52] lock acquired by 10.28.4.52
main.go:61] [10.28.0.225] we didn't get the lock within that minute
main.go:64] [10.28.0.225] now, let's attempt to grab the lock until termination
main.go:73] [10.28.0.225] stopping...
main.go:78] [10.28.0.225] we didn't get the lock in the end
main.go:73] [10.28.4.52] stopping...
main.go:75] [10.28.4.52] got the lock in the end

# Cleanup:
$ kubectl delete -f k8slock.yaml

spindle

This implementation is a wrapper to the spindle distributed locking library by providing a blocking Lock(...) / Unlock() function pair. This is probably useful if you are already using Cloud Spanner.

The basic usage will look something like:

ctx := context.Background()
db, _ := spanner.NewClient(ctx, "your/database")
defer db.Close()

lock := dlock.NewSpindleLock(&dlock.SpindleLockOptions{
  Client:   db,
  Table:    "testlease",
  Name:     "dlock",
  Duration: 1000,
})

start := time.Now()
lock.Lock(ctx)
log.Printf("lock acquired after %v, do protected work...", time.Since(start))
time.Sleep(time.Second * 5)
lock.Unlock()

Redis

The Redis implementation is basically a wrapper to the brilliant redsync package, with additional utility functions for working with Redis connection pools. It's also implemented in a way to follow the Locker interface.

To use with a single Redis host, no password, use defaults:

lock := dlock.NewRedisLock("testredislock", dlock.WithHost("1.2.3.4"))
lock.Lock(context.Background())
...
lock.Unlock()

To use with a single Redis host, with password, use defaults:

pool := dlock.NewRedisPool("1.2.3.4", dlock.WithPassword("secret-pass"))
lock := dlock.NewRedisLock("testredislock", dlock.WithPools([]*redis.Pool{pool}))
lock.Lock(context.Background())
...
lock.Unlock()

You can check out the test file and the sample code for usage reference. You can run the sample code in separate terminals simultaneously and one process should be able to grab the Redis lock.


Todo

PR's are welcome.

  • LeaseLock (k8s)
  • spindle
  • etcd
  • Zookeeper
  • Consul
  • Redis
  • Add CI