Simplified distributed locking implementation using Redis

Overview

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

Comments
  • 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.

    opened by parchk 5
  • 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!

    opened by iamatsundere 4
  • 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.

    opened by segevfiner 4
  • 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
    
    opened by xiekeyi98 4
  • 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.

    opened by smaheshs 3
  • 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.

    opened by pierrre 3
  • 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

    opened by rv-nath 2
  • 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

    opened by jose-zenledger 2
  • 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.

    opened by swtch1 2
  • 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.

    opened by Betula-L 2
  • Support go-redis V7.2.0

    Support go-redis V7.2.0

    Hi there,

    Since go-redis/redis v7.2.0 is stable now, using the latest go-redis with redislock will lead to error:

    Cannot use 'client' (type *redis.Client) as type RedisClient Type does not implement 'RedisClient' need method: ScriptExists(scripts ...string) *redis.BoolSliceCmd have method: ScriptExists(hashes ...string) *BoolSliceCmd

    Is there any plan to support this version?

    Thanks.

    opened by wantdrink 2
  • 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,

    opened by tshubham19 1
Owner
Black Square Media
Black Square Media
A distributed locking library built on top of Cloud Spanner and TrueTime.

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

null 47 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

Sanket Patel 48 Sep 14, 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

Hamza Yusuff 6 Jun 1, 2022
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

Juicedata, Inc 6.7k Sep 30, 2022
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

IceFireDB 921 Sep 24, 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

Sergey Shepelev 25 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

etcd-io 41.3k Sep 24, 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".

authorizer.tech 63 Sep 16, 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

YouScan 11 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

Rafael A. C 6 Feb 18, 2022
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

Shehjad Khan 0 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

James Gregory 48 Dec 2, 2021
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

Luno 7 Sep 9, 2022
A Distributed Content Licensing Framework (DCLF) using Hyperledger Fabric permissioned blockchain.

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

Tal Derei 3 May 3, 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

DOT & Chain 72 Aug 14, 2022
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

emitter 3.4k Sep 22, 2022
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

Chris Lu 3.1k Sep 21, 2022
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

Asim Aslam 19.1k Sep 21, 2022