Malachi.Cluster.Lease (malachi v0.8.13)

View Source

The pure state of a lease: a fenced, expiring lock that elects a single holder for the non-idempotent work of rebalancing. It is the deterministic core replicated by LeaseMachine over a dedicated ra cluster (exactly as Malachi.Metadata sits behind MetadataMachine), so every replica reaches the same lease state from the same command log.

A candidate acquire_or_renews the lease; it is granted when the lease is free, already held by that candidate (a renewal), or expired (now >= renew_at + duration_ms). The candidate identity must be a non-nil term (nil is the free-lease sentinel); in practice it is the node. Time is supplied by the caller: LeaseMachine passes the ra leader's system_time - and never read inside apply/3: reading a wall clock there would be non-deterministic and break Raft. So a single clock (the lease cluster's current leader) decides expiry, avoiding the cross-node clock skew a client-supplied time would carry.

fence is a monotonic fencing token: it advances only when the holder changes (a renewal keeps it). A holder carries its token into the work it fences; if the token has since advanced, a stale ex-holder's writes can be rejected: the guard against two simultaneous holders.

Summary

Functions

Applies a lease command at time now (epoch ms, from the ra leader's system_time). Returns {new_state, reply}. Deterministic given now.

The current holder, or nil when the lease is free.

A free lease (no holder, fence 0).

Types

command()

@type command() ::
  {:acquire_or_renew, holder :: term(), duration_ms :: pos_integer()}
  | {:release, holder :: term(), fence :: non_neg_integer()}

reply()

@type reply() ::
  {:ok, fence :: non_neg_integer()} | {:error, {:held, holder :: term()}} | :ok

t()

@type t() :: %Malachi.Cluster.Lease{
  duration_ms: pos_integer() | nil,
  fence: non_neg_integer(),
  holder: term() | nil,
  renew_at: integer() | nil
}

Functions

apply(state, arg, now)

@spec apply(t(), command(), integer()) :: {t(), reply()}

Applies a lease command at time now (epoch ms, from the ra leader's system_time). Returns {new_state, reply}. Deterministic given now.

holder(lease)

@spec holder(t()) :: term() | nil

The current holder, or nil when the lease is free.

new()

@spec new() :: t()

A free lease (no holder, fence 0).