lease

package module
v0.2.1 Latest Latest
Warning

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

Go to latest
Published: Dec 29, 2025 License: MIT Imports: 5 Imported by: 0

README

Lease

Go Reference Go Report Card Tests Coverage Status

This is lease, a Go library for concurrent, possibly distributed timed mutual-exclusion locks and leader elections.

Usage

In all cases you’ll need a Provider, which provides leases. This library includes two implementations of Provider (with more to come): an in-memory version and a Postgresql version.

Acquiring a lease:

secret, err := provider.Acquire(ctx, "leaseName", expirationTime)
if err != nil { ... }
defer provider.Release(ctx, "leaseName", secret)

Renewing an already-acquired lease:

err := provider.Renew(ctx, "leaseName", newExpirationTime)
if err != nil { ... }

Running a function after winning a leader election:

leader := lease.Leader{
  Name:   "leaseName",
  Dur:    5*time.Minute,
  Retry:  time.Minute,
  Jitter: 5*time.Second,
  Renew:  4*time.Minute,
}
err := leader.Run(ctx, provider, func(ctx context.Context) error {
  fmt.Println("I am the leader")
  ...
})
if err != nil { ... }

Documentation

Overview

Package lease defines a Provider that supplies leases, which are timed mutual-exclusion locks. It also defines Leader, a simple leader-election mechanism.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrHeld is the error returned by [Provider.Acquire] when the lease is already held by another caller.
	ErrHeld = errors.New("lease already held by another caller")

	// ErrNotHeld is the error returned by [Provider.Renew] or [Provider.Release] when the lease is not held by the caller.
	ErrNotHeld = errors.New("lease not held by caller")
)

Functions

This section is empty.

Types

type CallbackError added in v0.2.0

type CallbackError struct {
	Err error
}

CallbackError is a wrapper for the error returned by the callback function to Leader.Run.

func (CallbackError) Error added in v0.2.0

func (e CallbackError) Error() string

func (CallbackError) Unwrap added in v0.2.0

func (e CallbackError) Unwrap() error

type Clock

type Clock interface {
	Now() time.Time
	After(time.Duration) <-chan time.Time
}

Clock is an interface used by Provider for time-based operations. It can be replaced with a mock implementation for testing purposes. Note that one good mock implementation is in github.com/benbjohnson/clock, qv.

type DefaultClock

type DefaultClock struct{}

DefaultClock implements the Clock interface in terms of the stdlib time.

func (DefaultClock) After

func (DefaultClock) After(d time.Duration) <-chan time.Time

func (DefaultClock) Now

func (DefaultClock) Now() time.Time

type Leader

type Leader struct {
	Name   string        // name for the lease to acquire
	Dur    time.Duration // how long the lease should be valid for
	Retry  time.Duration // how often to retry acquiring the lease
	Jitter time.Duration // plus or minus this much jitter on the retry delay
	Renew  time.Duration // how often to renew the lease after acquiring it; should be less than Dur
}

Leader permits running a function after winning a leader election. See Leader.Run.

func (Leader) Run

func (l Leader) Run(ctx context.Context, p Provider, f func(context.Context) error) (bool, error)

Run runs a function after winning a leader election.

The election happens by trying to acquire a lease from the given Provider using the Name field of l. If the lease is already held by another caller, Run will retry indefinitely at l.Retry intervals (plus or minus up to l.Jitter), until the context is canceled or the lease is acquired.

Once the lease is acquired, Run will renew it periodically at l.Renew intervals.

The provided function f is run with a context that is canceled if the lease cannot be renewed. If this happens, context.Cause will return a RenewError wrapping the error from [Provider.Renew].

The boolean result from Run indicates whether f was ever called. If f was called and returned an error, that error is wrapped in a CallbackError and returned by Run. (That that may be a RenewError wrapping yet another error, if f encountered it and chose to return it.)

type Provider

type Provider interface {
	Clock

	// Acquire acquires a lease if available and returns a secret, needed for Renew and Release.
	// The lease expires at the given time,
	// or at the deadline of the provided context (if it has one), whichever is earlier.
	// Acquire does not wait; if the lease is already held by another caller, it returns [ErrHeld].
	Acquire(context.Context, string, time.Time) (string, error)

	// Renew extends the lease for the given name, using the provided secret.
	// Its expiration time is reset to the given time
	// or the deadline of the provided context (if it has one), whichever is earlier.
	// If the lease is not held by the caller, it returns [ErrNotHeld].
	Renew(ctx context.Context, name, secret string, exp time.Time) error

	// Release releases the lease for the given name, using the provided secret.
	// If the lease is not held by the caller, it returns [ErrNotHeld].
	Release(ctx context.Context, name, secret string) error
}

Provider is the type of a lease provider.

type RenewError

type RenewError struct {
	Err error
}

RenewError is a wrapper for the error from [Provider.Renew] when the lease in Leader.Run cannot be renewed.

func (RenewError) Error

func (e RenewError) Error() string

func (RenewError) Unwrap

func (e RenewError) Unwrap() error

Directories

Path Synopsis
Package mem implements lease.Provider in memory.
Package mem implements lease.Provider in memory.
Package pg implements lease.Provider in terms of a PostgresQL database.
Package pg implements lease.Provider in terms of a PostgresQL database.

Jump to

Keyboard shortcuts

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