Documentation
¶
Overview ¶
Package registry indexes the sessions a Host currently holds resident.
IT IS AN OPTIMIZATION AND NOTHING MORE. This index exists so a Host need not read durable state to answer "do I hold this session", and a wrong or stale answer must waste a request at worst. Concretely: a stale entry may cost a caller a round trip, and may never let a stale Host advance the journal, a mutable projection, a continuation or checkpoint pointer, or a command claim. Nothing this package decides is durable, which is what makes a lost, duplicated or reordered registry update survivable.
THE RULE THIS PACKAGE ENFORCES, and it is only half the sentence: the registry may READ and STORE a lease epoch so a caller can compare it, and may never DECIDE anything from one. TestRegistryNeverGatesOnTheLeaseEpoch holds that structurally, by whitelisting the positions the field may appear in rather than by guessing at the shapes a decision can take.
THE OTHER HALF IS NOT ENFORCED HERE AND CANNOT BE. "Every mutating caller separately validates the durable lease epoch" is a rule about CALLERS; this package has none yet, and its API gives a caller nothing to validate against. Whoever writes the first caller owns that half, and should not read the guard below as having covered it.
Index ¶
- type Admission
- type Clock
- type Entry
- type Key
- type Registry
- func (r *Registry) BeginTeardown(key Key, generation uint64) (Entry, bool)
- func (r *Registry) Get(key Key) (Entry, bool)
- func (r *Registry) Insert(key Key, admission Admission) (Entry, bool)
- func (r *Registry) Len() int
- func (r *Registry) MarkReleasing(key Key, generation uint64) (Entry, bool)
- func (r *Registry) RemoveByGeneration(key Key, generation uint64) bool
- func (r *Registry) RemoveStale(before time.Time) []Key
- func (r *Registry) ResumeResident(key Key, generation uint64) (Entry, bool)
- func (r *Registry) Snapshot() []Entry
- func (r *Registry) StopAdmitting(key Key, generation uint64) (Entry, bool)
- func (r *Registry) Touch(key Key, generation uint64) bool
- type ResidencyState
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Admission ¶
type Admission struct {
AgentID sessionwire.AgentID
Target department.LaunchTarget
CompatibilityID department.CompatibilityID
Runtime department.Runtime
LeaseEpoch uint64
}
Admission is what a caller supplies to claim residency for a session.
type Clock ¶
Clock is the time source, so last-activity and staleness are testable without sleeping.
type Entry ¶
type Entry struct {
Key Key
AgentID sessionwire.AgentID
// Target is the LaunchTarget that produced this runtime, kept so
// reconciliation can tell a runtime launched by the current Department from
// one launched by a Department that has since changed.
Target department.LaunchTarget
// CompatibilityID is the runtime build actually launched, which is not
// necessarily Target.CompatibilityID() any more: a target upgraded under a
// resident session still holds a runtime built by the old one. Both are
// stored because the difference is the thing reconciliation acts on.
CompatibilityID department.CompatibilityID
// Runtime is the live handle.
Runtime department.Runtime
// LeaseEpoch is the durable lease epoch this residency was established
// under. It is CARRIED, never consulted: see the package comment.
LeaseEpoch uint64
// Generation is this registry's own monotonic identity for the residency.
// Removal and teardown are keyed by it so a late caller cannot act on a
// residency that has since been replaced under the same Key.
Generation uint64
State ResidencyState
// Accepting reports whether THIS SESSION may take new work. It is NOT a
// function of State: StopAdmitting closes it while the residency is still
// resident, which is the state a warm release passes through between
// closing admission and writing its durable `releasing` observation.
//
// It was a strict function of State until O6.1 — Insert set
// {resident, true} and both MarkReleasing and BeginTeardown set false — and
// the consequence was recorded across this module: a reader consulting
// State and Accepting together would have had one decide every reachable
// case and the other decide none. That is no longer true, and the three
// facts a caller can now tell apart are a resident session that has stopped
// admitting, a releasing one, and a Host-wide drain, which live in three
// different places and have three different repairs.
Accepting bool
LastActivity time.Time
// TeardownOwned reports whether some caller has already claimed teardown.
TeardownOwned bool
}
Entry is a COPY of one registry row. Mutating it changes nothing.
type Key ¶
type Key struct {
TenantID sessionwire.TenantID
SessionID sessionwire.SessionID
}
Key identifies one resident session.
It is (TenantID, SessionID) and NEVER a bare SessionID. SessionIDs are client-supplied opaque strings — Core mints none of them — so two tenants naming a session identically is an ordinary event, not an attack, and a bare-SessionID index would hand one tenant the other's runtime handle. The composite key is the reason that cannot happen, which is why the tests construct the collision rather than asserting the struct's shape.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry is the tenant-aware index of resident sessions.
func (*Registry) BeginTeardown ¶
BeginTeardown claims teardown for a residency. Exactly one caller wins.
The claim is recorded on the entry rather than inferred from the state, because StateDraining is reachable and re-enterable while ownership is a once-only fact: a second caller finding the state already draining must be told it does NOT own the teardown, not that teardown is under way.
func (*Registry) Insert ¶
Insert claims residency for a key. It returns the entry now held and whether THIS call was the one that established it.
A loser is told the residency that ACTUALLY EXISTS rather than an error, so it can act on it without a second lookup — and because the alternative invites a re-read that races with the removal it is trying to avoid.
func (*Registry) MarkReleasing ¶
MarkReleasing moves a residency to StateReleasing and stops it accepting.
func (*Registry) RemoveByGeneration ¶
RemoveByGeneration removes a residency only if the generation still matches.
func (*Registry) RemoveStale ¶
RemoveStale removes every NON-RESIDENT entry last active before the cutoff, and returns what it removed.
Resident sessions are never reaped, however quiet. A session that is holding work and simply has not been touched is not the registry's to end — deciding that would be an index adjudicating residency, which is the whole thing this package promises not to do. Only a session already on its way out, and idle since the cutoff, is swept.
func (*Registry) ResumeResident ¶ added in v0.7.0
ResumeResident returns a residency a release had begun on to StateResident and accepting, under the same generation.
IT IS THE ONE WAY BACK, AND IT IS NARROW ON PURPOSE. A release that has published `releasing` cannot in general be taken back — a Factory may already have stopped routing to it — so nothing here offered this until a warm release found a runtime that REFUSED to release. Such a session never stopped being live under this Host's grant: no tombstone was written and no successor could attach. Leaving it `releasing` made it unanswerable through Factory (no reusable owner, bind and wake refused) until the Host drained. So a warm release may revert, and only that: a residency claimed for teardown or draining is refused, because its owner is the drain and not the warm path.
func (*Registry) Snapshot ¶
Snapshot returns a copy of every entry, ordered so callers see a stable sequence rather than map iteration order.
func (*Registry) StopAdmitting ¶
StopAdmitting closes a residency to new work WITHOUT moving its state.
IT IS NOT A WEAKER MarkReleasing AND MUST NOT BE FOLDED INTO ONE. Warm release stops admission first and marks the residency releasing second, because the reverse order publishes a `releasing` observation while this Host is still accepting commands into the runtime that observation says is going away. The intermediate state is {resident, accepting:false}: the route is still live for the bindings that hold it, and no new work enters.
It is IDEMPOTENT and takes the same generation rule as every other mutating method, so a late warm timer cannot close admission on the residency that REPLACED the one it was armed for.
type ResidencyState ¶
type ResidencyState string
ResidencyState is what the Host is currently doing with a resident session.
const ( // StateResident reports a session held and able to accept work. StateResident ResidencyState = "resident" // StateReleasing reports residency release under way. The session remains // resumable; this is not termination. StateReleasing ResidencyState = "releasing" // StateDraining reports teardown under way, owned by exactly one caller. StateDraining ResidencyState = "draining" )