Documentation ¶
Overview ¶
Package dynamolock provides a simple utility for using DynamoDB's consistent read/write feature to use it for managing distributed locks.
In order to use this package, the client must create a table in DynamoDB, although the client provides a convenience method for creating that table (CreateTable).
Basic usage:
import ( "log" "cirello.io/dynamolock" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/dynamodb" ) // --- svc := dynamodb.New(session.New(), &aws.Config{ Region: aws.String("us-west-2"), }) c, err := dynamolock.New(svc, "locks", dynamolock.WithLeaseDuration(3*time.Second), dynamolock.WithHeartbeatPeriod(1*time.Second), ) if err != nil { log.Fatal(err) } log.Println("ensuring table exists") c.CreateTable("locks", &dynamodb.ProvisionedThroughput{ ReadCapacityUnits: aws.Int64(5), WriteCapacityUnits: aws.Int64(5), }, dynamolock.WithCustomPartitionKeyName("key"), ) data := []byte("some content a") lockedItem, err := c.AcquireLock("spock", dynamolock.WithData(data), dynamolock.ReplaceData(), ) if err != nil { log.Fatal(err) } log.Println("lock content:", string(lockedItem.Data())) if got := string(lockedItem.Data()); string(data) != got { log.Println("losing information inside lock storage, wanted:", string(data), " got:", got) } log.Println("cleaning lock") success, err := c.ReleaseLock(lockedItem) if !success { log.Fatal("lost lock before release") } if err != nil { log.Fatal("error releasing lock:", err) } log.Println("done")
Index ¶
- Variables
- type AcquireLockOption
- func ReplaceData() AcquireLockOption
- func WithAdditionalAttributes(attr map[string]*dynamodb.AttributeValue) AcquireLockOption
- func WithAdditionalTimeToWaitForLock(d time.Duration) AcquireLockOption
- func WithData(b []byte) AcquireLockOption
- func WithDeleteLockOnRelease() AcquireLockOption
- func WithRefreshPeriod(d time.Duration) AcquireLockOption
- func WithSessionMonitor(safeTime time.Duration, callback func()) AcquireLockOption
- func WithSortKeyOnAcquire(sortKey string) AcquireLockOption
- type Client
- func (c *Client) AcquireLock(key string, opts ...AcquireLockOption) (*Lock, error)
- func (c *Client) Close()
- func (c *Client) CreateTable(tableName string, provisionedThroughput *dynamodb.ProvisionedThroughput, ...) (*dynamodb.CreateTableOutput, error)
- func (c *Client) Get(key string, opts ...GetOptions) (*Lock, error)
- func (c *Client) ReleaseLock(lockItem *Lock, opts ...ReleaseLockOption) (bool, error)
- func (c *Client) SendHeartbeat(lockItem *Lock) error
- type ClientOption
- type CreateTableOption
- type GetOptions
- type Lock
- type LockNotGrantedError
- type Logger
- type ReleaseLockOption
Constants ¶
This section is empty.
Variables ¶
var ( ErrSessionMonitorNotSet = errors.New("session monitor is not set") ErrLockAlreadyReleased = errors.New("lock is already released") )
Errors related to session manager life-cycle.
Functions ¶
This section is empty.
Types ¶
type AcquireLockOption ¶
type AcquireLockOption func(*acquireLockOptions)
AcquireLockOption allows to change how the lock is actually held by the client.
func ReplaceData ¶
func ReplaceData() AcquireLockOption
ReplaceData will force the new content to be stored in the key
func WithAdditionalAttributes ¶
func WithAdditionalAttributes(attr map[string]*dynamodb.AttributeValue) AcquireLockOption
WithAdditionalAttributes stores some additional attributes with each lock. This can be used to add any arbitrary parameters to each lock row.
func WithAdditionalTimeToWaitForLock ¶
func WithAdditionalTimeToWaitForLock(d time.Duration) AcquireLockOption
WithAdditionalTimeToWaitForLock defines how long to wait in addition to the lease duration (if set to 10 minutes, this will try to acquire a lock for at least 10 minutes before giving up and throwing the exception). If set, TimeUnit must also be set.
func WithData ¶
func WithData(b []byte) AcquireLockOption
WithData stores the content into the lock itself.
func WithDeleteLockOnRelease ¶
func WithDeleteLockOnRelease() AcquireLockOption
WithDeleteLockOnRelease defines whether or not the lock should be deleted when Close() is called on the resulting LockItem will force the new content to be stored in the key.
func WithRefreshPeriod ¶
func WithRefreshPeriod(d time.Duration) AcquireLockOption
WithRefreshPeriod defines how long to wait before trying to get the lock again (if set to 10 seconds, for example, it would attempt to do so every 10 seconds).
func WithSessionMonitor ¶
func WithSessionMonitor(safeTime time.Duration, callback func()) AcquireLockOption
WithSessionMonitor registers a callback that is triggered if the lock is about to expire.
The purpose of this construct is to provide two abilities: provide the ability to determine if the lock is about to expire, and run a user-provided callback when the lock is about to expire. The advantage this provides is notification that your lock is about to expire before it is actually expired, and in case of leader election will help in preventing that there are no two leaders present simultaneously.
If due to any reason heartbeating is unsuccessful for a configurable period of time, your lock enters into a phase known as "danger zone." It is during this "danger zone" that the callback will be run.
Bear in mind that the callback may be null. In this case, no callback will be run upon the lock entering the "danger zone"; yet, one can still make use of the Lock.AmIAboutToExpire() call. Furthermore, non-null callbacks can only ever be executed once in a lock's lifetime. Independent of whether or not a callback is run, the client will attempt to heartbeat the lock until the lock is released or obtained by someone else.
Consider an example which uses this mechanism for leader election. One way to make use of this SessionMonitor is to register a callback that kills the instance in case the leader's lock enters the danger zone:
func WithSortKeyOnAcquire ¶
func WithSortKeyOnAcquire(sortKey string) AcquireLockOption
WithSortKeyOnAcquire is the sort key to try and acquire the lock on (specify if and only if the table has sort keys).
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a dynamoDB based distributed lock client.
func (*Client) AcquireLock ¶
func (c *Client) AcquireLock(key string, opts ...AcquireLockOption) (*Lock, error)
AcquireLock holds the defined lock.
func (*Client) CreateTable ¶
func (c *Client) CreateTable(tableName string, provisionedThroughput *dynamodb.ProvisionedThroughput, opts ...CreateTableOption) (*dynamodb.CreateTableOutput, error)
CreateTable prepares a DynamoDB table with the right schema for it to be used by this locking library. The table should be set up in advance, because it takes a few minutes for DynamoDB to provision a new instance. Also, if the table already exists, this will throw an exception.
This method lets you specify a sort key to be used by the lock client. This sort key then needs to be specified in the AmazonDynamoDBLockClientOptions when the lock client object is created.
func (*Client) Get ¶
func (c *Client) Get(key string, opts ...GetOptions) (*Lock, error)
Get finds out who owns the given lock, but does not acquire the lock. It returns the metadata currently associated with the given lock. If the client currently has the lock, it will return the lock, and operations such as releaseLock will work. However, if the client does not have the lock, then operations like releaseLock will not work (after calling Get, the caller should check lockItem.isExpired() to figure out if it currently has the lock.)
func (*Client) ReleaseLock ¶
func (c *Client) ReleaseLock(lockItem *Lock, opts ...ReleaseLockOption) (bool, error)
ReleaseLock releases the given lock if the current user still has it, returning true if the lock was successfully released, and false if someone else already stole the lock. Deletes the lock item if it is released and deleteLockItemOnClose is set. Return true if the lock is released, false otherwise.
func (*Client) SendHeartbeat ¶
SendHeartbeat indicatee that the given lock is still being worked on. If using WithHeartbeatPeriod > 0 when setting up this object, then this method is unnecessary, because the background thread will be periodically calling it and sending heartbeats. However, if WithHeartbeatPeriod = 0, then this method must be called to instruct DynamoDB that the lock should not be expired.
type ClientOption ¶
type ClientOption func(*Client)
ClientOption reconfigure the lock client creation.
func DisableHeartbeat ¶
func DisableHeartbeat() ClientOption
DisableHeartbeat disables automatic hearbeats. Use SendHeartbeat to freshen up the lock.
func WithHeartbeatPeriod ¶
func WithHeartbeatPeriod(d time.Duration) ClientOption
WithHeartbeatPeriod defines the frequency of the heartbeats. Set to zero to disable it. Heartbeats should have no more than half of the duration of the lease.
func WithLeaseDuration ¶
func WithLeaseDuration(d time.Duration) ClientOption
WithLeaseDuration defines how long should the lease be held.
func WithLogger ¶
func WithLogger(l Logger) ClientOption
WithLogger injects a logger into the client, so its internals can be recorded.
func WithOwnerName ¶
func WithOwnerName(s string) ClientOption
WithOwnerName changes the owner linked to the client, and by consequence to locks.
func WithPartitionKeyName ¶
func WithPartitionKeyName(s string) ClientOption
WithPartitionKeyName defines the key name used for asserting keys uniqueness.
type CreateTableOption ¶
type CreateTableOption func(*createDynamoDBTableOptions)
CreateTableOption is an options type for the CreateTable method in the lock client. This allows the user to create a DynamoDB table that is lock client-compatible and specify optional parameters such as the desired throughput and whether or not to use a sort key.
func WithCustomPartitionKeyName ¶
func WithCustomPartitionKeyName(s string) CreateTableOption
WithCustomPartitionKeyName changes the partition key name of the table. If not specified, the default "key" will be used.
func WithCustomSortKeyName ¶
func WithCustomSortKeyName(s string) CreateTableOption
WithCustomSortKeyName changes the sort key name of the table. If not specified, the table will only have a partition key.
type GetOptions ¶
type GetOptions func(*getLockOptions)
GetOptions allows to configure lock reads.
func WithSortKeyName ¶
func WithSortKeyName(s string) GetOptions
WithSortKeyName defines the sort key necessary to load the lock content.
type Lock ¶
type Lock struct {
// contains filtered or unexported fields
}
Lock item properly speaking.
func (*Lock) AdditionalAttributes ¶
func (l *Lock) AdditionalAttributes() map[string]*dynamodb.AttributeValue
AdditionalAttributes returns the lock's additional data stored during acquisition.
func (*Lock) AmIAboutToExpire ¶
AmIAboutToExpire returns whether or not the lock is entering the "danger zone" time period.
It returns if the lock has been released or the lock's lease has entered the "danger zone". It returns false if the lock has not been released and the lock has not yet entered the "danger zone"
type LockNotGrantedError ¶
type LockNotGrantedError struct {
// contains filtered or unexported fields
}
LockNotGrantedError indicates that an AcquireLock call has failed to establish a lock because of its current lifecycle state.
func (*LockNotGrantedError) Error ¶
func (e *LockNotGrantedError) Error() string
type Logger ¶
type Logger interface {
Println(v ...interface{})
}
Logger defines the minimum desired logger interface for the lock client.
type ReleaseLockOption ¶
type ReleaseLockOption func(*releaseLockOptions)
ReleaseLockOption provides options for releasing a lock when calling the releaseLock() method. This class contains the options that may be configured during the act of releasing a lock.
func WithDataAfterRelease ¶
func WithDataAfterRelease(data []byte) ReleaseLockOption
WithDataAfterRelease is the new data to persist to the lock (only used if deleteLock=false.) If the data is null, then the lock client will keep the data as-is and not change it.
func WithDeleteLock ¶
func WithDeleteLock(deleteLock bool) ReleaseLockOption
WithDeleteLock defines whether or not to delete the lock when releasing it. If set to false, the lock row will continue to be in DynamoDB, but it will be marked as released.