lock

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Aug 27, 2026 License: MIT Imports: 21 Imported by: 0

README

Lock

Lock provides exclusive and shared locks for synchronizing access to resources inside one Go process or across services.

The package includes non-blocking and blocking acquisition, expiring locks, ownership checks, transferable keys, shared read locks, quorum stores, local file locks, and semantic adapters for common remote lock systems. It uses only the Go standard library and keeps storage clients behind the LeaseBackend interface.

Installation

The package requires Go 1.26 or newer.

go get github.com/lemric/lock-go

Quick Start

package main

import (
    "context"
    "fmt"
    "log"

    lock "github.com/lemric/lock-go"
)

func main() {
    store := lock.NewInMemoryStore()
    factory := lock.NewFactory(store)
    invoiceLock := factory.CreateDefault("invoice:INV-1001")

    acquired, err := invoiceLock.Acquire(context.Background(), false)
    if err != nil {
        log.Fatal(err)
    }
    if !acquired {
        fmt.Println("invoice is already being processed")
        return
    }
    defer func() {
        if err := invoiceLock.Close(); err != nil {
            log.Printf("release invoice lock: %v", err)
        }
    }()

    fmt.Println("processing invoice INV-1001")
}

The resource string identifies what is protected. Two independently created locks for the same resource have different owners, so only one can acquire an exclusive lock at a time. Calling Acquire again on the same lock is idempotent.

The second Acquire argument selects blocking behavior. false returns immediately with acquired == false when another owner holds the resource; true waits until acquisition succeeds or the context is canceled.

Go has no deterministic destructors. autoRelease therefore takes effect when the application explicitly calls Close; use defer lock.Close() or call Release directly.

Documentation

Testing

go test ./...
go test -race ./...
go vet ./...

License

This package is released under the MIT License.

Documentation

Index

Constants

View Source
const DefaultTTL = 300 * time.Second

Variables

View Source
var (
	ErrLockConflicted    = errors.New("lock acquired by other owner")
	ErrLockExpired       = errors.New("lock expired")
	ErrInvalidTTL        = errors.New("invalid lock TTL")
	ErrUnserializableKey = errors.New("key cannot be serialized")
	ErrUnsupportedStore  = errors.New("unsupported lock store")
	ErrTableNotFound     = errors.New("lock table not found")
)

Functions

func NormalizeKey

func NormalizeKey(key *Key) ([]byte, error)

Types

type AcquiringError

type AcquiringError struct {
	Resource string
	Err      error
}

func (*AcquiringError) Error

func (e *AcquiringError) Error() string

func (*AcquiringError) Unwrap

func (e *AcquiringError) Unwrap() error

type BackendLockMode

type BackendLockMode uint8
const (
	BackendWrite BackendLockMode = iota
	BackendRead
)

type BlockingSharedStore

type BlockingSharedStore interface {
	SharedStore
	BlockingStore
	WaitAndSaveRead(context.Context, *Key) error
}

type BlockingStore

type BlockingStore interface {
	Store
	WaitAndSave(context.Context, *Key) error
}

type CombinedStore

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

func NewCombinedStore

func NewCombinedStore(stores []Store, strategy Strategy) (*CombinedStore, error)

func (*CombinedStore) Delete

func (s *CombinedStore) Delete(key *Key) error

func (*CombinedStore) Exists

func (s *CombinedStore) Exists(key *Key) (bool, error)

func (*CombinedStore) PutOffExpiration

func (s *CombinedStore) PutOffExpiration(key *Key, ttl time.Duration) error

func (*CombinedStore) Save

func (s *CombinedStore) Save(key *Key) error

func (*CombinedStore) SaveRead

func (s *CombinedStore) SaveRead(key *Key) error

type ConsensusStrategy

type ConsensusStrategy struct{}

func (ConsensusStrategy) CanBeMet

func (ConsensusStrategy) CanBeMet(failures, total int) bool

func (ConsensusStrategy) IsMet

func (ConsensusStrategy) IsMet(successes, total int) bool

type DynamoDBStore

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

func NewDynamoDBStore

func NewDynamoDBStore(backend LeaseBackend, defaultTTL time.Duration) (*DynamoDBStore, error)

func (*DynamoDBStore) Delete

func (s *DynamoDBStore) Delete(key *Key) error

func (*DynamoDBStore) Exists

func (s *DynamoDBStore) Exists(key *Key) (bool, error)

func (*DynamoDBStore) PutOffExpiration

func (s *DynamoDBStore) PutOffExpiration(key *Key, ttl time.Duration) error

func (*DynamoDBStore) Save

func (s *DynamoDBStore) Save(key *Key) error

type Factory

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

func NewFactory

func NewFactory(store Store) *Factory

func (*Factory) Create

func (f *Factory) Create(resource string, ttl time.Duration, autoRelease bool) *Lock

func (*Factory) CreateDefault

func (f *Factory) CreateDefault(resource string) *Lock

func (*Factory) CreateFromKey

func (f *Factory) CreateFromKey(key *Key, ttl time.Duration, autoRelease bool) *Lock

type FileStore

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

func NewFileStore

func NewFileStore(directory string) (*FileStore, error)

func (*FileStore) Delete

func (s *FileStore) Delete(key *Key) error

func (*FileStore) Exists

func (s *FileStore) Exists(key *Key) (bool, error)

func (*FileStore) PutOffExpiration

func (*FileStore) PutOffExpiration(*Key, time.Duration) error

func (*FileStore) Save

func (s *FileStore) Save(key *Key) error

func (*FileStore) SaveRead

func (s *FileStore) SaveRead(key *Key) error

func (*FileStore) WaitAndSave

func (s *FileStore) WaitAndSave(ctx context.Context, key *Key) error

func (*FileStore) WaitAndSaveRead

func (s *FileStore) WaitAndSaveRead(ctx context.Context, key *Key) error

type InMemoryStore

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

InMemoryStore keeps process-local locks. A store instance owns its own lock namespace, so independently constructed stores do not share locks.

func NewInMemoryStore

func NewInMemoryStore() *InMemoryStore

func (*InMemoryStore) Delete

func (s *InMemoryStore) Delete(key *Key) error

func (*InMemoryStore) Exists

func (s *InMemoryStore) Exists(key *Key) (bool, error)

func (*InMemoryStore) PutOffExpiration

func (*InMemoryStore) PutOffExpiration(*Key, time.Duration) error

func (*InMemoryStore) Save

func (s *InMemoryStore) Save(key *Key) error

func (*InMemoryStore) SaveRead

func (s *InMemoryStore) SaveRead(key *Key) error

type Key

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

func DecodeKey

func DecodeKey(payload []byte) (*Key, error)

func DenormalizeKey

func DenormalizeKey(payload []byte) (*Key, error)

func NewKey

func NewKey(resource string) *Key

func (*Key) Expired

func (k *Key) Expired() bool

func (*Key) MarkUnserializable

func (k *Key) MarkUnserializable()

func (*Key) MarshalBinary

func (k *Key) MarshalBinary() ([]byte, error)

func (*Key) ReduceLifetime

func (k *Key) ReduceLifetime(ttl time.Duration)

func (*Key) RemainingLifetime

func (k *Key) RemainingLifetime() (time.Duration, bool)

func (*Key) RemoveState

func (k *Key) RemoveState(name string)

func (*Key) ResetLifetime

func (k *Key) ResetLifetime()

func (*Key) SetState

func (k *Key) SetState(name string, value any)

func (*Key) State

func (k *Key) State(name string) (any, bool)

func (*Key) String

func (k *Key) String() string

func (*Key) UnmarshalBinary

func (k *Key) UnmarshalBinary(payload []byte) error

type LeaseBackend

type LeaseBackend interface {
	Acquire(ctx context.Context, resource, token string, mode BackendLockMode, ttl time.Duration, blocking bool) error
	Delete(ctx context.Context, resource, token string) error
	Exists(ctx context.Context, resource, token string) (bool, error)
	Refresh(ctx context.Context, resource, token string, ttl time.Duration) error
}

type Lock

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

func NewLock

func NewLock(key *Key, store Store, ttl time.Duration, autoRelease bool) *Lock

func (*Lock) Acquire

func (l *Lock) Acquire(ctx context.Context, blocking bool) (bool, error)

func (*Lock) AcquireRead

func (l *Lock) AcquireRead(ctx context.Context, blocking bool) (bool, error)

func (*Lock) Close

func (l *Lock) Close() error

func (*Lock) Expired

func (l *Lock) Expired() bool

func (*Lock) IsAcquired

func (l *Lock) IsAcquired() (bool, error)

func (*Lock) Refresh

func (l *Lock) Refresh(ttl time.Duration) error

func (*Lock) Release

func (l *Lock) Release() error

func (*Lock) RemainingLifetime

func (l *Lock) RemainingLifetime() (time.Duration, bool)

func (*Lock) SetLogger

func (l *Lock) SetLogger(logger *slog.Logger)

type Locker

type Locker interface {
	Acquire(context.Context, bool) (bool, error)
	Refresh(time.Duration) error
	IsAcquired() (bool, error)
	Release() error
	Expired() bool
	RemainingLifetime() (time.Duration, bool)
}

type MemcachedStore

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

func NewMemcachedStore

func NewMemcachedStore(backend LeaseBackend, defaultTTL time.Duration) (*MemcachedStore, error)

func (*MemcachedStore) Delete

func (s *MemcachedStore) Delete(key *Key) error

func (*MemcachedStore) Exists

func (s *MemcachedStore) Exists(key *Key) (bool, error)

func (*MemcachedStore) PutOffExpiration

func (s *MemcachedStore) PutOffExpiration(key *Key, ttl time.Duration) error

func (*MemcachedStore) Save

func (s *MemcachedStore) Save(key *Key) error

type MongoDBOptions

type MongoDBOptions struct {
	URI        string
	Database   string
	Collection string
	DefaultTTL time.Duration
	Source     MongoSource
}

func ParseMongoDBStoreDSN

func ParseMongoDBStoreDSN(uri string, options MongoDBOptions) (MongoDBOptions, error)

type MongoDBStore

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

func NewMongoDBStore

func NewMongoDBStore(backend LeaseBackend, options MongoDBOptions) (*MongoDBStore, error)

func (*MongoDBStore) CreateTTLIndex

func (s *MongoDBStore) CreateTTLIndex(ctx context.Context) error

func (*MongoDBStore) Delete

func (s *MongoDBStore) Delete(key *Key) error

func (*MongoDBStore) Exists

func (s *MongoDBStore) Exists(key *Key) (bool, error)

func (*MongoDBStore) PutOffExpiration

func (s *MongoDBStore) PutOffExpiration(key *Key, ttl time.Duration) error

func (*MongoDBStore) Save

func (s *MongoDBStore) Save(key *Key) error

type MongoSource

type MongoSource uint8
const (
	MongoSourceClient MongoSource = iota
	MongoSourceDatabase
	MongoSourceCollection
)

type MySQLStore

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

func NewMySQLStore

func NewMySQLStore(backend LeaseBackend, options MySQLStoreOptions) (*MySQLStore, error)

func (*MySQLStore) Delete

func (s *MySQLStore) Delete(key *Key) error

func (*MySQLStore) Exists

func (s *MySQLStore) Exists(key *Key) (bool, error)

func (*MySQLStore) PutOffExpiration

func (s *MySQLStore) PutOffExpiration(key *Key, ttl time.Duration) error

func (*MySQLStore) Save

func (s *MySQLStore) Save(key *Key) error

func (*MySQLStore) WaitAndSave

func (s *MySQLStore) WaitAndSave(ctx context.Context, key *Key) error

type MySQLStoreOptions

type MySQLStoreOptions struct {
	Driver                   string
	ErrorMode                SQLErrorMode
	ServerVersion            string
	StringifyResults         bool
	NativePreparedStatements bool
}

func ParseMySQLStoreDSN

func ParseMySQLStoreDSN(dsn string) (MySQLStoreOptions, error)

type NoLock

type NoLock struct{}

func (NoLock) Acquire

func (NoLock) Acquire(context.Context, bool) (bool, error)

func (NoLock) AcquireRead

func (NoLock) AcquireRead(context.Context, bool) (bool, error)

func (NoLock) Close

func (NoLock) Close() error

func (NoLock) Expired

func (NoLock) Expired() bool

func (NoLock) IsAcquired

func (NoLock) IsAcquired() (bool, error)

func (NoLock) Refresh

func (NoLock) Refresh(time.Duration) error

func (NoLock) Release

func (NoLock) Release() error

func (NoLock) RemainingLifetime

func (NoLock) RemainingLifetime() (time.Duration, bool)

type NullStore

type NullStore struct{}

func NewNullStore

func NewNullStore() *NullStore

func (*NullStore) Delete

func (*NullStore) Delete(*Key) error

func (*NullStore) Exists

func (*NullStore) Exists(*Key) (bool, error)

func (*NullStore) PutOffExpiration

func (*NullStore) PutOffExpiration(*Key, time.Duration) error

func (*NullStore) Save

func (*NullStore) Save(*Key) error

func (*NullStore) SaveRead

func (*NullStore) SaveRead(*Key) error

type PostgreSQLStore

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

func NewPostgreSQLStore

func NewPostgreSQLStore(backend LeaseBackend, options PostgreSQLStoreOptions) (*PostgreSQLStore, error)

func (*PostgreSQLStore) Delete

func (s *PostgreSQLStore) Delete(key *Key) error

func (*PostgreSQLStore) Exists

func (s *PostgreSQLStore) Exists(key *Key) (bool, error)

func (*PostgreSQLStore) PutOffExpiration

func (s *PostgreSQLStore) PutOffExpiration(key *Key, ttl time.Duration) error

func (*PostgreSQLStore) Save

func (s *PostgreSQLStore) Save(key *Key) error

func (*PostgreSQLStore) SaveRead

func (s *PostgreSQLStore) SaveRead(key *Key) error

func (*PostgreSQLStore) WaitAndSave

func (s *PostgreSQLStore) WaitAndSave(ctx context.Context, key *Key) error

func (*PostgreSQLStore) WaitAndSaveRead

func (s *PostgreSQLStore) WaitAndSaveRead(ctx context.Context, key *Key) error

type PostgreSQLStoreOptions

type PostgreSQLStoreOptions struct{ Driver string }

type RedisStore

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

func NewRedisStore

func NewRedisStore(backend LeaseBackend, defaultTTL time.Duration) (*RedisStore, error)

func (*RedisStore) Delete

func (s *RedisStore) Delete(key *Key) error

func (*RedisStore) Exists

func (s *RedisStore) Exists(key *Key) (bool, error)

func (*RedisStore) PutOffExpiration

func (s *RedisStore) PutOffExpiration(key *Key, ttl time.Duration) error

func (*RedisStore) Save

func (s *RedisStore) Save(key *Key) error

func (*RedisStore) SaveRead

func (s *RedisStore) SaveRead(key *Key) error

type ReleasingError

type ReleasingError struct {
	Resource string
	Err      error
}

func (*ReleasingError) Error

func (e *ReleasingError) Error() string

func (*ReleasingError) Unwrap

func (e *ReleasingError) Unwrap() error

type SQLErrorMode

type SQLErrorMode uint8
const (
	SQLErrorModeException SQLErrorMode = iota
	SQLErrorModeSilent
)

type SQLPlatform

type SQLPlatform uint8
const (
	SQLPlatformSQLite SQLPlatform = iota
	SQLPlatformPostgreSQL
	SQLPlatformMySQL
	SQLPlatformMariaDB
)

type SQLSchema

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

func NewSQLSchema

func NewSQLSchema() *SQLSchema

func (*SQLSchema) AddTable

func (s *SQLSchema) AddTable(name string, columns []string)

func (*SQLSchema) Columns

func (s *SQLSchema) Columns(name string) []string

func (*SQLSchema) HasTable

func (s *SQLSchema) HasTable(name string) bool

type SQLStore

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

func NewSQLStore

func NewSQLStore(backend LeaseBackend, options SQLStoreOptions) (*SQLStore, error)

func (*SQLStore) ConfigureSchema

func (*SQLStore) ConfigureSchema(schema *SQLSchema, sameDatabase func() bool)

func (*SQLStore) Delete

func (s *SQLStore) Delete(key *Key) error

func (*SQLStore) Exists

func (s *SQLStore) Exists(key *Key) (bool, error)

func (*SQLStore) PutOffExpiration

func (s *SQLStore) PutOffExpiration(key *Key, ttl time.Duration) error

func (*SQLStore) Save

func (s *SQLStore) Save(key *Key) error

type SQLStoreOptions

type SQLStoreOptions struct {
	Dialect       string
	DefaultTTL    time.Duration
	Transactional bool
}

func ParseSQLStoreDSN

func ParseSQLStoreDSN(dsn string) (SQLStoreOptions, error)

type SemaphoreStore

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

func NewSemaphoreStore

func NewSemaphoreStore(projectID string, backend LeaseBackend) (*SemaphoreStore, error)

func (*SemaphoreStore) Delete

func (s *SemaphoreStore) Delete(key *Key) error

func (*SemaphoreStore) Exists

func (s *SemaphoreStore) Exists(key *Key) (bool, error)

func (*SemaphoreStore) ProjectID

func (s *SemaphoreStore) ProjectID() string

func (*SemaphoreStore) PutOffExpiration

func (s *SemaphoreStore) PutOffExpiration(key *Key, ttl time.Duration) error

func (*SemaphoreStore) Save

func (s *SemaphoreStore) Save(key *Key) error

func (*SemaphoreStore) WaitAndSave

func (s *SemaphoreStore) WaitAndSave(ctx context.Context, key *Key) error

type SharedLocker

type SharedLocker interface {
	Locker
	AcquireRead(context.Context, bool) (bool, error)
}

type SharedStore

type SharedStore interface {
	Store
	SaveRead(*Key) error
}

type Store

type Store interface {
	Save(*Key) error
	Delete(*Key) error
	Exists(*Key) (bool, error)
	PutOffExpiration(*Key, time.Duration) error
}

func OpenStore

func OpenStore(dsn string) (Store, error)

type StoreBackendKind

type StoreBackendKind uint8
const (
	StoreBackendRedis StoreBackendKind = iota
	StoreBackendMemcached
	StoreBackendRedisProxy
)

type StoreFactory

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

func NewStoreFactory

func NewStoreFactory(backend LeaseBackend) *StoreFactory

func (*StoreFactory) FromBackend

func (f *StoreFactory) FromBackend(kind StoreBackendKind, advisory bool) (Store, error)

func (*StoreFactory) FromSQLDriver

func (f *StoreFactory) FromSQLDriver(driver string, advisory bool) (Store, error)

func (*StoreFactory) FromSQLPlatform

func (f *StoreFactory) FromSQLPlatform(platform SQLPlatform, version string, advisory bool) (Store, error)

func (*StoreFactory) Open

func (f *StoreFactory) Open(dsn string) (Store, error)

type Strategy

type Strategy interface {
	IsMet(successes, total int) bool
	CanBeMet(failures, total int) bool
}

type UnanimousStrategy

type UnanimousStrategy struct{}

func (UnanimousStrategy) CanBeMet

func (UnanimousStrategy) CanBeMet(failures, _ int) bool

func (UnanimousStrategy) IsMet

func (UnanimousStrategy) IsMet(successes, total int) bool

type ZooKeeperOptions

type ZooKeeperOptions struct {
	Servers []string
	Path    string
}

func ParseZooKeeperDSN

func ParseZooKeeperDSN(dsn string) (ZooKeeperOptions, error)

type ZooKeeperStore

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

func NewZooKeeperStore

func NewZooKeeperStore(backend LeaseBackend, root string) (*ZooKeeperStore, error)

func (*ZooKeeperStore) Delete

func (s *ZooKeeperStore) Delete(key *Key) error

func (*ZooKeeperStore) Exists

func (s *ZooKeeperStore) Exists(key *Key) (bool, error)

func (*ZooKeeperStore) PutOffExpiration

func (s *ZooKeeperStore) PutOffExpiration(key *Key, ttl time.Duration) error

func (*ZooKeeperStore) Save

func (s *ZooKeeperStore) Save(key *Key) error

Jump to

Keyboard shortcuts

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