dblock

package module
v0.0.0-...-5b850e9 Latest Latest
Warning

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

Go to latest
Published: Aug 3, 2023 License: MIT Imports: 7 Imported by: 0

README

dblock

distributed lock based on rdbms, redis, etc.

package main

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"log"
	"time"

	"github.com/bingoohuang/dblock"
	"github.com/bingoohuang/dblock/rdblock"
	_ "github.com/go-sql-driver/mysql"
)

func main() {
	// db based
	db, err := sql.Open("mysql",
		"root:root@(127.0.0.1:3306)/mdb?charset=utf8mb4&parseTime=true&loc=Local")
	if err != nil {
		log.Panic(err)
	}
	defer db.Close()
	locker := rdblock.New(db)

	// // or redis based
	// client := redis.NewClient(&redis.Options{
	// 	Network: "tcp",
	// 	Addr:    "127.0.0.1:6379",
	// })
	// defer client.Close()
	// locker = redislock.New(client)

	ctx := context.Background()

	// Try to obtain lock.
	lock, err := locker.Obtain(ctx, "my-key", 100*time.Millisecond)
	if errors.Is(err, dblock.ErrNotObtained) {
		fmt.Println("Could not obtain lock!")
	} else if err != nil {
		log.Panicln(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.Panicln(err)
	} else if ttl > 0 {
		fmt.Println("Yay, I still have my lock!")
	}

	// extend my lock.
	if err := lock.Refresh(ctx, 100*time.Millisecond); err != nil {
		log.Panicln(err)
	}

	// Sleep a little longer, then check.
	time.Sleep(100 * time.Millisecond)
	if ttl, err := lock.TTL(ctx); err != nil {
		log.Panicln(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!
}

cli

install go install github.com/bingoohuang/dblock/...@latest

db 锁

# 第一次以 key = abc 加锁,成功(默认有效期1小时)
$ dblock -uri mysql://root:root@localhost:3306/mysql -key abc
2023/08/02 23:15:05 obtained, token: ssddGH1_k1__PFD71dawTw, meta: 
2023/08/02 23:15:05 ttl 59m59.994631s

# 再次以 key = abc 加锁,失败 
$ dblock -uri mysql://root:root@localhost:3306/mysql -key abc
2023/08/02 23:15:07 obtained failed: dblock: not obtained

# 以 key = abc 以及 对应的 token 加锁,成功
$ dblock -uri mysql://root:root@localhost:3306/mysql -key abc -token ssddGH1_k1__PFD71dawTw
2023/08/02 23:15:22 obtained, token: ssddGH1_k1__PFD71dawTw, meta: 
2023/08/02 23:15:22 ttl 59m59.994631s

# 释放 key = abc 锁
$ dblock -uri mysql://root:root@localhost:3306/mysql -key abc -token ssddGH1_k1__PFD71dawTw -release
2023/08/02 23:15:26 obtained, token: ssddGH1_k1__PFD71dawTw, meta: 
2023/08/02 23:15:26 ttl 59m59.99436s
2023/08/02 23:15:26 release successfully

# 释放后再次获取 key = abc 锁,成功
$ dblock -uri mysql://root:root@localhost:3306/mysql -key abc                                       
2023/08/02 23:15:30 obtained, token: eF87Wg8N_FVLJvkV7RfEPw, meta: 
2023/08/02 23:15:30 ttl 59m59.994559s

redis 锁

# 首次加锁 ( key = abc ) 成功
$ dblock -uri redis://localhost:6379 -key abc
2023/08/02 23:20:13 obtained, token: dGjS1xUq1RCAbsGPBbP66w, meta: 
2023/08/02 23:20:13 ttl 59m59.998s

# 再次加锁 ( key = abc ) 失败
$ dblock -uri redis://localhost:6379 -key abc
2023/08/02 23:20:14 obtained failed: dblock: not obtained

# 以指定相同的 token 加锁,成功
$ dblock -uri redis://localhost:6379 -key abc -token dGjS1xUq1RCAbsGPBbP66w
2023/08/02 23:20:26 obtained, token: dGjS1xUq1RCAbsGPBbP66w, meta: 
2023/08/02 23:20:26 ttl 59m59.998s

# 释放锁
$ dblock -uri redis://localhost:6379 -key abc -token dGjS1xUq1RCAbsGPBbP66w -release
2023/08/02 23:20:31 obtained, token: dGjS1xUq1RCAbsGPBbP66w, meta: 
2023/08/02 23:20:31 ttl 59m59.999s
2023/08/02 23:20:31 release successfully

# 释放锁后,再次加锁,成功
$ dblock -uri redis://localhost:6379 -key abc                                       
2023/08/02 23:20:41 obtained, token: mX6u3WSLkObsZWYouk6PcA, meta: 
2023/08/02 23:20:41 ttl 59m59.999s

resources

  • PostgreSQL Lock Client for Go
  • bsm/redislock
  • lukas-krecan/ShedLock ShedLock 确保您的计划任务最多同时执行一次。如果一个任务正在一个节点上执行,它会获取一个锁,以防止从另一个节点(或线程)执行同一任务。请注意,如果一个任务已经在一个节点上执行,则其他节点上的执行不会等待,而是会被跳过。 ShedLock 使用 Mongo、JDBC 数据库、Redis、Hazelcast、ZooKeeper 等外部存储进行协调。
  • Lockgate is a cross-platform distributed locking library for Go. Supports distributed locks backed by Kubernetes or HTTP lock server. Supports conventional OS file locks.

Documentation

Overview

Package dblock provides a distributed lock abstraction.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNotObtained is returned when a lock cannot be obtained.
	ErrNotObtained = errors.New("dblock: not obtained")

	// ErrLockNotHeld is returned when trying to release an inactive lock.
	ErrLockNotHeld = errors.New("dblock: lock not held")

	// ErrNoProviders is returned when trying to obtain a lock.
	ErrNoProviders = errors.New("dblock: no providers registered")
)

Functions

func RandomToken

func RandomToken() (string, error)

RandomToken generates a random token.

Types

type Client

type Client interface {
	// Obtain tries to obtain a new lock using a key with the given TTL.
	// May return ErrNotObtained if not successful.
	Obtain(ctx context.Context, key string, ttl time.Duration, optionsFns ...OptionsFn) (Lock, error)
}

Client abstracts the distributed lock.

var DefaultClient Client

DefaultClient setup the default client.

type ClientView

type ClientView interface {
	View(ctx context.Context, key string) (LockView, error)
}

ClientView abstracts the distributed lock viewing.

type Lock

type Lock interface {
	// Token returns the token value set by the lock.
	Token() string

	// Metadata returns the metadata of the lock.
	Metadata() string

	// TTL returns the remaining time-to-live. Returns 0 if the lock has expired.
	TTL(ctx context.Context) (time.Duration, error)
	// Refresh extends the lock with a new TTL.
	// May return ErrNotObtained if refresh is unsuccessful.
	Refresh(ctx context.Context, ttl time.Duration) error
	// Release manually releases the lock.
	// May return ErrLockNotHeld.
	Release(ctx context.Context) error
}

Lock represents an obtained, distributed lock.

func Obtain

func Obtain(ctx context.Context, key string, ttl time.Duration, optionsFns ...OptionsFn) (Lock, error)

Obtain tries to obtain a new lock using a key with the given TTL. May return ErrNotObtained if not successful, or ErrNoProviders if no providers registers.

type LockView

type LockView interface {
	// GetToken returns the token value set by the lock.
	GetToken() string

	// GetMetadata returns the metadata of the lock.
	GetMetadata() string

	// GetUntil returns expired time in RFC3339.
	GetUntil() string
}

LockView is the view of a lock for viewing.

type Options

type Options struct {
	// RetryStrategy allows to customise the lock retry strategy.
	// Default: do not retry
	RetryStrategy RetryStrategy

	// Meta string.
	Meta string

	// Token is a unique value that is used to identify the lock. By default, a random tokens are generated. Use this
	// option to provide a custom token instead.
	Token string
}

Options describe the options for the lock.

func (*Options) GetRetryStrategy

func (o *Options) GetRetryStrategy() RetryStrategy

GetRetryStrategy returns the retry strategy.

type OptionsFn

type OptionsFn func(*Options)

OptionsFn allows to customise the lock retry strategy.

func WithMeta

func WithMeta(meta string) OptionsFn

WithMeta set the metadata.

func WithRetryStrategy

func WithRetryStrategy(strategy RetryStrategy) OptionsFn

WithRetryStrategy set the lock retry strategy.

func WithToken

func WithToken(token string) OptionsFn

WithToken set the token.

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.

Directories

Path Synopsis
cmd
pkg

Jump to

Keyboard shortcuts

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