Defines docket_tenant_fair_claim, the database-side TenantFair
admission engine.
One call accepts a point in time, expired-claim cutoff, requested outcome count, poison-attempt threshold, optional ready/expired preference, and trace flag. The function returns newly claimed leases, poisoned runs, or a normalized pre-admission error.
Scheduling model
docket_claim_schedule is a stable ring of claim partitions. A schedule
row participates while unfinished_count > 0; membership therefore covers
ready work, claimed work, future timers, and other nonterminal runs rather
than only work that can produce an outcome immediately.
The singleton policy row stores scan_ring_position, the position inspected
by the last committed TenantFair call. A call starts at its absolute
successor, wraps to the smallest positive position when necessary, and
materializes exactly the next S = 32 visits when demand remains. If the
positive ring contains fewer than 32 partitions, traversal wraps repeatedly
and may visit the same partition more than once. An empty ring produces no
visits and leaves the cursor unchanged.
Entering a visit advances the call-local cursor. The visit counts even when partition authority is unavailable, the partition is capped, its work is dormant, its bounded candidate page is empty, every exact run lock is skipped, or every locked row fails authoritative recheck. If demand becomes satisfied, prefetched but unentered visits do not advance the cursor.
Each entered visit targets one partition and produces at most one grant. A
grant contains between one and Q = 8 outcomes, limited further by the
caller's remaining demand. Only a nonempty grant increments that
partition's admission_epoch, and it increments the epoch exactly once
regardless of the number of outcomes in the grant.
Policy and partition authority
The function requires a writable Read Committed transaction. It preserves
a lower caller lock_timeout; otherwise it limits lock waits throughout
the function to 250 ms.
It locks the singleton policy row, verifies that backend startup persisted
an initialized TenantFair default, and never changes the cap or active
engine. That lock serializes committed cursor movement and remains held
until the surrounding transaction ends. A timeout during this narrow
readiness/cursor phase returns lock_contention without inspecting a
partition or changing policy, schedules, epochs, or runs. Later errors
abort the statement.
For every entered visit, the function attempts the exact partition with
FOR NO KEY UPDATE SKIP LOCKED. Under that authority it freshly counts
healthy running rows carrying the durable tenant_admitted_at marker.
The effective cap is the partition override or persisted default;
promotion capacity is the nonnegative difference between that cap and the
admitted count.
Ready and expired candidates
Ready work is an unclaimed running row whose wake_at is due. An admitted
ready row already has tenant_admitted_at and may reacquire a transient
claim at or above the cap. A queued ready row has no marker; its FIFO-head
promotion atomically installs both the marker and a claim and requires a
free logical-run slot. Cap debt blocks promotion, not reacquisition.
Expired work is a claimed running row whose claimed_at precedes the
cutoff. Replacing its claim token is a count-neutral steal, so ordinary
expired work remains admissible at or above the cap.
A ready or expired row at the attempt threshold becomes a poison outcome. Poisoning clears scheduling, claim, and admission state and returns no claim token. An admitted poison can therefore retire a cohort member at the cap. A queued poison must itself be the FIFO head and requires a free promotion slot; it never bypasses older ordinary queued work.
The function reads a shared page of at most K = 16 structural ready
candidates: admitted-ready rows first, then the oldest queued-ready rows
that fit the residual page. It separately reads at most 16 expired
candidates. From their bounded union
it ranks and freezes at most 16 exact run IDs. Demand-one calls put the
requested class head first and retain the other class as fallback. Calls
with demand of at least two reserve service for both ready and expired work
until each class produces a committed outcome. If only one final demand
slot remains while a class is still unserved, the other class cannot consume
it; the call may therefore intentionally return one fewer outcome.
The frozen attempt set first retains each unserved class head when demand
is at least two, so either class cannot disappear at the K truncation,
then prefers admitted work and applies demand-one preference. Mutation
still orders admitted work before promotion. Queued rows retain exact
(wake_at, id) FIFO order even when a later queued row is poison. The
function locks only those exact IDs with FOR UPDATE SKIP LOCKED. A skipped
or stale ID is not replaced by structural row 17 or any ID outside the
frozen set during that visit.
After locking, every row is rechecked against the frozen scope, ready or expired class, poison class, running/poisoned state, claim state, wake or cutoff time, and remaining promotion capacity. Only rows that still satisfy all authoritative predicates are mutated. Queue mutation also requires the next contiguous frozen queue ordinal and an authoritative absence check for an older currently visible unadmitted head. A locked or stale head therefore blocks every later promotion in that visit. TenantFair never rotates queued discovery.
Lock and write order
Authority is acquired in this order:
policy cursor -> partition -> frozen run IDsThe function never locks a schedule row and then seeks another run. Policy, run, epoch, and cursor changes are part of the caller's transaction and roll back together.
Results and trace mode
Outcome and error rows begin with the stable fourteen columns decoded by
TenantFair. The raw record appends a per-call token, transaction ID, visit
and outcome ordinals, demand, cursor transition, ring identity,
disposition, outcome count, and epoch delta.
Production calls the function with trace disabled. TenantFair.SQL removes
the internal columns and explicitly orders public outcomes by visit and
outcome ordinal. Trace-enabled calls additionally receive one inspection
row per entered visit, including skips, denials, stale pages, empty pages,
and grants. Trace rows are returned only to the caller and are never stored
in a trace table or emitted as identity-bearing metric labels.