postgres

package
v0.14.2 Latest Latest
Warning

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

Go to latest
Published: Jul 10, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	// TTL is the lease duration for each EID lock row.
	TTL time.Duration `yaml:"ttl"`
	// AcquireBackoff is the wait between retry attempts when a lock is contended.
	AcquireBackoff time.Duration `yaml:"acquireBackoff"`
	// AcquireDeadline is the total time allowed to acquire all EID locks.
	AcquireDeadline time.Duration `yaml:"acquireDeadline"`
	// Heartbeat is the interval at which held leases are renewed (~TTL/3).
	Heartbeat time.Duration `yaml:"heartbeat"`
	// Owner identifies this replica. Defaults to the FSC node ID when empty.
	Owner string `yaml:"owner"`
}

Config holds Postgres lease-table locking settings.

type Locker

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

Locker implements locker.Locker using a SQL lease table. Acquire and renew queries use Postgres-specific features (TIMESTAMPTZ, ON CONFLICT DO UPDATE … RETURNING, ::interval casts).

func New

func New(db *sql.DB, table string, cfg Config, replicaID id.ReplicaIDProvider) (*Locker, error)

New creates a Postgres-backed distributed Locker. The table is created if it does not exist. db must be a *sql.DB connected to Postgres.

func (*Locker) AcquireLocks

func (p *Locker) AcquireLocks(ctx context.Context, anchor string, eIDs ...string) error

AcquireLocks claims a lease on every enrollment ID in eIDs for anchor, across all replicas sharing the table.

Implementation: the IDs are deduplicated and sorted (dedup.AndSort) for the same deadlock-free ordering the in-memory locker relies on. It then retries tryAcquireAll — a single atomic upsert that succeeds only if it can claim every ID — sleeping AcquireBackoff between attempts and giving up with a contention error once AcquireDeadline passes (or ctx is cancelled). On success it records the held IDs under anchor and starts a background heartbeat that renews the leases before they expire, so a long-running audit keeps its locks while a crashed replica's leases expire and become claimable by others. Any partial state is released on the give-up/cancel paths.

func (*Locker) AssertLocksHeld

func (p *Locker) AssertLocksHeld(ctx context.Context, anchor string) error

AssertLocksHeld verifies this replica still holds every lease it acquired for anchor. It compares the number of IDs recorded locally at acquisition time against the count of matching, non-expired, owner-scoped rows in the table. A mismatch (or no local record) means a lease expired and may have been taken over by another replica, so it returns ErrLockNotHeld. Callers use this after long-running work to confirm their locks were not silently lost.

func (*Locker) ReleaseLocks

func (p *Locker) ReleaseLocks(ctx context.Context, anchor string)

ReleaseLocks releases all leases held under anchor: it stops the background heartbeat for that anchor and deletes the corresponding rows from the table.

Jump to

Keyboard shortcuts

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