Two controllers could both act (issue 204), a reconcile's report could overtake the apply after it and the digest decided (issue 267), and a grant could make a second writer of a machine's report. - The lease (internal/lease, ADR 0229): mesh-controller_lease key `holder`, 15 s age, renewed every 5 s by compare-and-set; the epoch is the revision it was taken at. The gate is the clock (stops 3 s before expiry); a refused renewal is a loss and the process exits; a holder that stops gives it back. serve takes it before asserting the bus. Epochs kept in the store (migration 0068 controller_epoch) as a floor: a bucket raised from nothing is compacted past it. Unleased (no epoch, S12 urgent) only when nobody holds it and the bus will not let it be written. A shell command acts under the holder's epoch, or its own lease when none. - Declarations carry `epoch` inside the signed envelope, only to a machine whose latest account carried a report_sequence (mesh-host #35); would-send is composed with the epoch last sent. Allot and the send both pass the gate. - Reports: contract in internal/link/order.go (epoch, sequence, report_sequence, older_than, refused_older). Accounts kept by epoch, then sequence, then report sequence; older refused, counted; unordered reports keep the digest rule. Plans by compare-and-set on a revision, with epoch. Conditions and calls carry the epoch and are not written off the lease. - S12 and S13 (naming the writer by epoch) watched, D5 run; reset of the bucket said. Writers table compiled in and enforced in PermissionsFor; the controller no longer publishes mesh.control.>. A contract per consumed kind, and the empty-on-error lint over the repository. - mesh-host pinned to its main with the epoch in the validator (D1 validates the envelope as sent). Needs mesh-host's genesis lock with the lease grant (mesh-host PR) for TestTheInstallersFirstUserListIsWhatTheControllerWouldCompose.
48 lines
3.2 KiB
Go
48 lines
3.2 KiB
Go
package link
|
||
|
||
// The contract of every message kind the controller consumes (novox/hq to-be 45 Phase 2, ADR 0227 rule
|
||
// 2 "how it is checked": a contract test per consumed message kind in the receiver's repository, and a
|
||
// check listing every consumed subject against the tests that name it).
|
||
//
|
||
// **Each kind says how an older one is told from a newer, and the tests that deliver n, then n−1.** A
|
||
// kind that carries no order says why none is needed — a word whose newest is simply the latest heard,
|
||
// a request answered once. The test beside it fails a kind the controller can be handed without an
|
||
// entry here, and an entry naming a test that does not exist.
|
||
|
||
// Contract is one consumed kind's order.
|
||
type Contract struct {
|
||
// Ordered is how an older message of this kind is refused; empty for a kind that carries no order.
|
||
Ordered string
|
||
// Unordered is why a kind needs no order; empty for an ordered one.
|
||
Unordered string
|
||
// Tests are the tests that deliver the newer and then the older, and assert the older refused.
|
||
Tests []string
|
||
}
|
||
|
||
// Contracts are every kind the controller consumes, by kind.
|
||
var Contracts = map[string]Contract{
|
||
KindReport: {Ordered: "by the epoch and sequence of the declaration it is about, then the node-engine's " +
|
||
"report sequence (order.go); an older account is refused and counted. A report from a node-engine " +
|
||
"that orders nothing is judged by the digest it names (issue 267)",
|
||
Tests: []string{"TestAnAccountOfAnOlderDeclarationIsRefusedByItsSequence",
|
||
"TestAnOlderReportOfTheSameDeclarationIsRefused", "TestAnAccountOfAnOlderDeclarationDoesNotReplaceTheNewer",
|
||
"TestAnAccountIsOlderByEpochThenSequenceThenReportSequence"}},
|
||
KindBuilt: {Ordered: "by the build's ask: an older ask finishing later is recorded and not registered (issue 219)",
|
||
Tests: []string{"TestAnOlderBuildHeardLaterDoesNotReplaceTheNewer"}},
|
||
KindSourceMoved: {Ordered: "by the merge's commit against what was built from it: a merge already acted on " +
|
||
"or older than the last look is history, not a second plan (issues 250, 266)",
|
||
Tests: []string{"TestAMergeOlderThanTheLastLookIsHistory", "TestAMergeMatchesTheSourcesBuiltFromIt"}},
|
||
KindHeartbeat: {Unordered: "a word that the machine is there: the newest heard is the newest said, and one " +
|
||
"lost is the next one"},
|
||
KindToolsHeartbeat: {Unordered: "a word that the node tools are there, as a machine's heartbeat"},
|
||
KindEnrolment: {Unordered: "a request answered once, under a token spent once: a second presentation is " +
|
||
"refused by the token, not by an order (issue 083)",
|
||
Tests: []string{"TestAnEnrolmentMetByAHeldTokenIsAskedToTryAgain"}},
|
||
KindModuleMoved: {Unordered: "the catalogue saying a module's current build moved: acted on by reading the " +
|
||
"catalogue's record, which is the order, so a late one reads the same record"},
|
||
KindCatchUp: {Unordered: "a catalogue asking what it missed: answered from the record, whenever asked"},
|
||
KindProvisioner: {Unordered: "a provider's newest word about a consumer, said again every fifteen minutes " +
|
||
"while it holds (ADR 0224): the condition keeps the last observed, and S8 says when the words stop",
|
||
Tests: []string{"TestAProviderFailingAConsumerBreaksAllWellUntilItRecovers"}},
|
||
}
|