Redsync

repository·master·Indexed 26 days ago

https://github.com/go-redsync/redsync

A Go implementation of a Redis-based distributed mutual exclusion lock based on the Redlock algorithm. It allows multiple distributed processes to coordinate access to shared resources and provides driver implementations for both Redigo and Go-redis.

Tokens
1.9K
Snippets
2
Records
17
Agent score
86%

What's inside redsync

  1. Install Redsync v4

    master

    Install Redsync using the go get command. The package includes driver implementations for both Redigo and Go-redis, but only the driver you explicitly use in your code will be included in your final project binary.

    $ go get github.com/go-redsync/redsync/v4
  2. Use Redsync for distributed locking

    master

    Redsync provides a Redis-based distributed mutual exclusion lock implementation. To use it, you must create a pool using a Redis driver (like go-redis or redigo), initialize redsync.New(pool), and then create a mutex using rs.NewMutex(name). You can then call mutex.Lock() to acquire the lock and mutex.Unlock() to release it.

    package main
    
    import (
    	goredislib "github.com/redis/go-redis/v9"
    	"github.com/go-redsync/redsync/v4"
    	"github.com/go-redsync/redsync/v4/redis/goredis/v9"
    )
    
    func main() {
    	// Create a pool with go-redis (or redigo) which is the pool redsync will
    	// use while communicating with Redis. This can also be any pool that
    	// implements the `redis.Pool` interface.
    	client := goredislib.NewClient(&goredislib.Options{
    		Addr: "localhost:6379",
    	})
    	pool := goredis.NewPool(client) // or, pool := redigo.NewPool(...)
    
    	// Create an instance of redsync to be used to obtain a mutual exclusion
    	// lock.
    	rs := redsync.New(pool)
    
    	// Obtain a new mutex by using the same name for all instances wanting
    	// the same lock.
    	mutexname := "my-global-mutex"
    	mutex := rs.NewMutex(mutexname)
    
    	// Obtain a lock for our given mutex. After this is successful, no one else
    	// can obtain the same lock (the same mutex name) until we unlock it.
    	if err := mutex.Lock(); err != nil {
    		panic(err)
    	}
    
    	// Do your work that requires the lock.
    
    	// Release the lock so other processes or threads can obtain a lock.
    	if ok, err := mutex.Unlock(); !ok || err != nil {
    		panic("unlock failed")
    	}
    }
  3. Extend a lock's expiry with Mutex.Extend()

    master
    Use Extend() to reset the mutex's expiry time, effectively renewing the lock. It returns a boolean indicating if the extension was successful and an error if one occurred. For context-aware extension, use ExtendContext(ctx context.Context).
  4. Inspect Mutex state with Name(), Value(), and Until()

    master

    The Mutex type provides methods to inspect its current state:

    • Name(): Returns the mutex name (the Redis key).
    • Value(): Returns the current random value used for the lock. This is empty until the lock is acquired (unless WithValue was used during creation).
    • Until(): Returns the time.Time when the acquired lock is set to expire. Returns a zero value if the lock is not held.
  5. Check lock validity with Mutex.ValidContext()

    master

    Use ValidContext(ctx context.Context) to check if the lock acquired through the mutex is still valid.

    Note: The Valid() method is deprecated. Use Until() to check the expiration time instead.

  6. Release a distributed lock with Mutex.Unlock()

    master
    Use Unlock() to release the held lock. It returns a boolean indicating if the unlock was successful (i.e., reached quorum) and an error if one occurred. For context-aware unlocking, use UnlockContext(ctx context.Context).
  7. Attempt a single lock acquisition with Mutex.TryLock()

    master
    Use TryLock() to attempt to acquire the lock exactly once. It returns immediately regardless of whether the acquisition succeeded or failed, without any retries. For context-aware single attempts, use TryLockContext(ctx context.Context).
  8. Acquire a distributed lock with Mutex.Lock()

    master
    Use Lock() to acquire a distributed mutual exclusion lock. This method will retry to acquire the lock based on the configured number of tries. If it returns an error, you may attempt to call Lock() again. For context-aware locking, use LockContext(ctx context.Context).
  9. Handle expired lock errors with ErrLockAlreadyExpired

    master
    If you attempt to unlock a lock that has already expired, Redsync returns ErrLockAlreadyExpired. This indicates that the lock's TTL has passed and it is no longer valid for unlocking.
  10. Configure Mutex options

    master

    Customize mutex behavior using the following Option functions passed to NewMutex:

    • WithExpiry(expiry time.Duration): Sets the mutex expiry. Default is 8s.
    • WithTries(tries int): Sets the number of lock acquisition attempts. Default is 32.
    • WithRetryDelay(delay time.Duration): Sets a fixed delay between retries. Default is a random value between 50ms and 250ms.
    • WithRetryDelayFunc(delayFunc DelayFunc): Overrides the default delay behavior with a custom function.
    • WithDriftFactor(factor float64): Sets the clock drift factor. Default is 0.01.
    • WithTimeoutFactor(factor float64): Sets the timeout factor. Default is 0.05.
    • WithGenValueFunc(genValueFunc func() (string, error)): Sets a custom value generator for the lock.
    • WithValue(v string): Assigns a specific value to the lock, allowing ownership to be transferred or unlocked from elsewhere.
    • WithSetNXOnExtend(): Improves extension logic to attempt a SET NX if the key does not exist during an extension attempt. Useful for frequent Redis restarts.
    • WithFailFast(b bool): If true, the mutex will not wait for responses from all Redis servers if the quorum is already met. This reduces latency and avoids blocking if some Redis servers are slow.
    • WithShufflePools(b bool): If true, shuffles the Redis pools to reduce centralized access in concurrent scenarios.