halease

package
v1.0.21 Latest Latest
Warning

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

Go to latest
Published: Jul 4, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package halease is the HA fencing-lease primitive (ADR-0005 S1): a single cluster-wide lease with a strictly-monotonic epoch (the fencing token), arbitrated by a strongly-consistent backend. etcd is the default implementation (Etcd); Fake is the in-memory implementation the Culvert-side HA logic is unit-tested against.

S1 ships the primitive ONLY — nothing in the runtime consumes it yet. S2 wires Acquire into promotion, the keepalive loop, and self-fence; S3 stamps the epoch on every write sink; S5 adds flags/compose/GUI. Policy decisions (e.g. whether a clean shutdown releases the lease) deliberately live in those slices, not here.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	Endpoints   []string
	TLS         *tls.Config   // nil = plaintext (operator's call; S5 wires flags)
	TTL         time.Duration // lease TTL; rounded up to whole seconds, min 1s; default 10s
	DialTimeout time.Duration // default 5s
}

Config configures the etcd-backed Provider.

type Etcd

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

Etcd is the etcd-backed Provider (ADR-0005 S1 default implementation).

func NewEtcd

func NewEtcd(cfg Config) (*Etcd, error)

NewEtcd connects to etcd and returns the Provider. It does NOT touch the leader key — connection only.

func (*Etcd) Acquire

func (e *Etcd) Acquire(ctx context.Context, candidateID string) (bool, Status, error)

Acquire implements Provider: a single transaction that puts the lease-bound key iff it does not exist. etcd deletes the key when the old holder's lease expires, so "free or expired" is exactly create_revision == 0.

func (*Etcd) Close

func (e *Etcd) Close() error

Close implements Provider: closes the client connection WITHOUT revoking a held lease (release-on-shutdown is S2 policy; an unrevoked lease simply expires after its TTL).

func (*Etcd) Read

func (e *Etcd) Read(ctx context.Context) (Status, error)

Read implements Provider.

func (*Etcd) Renew

func (e *Etcd) Renew(ctx context.Context, holderID string, epoch int64) (bool, time.Duration, error)

Renew implements Provider: keepalive our lease, then re-verify the key still carries our (holder, epoch). The re-verify guards the fencing property even if a stray keepalive outlives the key.

type Fake

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

Fake is the in-memory Provider used to unit-test the Culvert-side HA logic (ADR-0005 S1). It implements the same contract the etcd backend does — the shared conformance suite in halease_test.go pins that both stay in agreement. Time is injectable so expiry is deterministic.

func NewFake

func NewFake(ttl time.Duration) *Fake

NewFake builds a Fake with the given lease TTL.

func (*Fake) Acquire

func (f *Fake) Acquire(_ context.Context, candidateID string) (bool, Status, error)

Acquire implements Provider.

func (*Fake) Close

func (f *Fake) Close() error

Close implements Provider (no resources to release).

func (*Fake) ExpireForTest

func (f *Fake) ExpireForTest()

ExpireForTest force-expires the current lease without changing the holder record — the next Acquire wins as if the TTL lapsed.

func (*Fake) Read

func (f *Fake) Read(_ context.Context) (Status, error)

Read implements Provider.

func (*Fake) Renew

func (f *Fake) Renew(_ context.Context, holderID string, epoch int64) (bool, time.Duration, error)

Renew implements Provider.

func (*Fake) SetNowForTest

func (f *Fake) SetNowForTest(now func() time.Time) (prev func() time.Time)

SetNowForTest injects a clock, returning the previous one (deterministic expiry in tests).

type Provider

type Provider interface {
	// Acquire attempts to take the lease for candidateID.
	Acquire(ctx context.Context, candidateID string) (granted bool, st Status, err error)
	// Renew keepalives the lease previously granted to (holderID, epoch).
	Renew(ctx context.Context, holderID string, epoch int64) (ok bool, validFor time.Duration, err error)
	// Read returns the current lease state.
	Read(ctx context.Context) (Status, error)
	// Close releases client resources. It MUST NOT revoke a held lease —
	// release-on-shutdown is an S2 policy decision, not a primitive.
	Close() error
}

Provider is the backend-agnostic fencing-lease interface (ADR-0005). Implementations MUST guarantee:

  • Acquire grants iff the lease is free or expired, and every grant carries an epoch STRICTLY GREATER than any previously granted epoch (the fencing property). When denied, the returned Status describes the current holder.
  • Renew succeeds iff (holderID, epoch) is still the live lease. A lost/expired/superseded lease returns ok=false with a nil error — loss is an outcome, not an error; errors are reserved for transport failures where the truth is UNKNOWN (callers must fail toward self-fence on both, but may retry transport errors within budget).
  • Read never mutates state.

The backend — not the caller's wall clock — is the lease-time authority (ADR-0005 Finding 3).

type Status

type Status struct {
	Holder   string        // current holder's candidate ID ("" = free)
	Epoch    int64         // fencing token; strictly monotonic across the backend's life; 0 = never held
	ValidFor time.Duration // remaining lease time as reported by the backend (0 = free/expired/unknown)
}

Status is a point-in-time view of the lease.

Jump to

Keyboard shortcuts

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