lease

package
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 22, 2026 License: Apache-2.0 Imports: 6 Imported by: 0

Documentation

Overview

Package lease provides a distributed single-holder lease primitive backed by a SQL table. It lets multiple service pods coordinate so that a periodic task runs on exactly one pod at a time, while the other pods skip the tick.

The typical flow is:

l := lease.New(repo)
err := l.WithLease(ctx, "agent-scheduler", 90*time.Second, func(ctx context.Context) error {
    return scheduler.checkSchedules(ctx)
})

WithLease acquires the lease, runs fn with a context that is cancelled if the lease is lost, renews the lease at ttl/3 while fn runs, and releases it on exit. If another pod currently holds the lease, WithLease returns nil without running fn.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Lease

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

Lease acquires leases on behalf of a single holder identity.

func New

func New(repo Repo) *Lease

New returns a Lease that identifies itself as "{hostname}-{pid}", matching the outbox enqueuer's LockOwner convention.

func NewWithHolder

func NewWithHolder(repo Repo, holder string) *Lease

NewWithHolder is like New but uses an explicit holder identity. Intended for tests that need to simulate multiple pods within a single process.

func (*Lease) Holder

func (l *Lease) Holder() string

Holder returns the identity this Lease uses when acquiring leases.

func (*Lease) WithLease

func (l *Lease) WithLease(ctx context.Context, name string, ttl time.Duration, fn func(context.Context) error) error

WithLease runs fn iff the lease `name` can be acquired. While fn runs the lease is renewed at ttl/3 intervals; if renewal fails (the lease was lost), the context passed to fn is cancelled so fn can bail out. On exit the lease is released.

Returns fn's error if fn ran. Returns nil if another pod holds the lease or acquisition errored — these are not treated as caller-visible failures because the tick will simply re-attempt on the next interval.

type Repo

type Repo interface {
	// Acquire attempts to claim `name` for `holder`, setting expires_at to now()+ttl. Returns true iff the caller now holds the lease. The claim succeeds when either no row exists for `name`, the existing row has expired, or the existing row is already held by the same `holder` (idempotent re-acquire).
	Acquire(ctx context.Context, name, holder string, ttl time.Duration) (bool, error)

	// Renew extends expires_at to now()+ttl iff (name, holder) matches. Returns true iff the renewal succeeded. A false return means the lease was lost (expired and taken by someone else, or manually released) and the caller should stop its in-flight work.
	Renew(ctx context.Context, name, holder string, ttl time.Duration) (bool, error)

	// Release deletes the lease row iff (name, holder) matches. Safe to call even if the caller no longer holds the lease.
	Release(ctx context.Context, name, holder string) error
}

Repo abstracts the persistence backend for a task_leases table. MySQL and PostgreSQL each have per-service implementations wrapping sqlc-generated queries.

Jump to

Keyboard shortcuts

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