distlock

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 19, 2021 License: Apache-2.0 Imports: 12 Imported by: 0

README

distlock-go

Transferable distributed lock

Documentation

Overview

Example
// Connect to redis.
client := redis.NewClient(&redis.Options{
	Network: "tcp",
	Addr:    "127.0.0.1:6379",
})
defer client.Close()

// Create a new lock client.
ctx := context.Background()

// Try to obtain lock.
lock, err := distlock.Obtain(ctx, client, "my-key", 100*time.Millisecond)
if err == distlock.ErrNotObtained {
	fmt.Println("Could not obtain lock!")
} else if err != nil {
	log.Fatalln(err)
}

// Don't forget to defer Release.
defer distlock.Release(ctx, client, lock)
fmt.Println("I have a lock!")

// Sleep and check the remaining TTL.
time.Sleep(50 * time.Millisecond)
if ttl, err := distlock.TTL(ctx, client, lock); err != nil {
	log.Fatalln(err)
} else if ttl > 0 {
	fmt.Println("Yay, I still have my lock!")
}

// Extend my lock.
if err := distlock.Refresh(ctx, client, lock, 100*time.Millisecond); err != nil {
	log.Fatalln(err)
}

// Sleep a little longer, then check.
time.Sleep(100 * time.Millisecond)
if ttl, err := distlock.TTL(ctx, client, lock); err != nil {
	log.Fatalln(err)
} else if ttl == 0 {
	fmt.Println("Now, my lock has expired!")
}
Output:

I have a lock!
Yay, I still have my lock!
Now, my lock has expired!

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrNotObtained is returned when a lock cannot be obtained.
	ErrNotObtained = errors.New("distlock: not obtained")
	// ErrLockNotHeld is returned when trying to release an inactive lock.
	ErrLockNotHeld = errors.New("distlock: lock not held")
)

Functions

func Ensure added in v0.2.0

func Ensure(ctx context.Context, cli RedisClient, lock Lock, ttl time.Duration) error

Ensure ttl greater than

func Refresh

func Refresh(ctx context.Context, cli RedisClient, lock Lock, ttl time.Duration) error

Refresh extends the lock with a new TTL. May return ErrNotObtained if refresh is unsuccessful.

func Release

func Release(
	ctx context.Context, cli RedisClient, lock Lock) error

Release manually releases the lock. May return ErrLockNotHeld.

func TTL

func TTL(ctx context.Context, cli RedisClient, lock Lock) (time.Duration, error)

TTL returns the remaining time-to-live. Returns 0 if the lock has expired.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

func New

func New(client RedisClient) *Client

func (*Client) Ensure added in v0.2.0

func (c *Client) Ensure(ctx context.Context, lock Lock, ttl time.Duration) error

func (*Client) Obtain

func (c *Client) Obtain(ctx context.Context, key string, ttl time.Duration, opts ...Option) (Lock, error)
Example (CustomDeadline)
client := redis.NewClient(&redis.Options{Network: "tcp", Addr: "127.0.0.1:6379"})
defer client.Close()

// Retry every 500ms, for up-to a minute
backoff := distlock.LinearBackoff(500 * time.Millisecond)
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Minute))
defer cancel()

// Obtain lock with retry + custom deadline
lock, err := distlock.Obtain(ctx, client, "my-key", time.Second, distlock.WithRetryStrategy(backoff))
if err == distlock.ErrNotObtained {
	fmt.Println("Could not obtain lock!")
} else if err != nil {
	log.Fatalln(err)
}
defer distlock.Release(ctx, client, lock)

fmt.Println("I have a lock!")
Output:

Example (Retry)
client := redis.NewClient(&redis.Options{Network: "tcp", Addr: "127.0.0.1:6379"})
defer client.Close()

// Retry every 100ms, for up-to 3x
backoff := distlock.LimitRetry(distlock.LinearBackoff(100*time.Millisecond), 3)

ctx := context.Background()

// Obtain lock with retry
lock, err := distlock.Obtain(ctx, client, "my-key", time.Second, distlock.WithRetryStrategy(backoff))
if err == distlock.ErrNotObtained {
	fmt.Println("Could not obtain lock!")
} else if err != nil {
	log.Fatalln(err)
}
defer distlock.Release(ctx, client, lock)

fmt.Println("I have a lock!")
Output:

func (*Client) Refresh

func (c *Client) Refresh(ctx context.Context, lock Lock, ttl time.Duration) error

func (*Client) Release

func (c *Client) Release(ctx context.Context, lock Lock) error

func (*Client) TTL

func (c *Client) TTL(ctx context.Context, lock Lock) (time.Duration, error)

type Lock

type Lock string

func NewLock added in v0.2.0

func NewLock(key, token string) Lock

func Obtain

func Obtain(ctx context.Context, cli RedisClient, key string, ttl time.Duration, opts ...Option) (Lock, error)

Obtain tries to obtain a new lock using a key with the given TTL. May return ErrNotObtained if not successful.

func (Lock) GetKey

func (lock Lock) GetKey() string

func (Lock) GetToken added in v0.2.0

func (lock Lock) GetToken() string

func (Lock) Refresh added in v0.2.0

func (lock Lock) Refresh(
	ctx context.Context, cli RedisClient, ttl time.Duration) error

func (Lock) Release added in v0.2.0

func (lock Lock) Release(
	ctx context.Context, cli RedisClient) error

func (Lock) TTL added in v0.2.0

func (lock Lock) TTL(
	ctx context.Context, cli RedisClient) (time.Duration, error)

type Option

type Option interface {
	// contains filtered or unexported methods
}

func WithRetryStrategy

func WithRetryStrategy(strategy RetryStrategy) Option

type RedisClient

type RedisClient interface {
	SetNX(ctx context.Context, key string, value interface{}, expiration time.Duration) *redis.BoolCmd
	EvalSha(ctx context.Context, sha1 string, keys []string, args ...interface{}) *redis.Cmd
	ScriptLoad(ctx context.Context, script string) *redis.StringCmd
}

Client is a minimal client interface.

type RetryStrategy

type RetryStrategy interface {
	// NextBackoff returns the next backoff duration.
	NextBackoff() time.Duration
}

RetryStrategy allows to customise the lock retry strategy.

func ExponentialBackoff

func ExponentialBackoff(min, max time.Duration) RetryStrategy

ExponentialBackoff strategy is an optimization strategy with a retry time of 2**n milliseconds (n means number of times). You can set a minimum and maximum value, the recommended minimum value is not less than 16ms.

func LimitRetry

func LimitRetry(s RetryStrategy, max int) RetryStrategy

LimitRetry limits the number of retries to max attempts.

func LinearBackoff

func LinearBackoff(backoff time.Duration) RetryStrategy

LinearBackoff allows retries regularly with customized intervals

func NoRetry

func NoRetry() RetryStrategy

NoRetry acquire the lock only once.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL