Simplified distributed locking implementation using Redis

redislock

Build Status GoDoc Go Report Card License

Simplified distributed locking implementation using Redis. For more information, please see examples.

Examples

import (
  "fmt"
  "time"

  "github.com/bsm/redislock"
  "github.com/go-redis/redis/v8"
)

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!")
	}

}

Documentation

Full documentation is available on GoDoc

Owner
Comments
  • RedisClient error

    RedisClient error

    I try your example code but doesn't working.

    Go version: go1.19.1 windows/amd64 redislock version in go mod file: github.com/bsm/redislock v0.8.0 redis version in go mod file: github.com/go-redis/redis/v9 v9.0.0-beta.3

    I have error as below.

    redislock.go:149:31: cannot use l.client.client (variable of type RedisClient) as type redis.Scripter in argument to luaPTTL.Run: RedisClient does not implement redis.Scripter (missing EvalRO method) C:\Users\P1608\go\pkg\mod\github.com\bsm\[email protected]\redislock.go:166:37: cannot use l.client.client (variable of type RedisClient) as type redis.Scripter in argument to luaRefresh.Run: RedisClient does not implement redis.Scripter (missing EvalRO method) C:\Users\P1608\go\pkg\mod\github.com\bsm\[email protected]\redislock.go:178:34: cannot use l.client.client (variable of type RedisClient) as type redis.Scripter in argument to luaRelease.Run: RedisClient does not implement redis.Scripter (missing EvalRO method)

  • A question about ttl

    A question about ttl

    https://github.com/bsm/redislock/blob/97011e6af39ac0f8f2a2ee4d04cadc50a43d1f68/redislock.go#L64 https://github.com/bsm/redislock/blob/97011e6af39ac0f8f2a2ee4d04cadc50a43d1f68/redislock.go#L89-L91 Why is the deaaline here related to the value of ttl and not another option, if I want to keep blocking and waiting until the other party actively releases this lock, I find I have no way to implement this requirement.

  • Does not redislock work properly with multiple Go gin API at a time?

    Does not redislock work properly with multiple Go gin API at a time?

    Hi, I made an API that receive file

    func main() {
    	route := gin.Default()
    	route.POST("/upload",func(c *gin.Context) {
    	        //process file
    		clientL := redis.NewClient(&redis.Options{
    		                Network:  "tcp",
    		                Addr:     "localhost:6379",
    		                DB:       0,
    	                })
    	        defer clientL.Close()
    	        locker := redislock.New(clientL)
    	        ctx := context.Background()
    	        lock, err := locker.Obtain(ctx, "file", 5000*time.Millisecond, nil)
    	        if err != nil {
    		        fmt.Println(err)
    		        return
    	        }
    	        defer lock.Release(ctx)
                    fmt.Println("pass")
                    //return response
    	})
    	route.Run("0.0.0.0:8080")
    }
    

    Normally, I think when there are multiple requests at the same time want to upload a file. They must pause at the line I obtain the lock, if my lock is released or timeout, they will continue. It it doesn't, the request will fail. But when I make a client that sends 4 requests at the same time to my API, they can obtain the lock for all of them and it continues for next calls.

    Did I do something wrong or I misunderstand something in this concept, please help!

  • Allow setting/disabling Obtain deadline in Options

    Allow setting/disabling Obtain deadline in Options

    The current behavior is to simply use the ttl as the deadline, but you might want to try for a longer period than the ttl, possibly even indefinitely, I believe it should be supported to set the deadline via the options, including the ability to have no deadline, allowing to rely on the context cancellation/deadline instead.

    I had to workaround this in our code by not using the builtin RetryStrategy and implementing the loop by ourselves.

  • Uncompatible with new version.

    Uncompatible with new version.

    Thanks for this good repo. Now I am trying to update the version get this:

    cannot use redisClient (type *"github.com/go-redis/redis".Client) as type redislock.RedisClient in argument to redislock.New:
            *"github.com/go-redis/redis".Client does not implement redislock.RedisClient (wrong type for Eval method)
                    have Eval(string, []string, ...interface {}) *"github.com/go-redis/redis".Cmd
                    want Eval(string, []string, ...interface {}) *"github.com/go-redis/redis/v7".Cmd
    
  • Doesn't work with redis/v9

    Doesn't work with redis/v9

    When I have this code

    rc := GetRedisClient()
    locker  := redislock.New(rc)
    

    And my redis helper file, uses redis v9 like below..

    package main
    
    import (
      "github.com/ilyakaznacheev/cleanenv"
      "github.com/go-redis/redis/v9"
      "github.com/rs/zerolog/log"
    )
    
     ...
     ...
    
    func GetRedisClient() *redis.Client{
    
      if client == nil {
        //fmt.Println("Obtaining a connection to Redis...")
        log.Print("Obtaining connection to Redis...")
        client = redis.NewClient(&redis.Options{
          Addr: cfg.Addr,
          Password: cfg.Password,
          DB: cfg.DB,
        })
      } else {
        log.Print("Reusing connection to Redis...")
      }
      return client
    }
    
    

    Then, I get an error cmd/app/game-controller.go:85:27: cannot use rc (variable of type *"github.com/go-redis/redis/v9".Client) as type redislock.RedisClient in argument to redislock.New: *"github.com/go-redis/redis/v9".Client does not implement redislock.RedisClient (wrong type for Eval method) have Eval(ctx context.Context, script string, keys []string, args ...interface{}) *"github.com/go-redis/redis/v9".Cmd want Eval(ctx context.Context, script string, keys []string, args ...interface{}) *"github.com/go-redis/redis/v8".Cmd make: *** [makefile:8: build] Error 2

  • Global locker object or local

    Global locker object or local

    Hi,

    I have checked that having a global locker object or creating a new one at the point of acquiring a lock both works.

    However what is the best practice that you would suggest, create globally or at the point of acquiring the lock.

    Thanks. Mahesh S.

  • redis.Nil check for SetNX

    redis.Nil check for SetNX

    https://github.com/bsm/redislock/blob/b80b08c8989703908279cfb50782179de7fdd5f0/redislock.go#L98 Maybe I'm misunderstanding something, but why is the code checking redis.Nil for a call to SetNX ? I thought that it was returned only for Get. I don't see anything in the documentation about this.

  • redis/v8 support

    redis/v8 support

    I am following the example listed in the go doc but getting an error when passing the v8 redis client to redislock.New(client). It is saying it's missing ScriptExists(scripts ...string) *redis.BoolSliceCmd because the method in v8 has the context as the first param. Let me know if you are seeing the same thing and if you want me to make a PR

  • Please Follow Semantic Import Versioning

    Please Follow Semantic Import Versioning

    Thank you for this package. My company uses it to maintain distributed locks. We appreciate your contribution, but would more appreciate it if your feature updates were backwards compatible. Changing the signature of methods causes an issue when the major version is not bumped because Go does not expect it, and we get build and import errors. There's nothing wrong with moving to a v2 at some point.

  • add function which checks locks whether existed or not

    add function which checks locks whether existed or not

    Proposal

    Why do we need that? Currently, we do not have a way to find out whether locks existed in redis.

    The lock acquires immediately, if called function Obtain. However, sometimes we just wanna check the staff whether was locked.

  • How to get lock with a key

    How to get lock with a key

    This is not an issue, just a question. How do we get lock having just a key value. We need this option in the scenarios when we acquire a lock in a thread and try to release the same lock in the other thread. Thanks team,

A distributed locking library built on top of Cloud Spanner and TrueTime.

A distributed locking library built on top of Cloud Spanner and TrueTime.

Sep 13, 2022
MySQL Backed Locking Primitive

go-mysql-lock go-mysql-lock provides locking primitive based on MySQL's GET_LOCK Lock names are strings and MySQL enforces a maximum length on lock na

Dec 21, 2022
Distributed-Services - Distributed Systems with Golang to consequently build a fully-fletched distributed service

Distributed-Services This project is essentially a result of my attempt to under

Jun 1, 2022
JuiceFS is a distributed POSIX file system built on top of Redis and S3.
JuiceFS is a distributed POSIX file system built on top of Redis and S3.

JuiceFS is a high-performance POSIX file system released under GNU Affero General Public License v3.0. It is specially optimized for the cloud-native

Jan 4, 2023
Distributed disk storage database based on Raft and Redis protocol.
Distributed disk storage database based on Raft and Redis protocol.

IceFireDB Distributed disk storage system based on Raft and RESP protocol. High performance Distributed consistency Reliable LSM disk storage Cold and

Dec 31, 2022
Distributed lock manager. Warning: very hard to use it properly. Not because it's broken, but because distributed systems are hard. If in doubt, do not use this.

What Dlock is a distributed lock manager [1]. It is designed after flock utility but for multiple machines. When client disconnects, all his locks are

Dec 24, 2019
Distributed reliable key-value store for the most critical data of a distributed system

etcd Note: The main branch may be in an unstable or even broken state during development. For stable versions, see releases. etcd is a distributed rel

Dec 30, 2022
An implementation of a distributed access-control server that is based on Google Zanzibar

An implementation of a distributed access-control server that is based on Google Zanzibar - "Google's Consistent, Global Authorization System".

Dec 22, 2022
Golang implementation of distributed mutex on Azure lease blobs

Distributed Mutex on Azure Lease Blobs This package implements distributed lock available for multiple processes. Possible use-cases include exclusive

Jul 31, 2022
implementation of some distributed system techniques

Distributed Systems These applications were built with the objective of studding a distributed systems using the most recent technics. The main ideia

Feb 18, 2022
An implementation of a distributed KV store backed by Raft tolerant of node failures and network partitions 🚣
An implementation of a distributed KV store backed by Raft tolerant of node failures and network partitions 🚣

barge A simple implementation of a consistent, distributed Key:Value store which uses the Raft Concensus Algorithm. This project launches a cluster of

Nov 24, 2021
A distributed lock service in Go using etcd

locker A distributed lock service client for etcd. What? Why? A distributed lock service is somewhat self-explanatory. Locking (mutexes) as a service

Sep 27, 2022
Rink is a "distributed sticky ranked ring" using etcd.

Rink is a "distributed sticky ranked ring" using etcd. A rink provides role scheduling across distributed processes, with each role only assigned

Dec 5, 2022
A Distributed Content Licensing Framework (DCLF) using Hyperledger Fabric permissioned blockchain.

A Distributed Content Licensing Framework (DCLF) using Hyperledger Fabric permissioned blockchain.

Nov 4, 2022
distributed data sync with operational transformation/transforms

DOT The DOT project is a blend of operational transformation, CmRDT, persistent/immutable datastructures and reactive stream processing. This is an im

Dec 16, 2022
High performance, distributed and low latency publish-subscribe platform.
High performance, distributed and low latency publish-subscribe platform.

Emitter: Distributed Publish-Subscribe Platform Emitter is a distributed, scalable and fault-tolerant publish-subscribe platform built with MQTT proto

Jan 2, 2023
Fast, efficient, and scalable distributed map/reduce system, DAG execution, in memory or on disk, written in pure Go, runs standalone or distributedly.

Gleam Gleam is a high performance and efficient distributed execution system, and also simple, generic, flexible and easy to customize. Gleam is built

Jan 1, 2023
Go Micro is a framework for distributed systems development

Go Micro Go Micro is a framework for distributed systems development. Overview Go Micro provides the core requirements for distributed systems develop

Jan 8, 2023