glock

package module
v1.0.0-...-21ad2bc Latest Latest
Warning

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

Go to latest
Published: Aug 25, 2017 License: MIT Imports: 14 Imported by: 2

README

glock

Build Status GoDoc

Package glock implements locking against a variety of backends for the Go programming language.

glock is released under the MIT license.

WARN: this package is under heavy development, API is not stable yet.

Backends

  • Redis

    Simple Redis implementation. Requires redis >= 2.6 as it uses lua scripting.
    This implementation is safe only if used againt a single master, with no replication.

  • Cassandra

    Cassandra implementation, inspired by datastax's "Consensus on Cassandra" blogpost.
    Requires cassandra >= 2.0 as it uses lightweight transactions.

  • Memory

    Naive in-process implementation, only useful for testing.

Installation

go get gopkg.in/gbagnoli/glock.v1

Tests

By default, tests runs only using the memory driver. To run test for one or more specific backend, use build tags.

go test -tags="redis cassandra"

Roadmap

  1. Add more tests
  2. Add more documentation
  3. Add more backends (in no particular order)
  1. Stabilize interface.

Example

See glock-example

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidTTL is returnend when the TTL specified is not a valid TTL
	ErrInvalidTTL = errors.New("Invalid ttl value")
	// ErrLockHeldByOtherClient is returned when the operation cannot be performed as the lock is
	// not held by the current client
	ErrLockHeldByOtherClient = errors.New("Lock held by other client")
	// ErrInvalidLock is returned when the lock name is invalid
	ErrInvalidLock = errors.New("Invalid lock name")
	// ErrLockNotOwned is returned when either the lock is not existing or held by another client
	ErrLockNotOwned = errors.New("Lock is not held by current client")
)

Functions

This section is empty.

Types

type AcquireOptions

type AcquireOptions struct {
	// TTL is the ttl of the lock. If <= 0 the manager defaultTTL is used
	TTL time.Duration
	// How long to wait for the lock to be acquired.
	MaxWait time.Duration
	// The Data to set with the lock.
	Data string
}

AcquireOptions allows to set options during lock acquisition.

type CassandraClient

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

CassandraClient is the Client implementation for cassandra

func NewCassandraLockClient

func NewCassandraLockClient(opts CassandraOptions) (*CassandraClient, error)

NewCassandraLockClient creates a new client from options

func (*CassandraClient) Clone

func (c *CassandraClient) Clone() Client

Clone returns a copy of the currenct client

func (*CassandraClient) Close

func (c *CassandraClient) Close()

Close closes the connection to cassandra

func (*CassandraClient) ID

func (c *CassandraClient) ID() string

ID returns the current client ID

func (*CassandraClient) NewLock

func (c *CassandraClient) NewLock(name string) Lock

NewLock creates a new Lock. Lock is not automatically acquired.

func (*CassandraClient) Reconnect

func (c *CassandraClient) Reconnect() error

Reconnect reconnects to cassandra, or connects if not connected

func (*CassandraClient) SetID

func (c *CassandraClient) SetID(id string)

SetID sets the ID for the current client

type CassandraLock

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

CassandraLock is the Lock implementation for cassandra

func (*CassandraLock) Acquire

func (l *CassandraLock) Acquire(ttl time.Duration) error

Acquire acquires the lock for the specified time lentgh (ttl). It returns immadiately if the lock cannot be acquired

func (*CassandraLock) Info

func (l *CassandraLock) Info() (*LockInfo, error)

Info returns information about the lock.

func (*CassandraLock) Refresh

func (l *CassandraLock) Refresh() error

Refresh extends the lock by extending the TTL in the store. It returns an error if the lock is not owned by the current client

func (*CassandraLock) RefreshTTL

func (l *CassandraLock) RefreshTTL(ttl time.Duration) error

RefreshTTL Extends the lock, if owned, for the specified TTL. ttl argument becomes the new ttl for the lock: successive calls to Refresh() will use this ttl It returns an error if the lock is not owned by the current client

func (*CassandraLock) Release

func (l *CassandraLock) Release() error

Release releases the lock if owned. Returns an error if the lock is not owned by this client

func (*CassandraLock) SetData

func (l *CassandraLock) SetData(data string)

SetData sets the data payload for the lock. The data is set into the backend only when the lock is acquired, so any call to this method after acquisition won't update the value.

type CassandraOptions

type CassandraOptions struct {
	Hosts             []string
	KeySpace          string
	Username          string
	Password          string
	TableName         string
	ReplicationFactor int
}

CassandraOptions represents options for connecting to cassandra

type Client

type Client interface {

	// ID returns the client id
	ID() string

	// SetID set the client id
	SetID(id string)

	// Reconnect reconnects to the store, or connects if not connected
	Reconnect() error

	// Close closes the connection to the store
	Close()

	// NewLock returns a new lock object
	NewLock(name string) Lock

	// Clone returns a disconnected copy of the client
	Clone() Client
}

Client represents a client that can own locks in the store

type DialFunc

type DialFunc func(network, address string, options ...redis.DialOption) (redis.Conn, error)

DialFunc is a function prototype that matches redigo/redis.Dial signature.

type ExecOptions

type ExecOptions struct {
	Options AcquireOptions
	PreExec PreExecHook
}

ExecOptions controls execution of Exec

type Lock

type Lock interface {

	// Info returns a LockInfo struct with information about this lock
	Info() (*LockInfo, error)

	// Acquire tries to acquire the lock for a specified duration
	// The lock must not be locked.
	Acquire(ttl time.Duration) error

	// Refresh extends the validity of the lock by its ttl
	// The lock must be acquired by the current client.
	Refresh() error

	// RefreshTTL extends the validity of the lock by the given ttl.
	// The lock must be acquired by the current client
	RefreshTTL(ttl time.Duration) error

	// Release removed the lock from the store.
	// The lock must be owned by the current client
	Release() error

	// SetData sets the data payload for the lock.
	// The data is set into the backend only when the lock is acquired,
	// so any call to this method after acquisition won't update the value.
	SetData(data string)
}

Lock represent a lock in the store

type LockInfo

type LockInfo struct {
	// Name is the lock name
	Name string
	// Acquired is true if the lock is acquired, false otherwise
	Acquired bool
	// Owner if the ClientID of the client owning the lock, if any
	Owner string
	// The remaining TTL until the lock is automatically expired
	TTL time.Duration
	// Data associated with the lock, if any
	Data string
}

LockInfo represent information about a given lock

type LockManager

type LockManager struct {
	Logger *log.Logger
	// contains filtered or unexported fields
}

LockManager manages all the locks for a single client

func NewLockManager

func NewLockManager(client Client, opts AcquireOptions) *LockManager

NewLockManager returns a new LockManager for the given client. By default, logging is sent to /dev/null. You must call SetOutput() on the Logger instance if you want logging to be sent somewhere.

func (*LockManager) Acquire

func (m *LockManager) Acquire(lockName string, opts AcquireOptions) error

Acquire tries to acquire the lock with the given name using the default TTL for the manager. If the lock cannot be acquired, it will wait up to MaxWait for the lock to be released by the owner. If this manager instance already has acquired this lock, this action is a no-op.

func (*LockManager) Client

func (m *LockManager) Client() Client

Client returns the current lock client in use

func (*LockManager) Exec

func (manager *LockManager) Exec(lock string, command *exec.Cmd, opts ExecOptions) (int, error)

Exec executes the command only if the lock can be acquired It refreshes the lock using manager's heartbeats, and terminates the command if the lock is lost somehow. It will wait up to maxWait for the lock to be acquired returns the return code of the command, and any errors

func (*LockManager) Info

func (m *LockManager) Info(lockName string) (*LockInfo, error)

Info returns information about a lock with the given name

func (*LockManager) Refresh

func (m *LockManager) Refresh(lockName string) error

Refresh refreshes a single lock.

func (*LockManager) Release

func (m *LockManager) Release(lockName string) error

Release releases a lock with the given name. The lock must be held by the current manager. Any eventual heartbeating will be stopped as well.

func (*LockManager) ReleaseAll

func (m *LockManager) ReleaseAll() map[string]error

ReleaseAll releases all the locks held by the manager.

func (*LockManager) SetData

func (m *LockManager) SetData(lock, data string) error

SetData updates data for an existing, acquired lock. Data won't be saved into the backend database until the lock is refreshed (either manually or at the next heartbeat.

func (*LockManager) StartHeartbeat

func (m *LockManager) StartHeartbeat(lockName string) (<-chan error, error)

StartHeartbeat starts a goroutine in the backgroud that will refresh the lock The lock will be refreshed every ttl/2 or whichever is greater. The background goroutine will panic() if the lock cannot be refreshed for any reason The background goroutine will run forever until StopHeartbeat is called or the lock released. It will return a channel to signal if the lock cannot be refreshed during heartbeats (before panicking)

func (*LockManager) StopHeartbeat

func (m *LockManager) StopHeartbeat(lockName string)

StopHeartbeat will stop the background gororoutine, if any, that is heartbeating the given lock

type MemoryClient

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

func NewMemoryClient

func NewMemoryClient(id string) *MemoryClient

func (*MemoryClient) Clone

func (m *MemoryClient) Clone() Client

func (*MemoryClient) Close

func (m *MemoryClient) Close()

func (*MemoryClient) ID

func (m *MemoryClient) ID() string

func (*MemoryClient) NewLock

func (m *MemoryClient) NewLock(name string) Lock

func (*MemoryClient) Reconnect

func (m *MemoryClient) Reconnect() error

func (*MemoryClient) SetID

func (m *MemoryClient) SetID(id string)

type MemoryLock

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

func (*MemoryLock) Acquire

func (l *MemoryLock) Acquire(ttl time.Duration) error

func (*MemoryLock) Info

func (l *MemoryLock) Info() (*LockInfo, error)

func (*MemoryLock) Refresh

func (l *MemoryLock) Refresh() error

func (*MemoryLock) RefreshTTL

func (l *MemoryLock) RefreshTTL(ttl time.Duration) error

func (*MemoryLock) Release

func (l *MemoryLock) Release() error

func (*MemoryLock) SetData

func (l *MemoryLock) SetData(data string)

SetData sets the data payload for the lock. The data is set into the backend only when the lock is acquired, so any call to this method after acquisition won't update the value.

type PreExecHook

type PreExecHook func(log *log.Logger) error

PreExecHook is an hook for the manager.Exec. It gets called after the lock is acquired but before running the subprocess. If the return value (err) is not nil, Exec will exit with (-1, err)

type RedisClient

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

RedisClient implements the Client interface to manage locks in redis

func NewRedisClient

func NewRedisClient(opts RedisOptions) (*RedisClient, error)

NewRedisClient return a new RedisClient given the provided RedisOptions

func (*RedisClient) Clone

func (c *RedisClient) Clone() Client

Clone returns a disconnected copy of the currenct client

func (*RedisClient) Close

func (c *RedisClient) Close()

Close closes the connecton to redis

func (*RedisClient) ID

func (c *RedisClient) ID() string

ID returns the current client ID

func (*RedisClient) NewLock

func (c *RedisClient) NewLock(name string) Lock

NewLock creates a new Lock. Lock is not automatically acquired.

func (*RedisClient) Reconnect

func (c *RedisClient) Reconnect() error

Reconnect reconnects to redis, or connects if not connected

func (*RedisClient) SetID

func (c *RedisClient) SetID(id string)

SetID sets the ID for the current client

type RedisLock

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

RedisLock implements the Lock interface for locks in the redis store

func (*RedisLock) Acquire

func (l *RedisLock) Acquire(ttl time.Duration) error

Acquire acquires the lock for the specified time lentgh (ttl). It returns immadiately if the lock cannot be acquired

func (*RedisLock) Info

func (l *RedisLock) Info() (*LockInfo, error)

Info returns information about the lock.

func (*RedisLock) Refresh

func (l *RedisLock) Refresh() error

Refresh extends the lock by extending the TTL in the store. It returns an error if the lock is not owned by the current client

func (*RedisLock) RefreshTTL

func (l *RedisLock) RefreshTTL(ttl time.Duration) error

RefreshTTL Extends the lock, if owned, for the specified TTL. ttl argument becomes the new ttl for the lock: successive calls to Refresh() will use this ttl It returns an error if the lock is not owned by the current client

func (*RedisLock) Release

func (l *RedisLock) Release() error

Release releases the lock if owned. Returns an error if the lock is not owned by this client

func (*RedisLock) SetData

func (l *RedisLock) SetData(data string)

SetData sets the data payload for the lock. The data is set into the backend only when the lock is acquired, so any call to this method after acquisition won't update the value.

type RedisOptions

type RedisOptions struct {
	// Network, i.e. 'tcp'
	Network string
	// Address, i.e. 'localhost:6379'
	Address string
	// ClientID is the current client ID. If not set, it will be autogenerated
	ClientID string
	// Namespace is an optional namespace for all redis keys that will be created.
	// It must contain the separator (if any). If not set, the deafault value
	// is used "glock:"
	Namespace string
	// A list of redigo/redis.DialOption to be used when connecting to redis
	DialOptions []redis.DialOption
	// The function used to connect to redis. defaults to redigo/redis.Dial
	DialFunc DialFunc
}

RedisOptions represent options to connect to redis

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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