Act under a lease, keep accounts by order, one writer at composition (hq to-be 45 Phase 2)
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.
This commit is contained in:
@@ -73,7 +73,25 @@ type signalFacts struct {
|
||||
|
||||
selfCheck selfCheckFacts
|
||||
|
||||
staleRefusals map[string]int
|
||||
// staleRefusals are the writers refused as older lately, and epochs the mesh's record of each epoch
|
||||
// they name (novox/hq to-be 45 §6, S13).
|
||||
staleRefusals []link.WriterRefusals
|
||||
epochs map[int64]inventory.Epoch
|
||||
|
||||
// lease is this controller's standing to the lease, and the epochs that ended lately (S12).
|
||||
lease leaseFacts
|
||||
leaseErr error
|
||||
}
|
||||
|
||||
type leaseFacts struct {
|
||||
held bool
|
||||
epoch uint64
|
||||
renewed time.Time
|
||||
unleased string
|
||||
ended []inventory.Epoch
|
||||
// reset is when the lease bucket was found raised again from nothing; resetSaid what of it.
|
||||
reset time.Time
|
||||
resetSaid string
|
||||
}
|
||||
|
||||
type machineFacts struct {
|
||||
@@ -246,12 +264,23 @@ func blindRow(row signalRow, err error) conditions.Observation {
|
||||
func (w *watchdogs) gather(ctx context.Context) *signalFacts {
|
||||
now := time.Now()
|
||||
f := &signalFacts{now: now, started: w.started, toolsHeardFrom: link.ToolsBeats.Started(), calls: link.Calls.Running(),
|
||||
staleRefusals: link.StaleRefusals.Within(now.Add(-staleRefusalsWithin)), lostConsumers: map[string]bool{}}
|
||||
staleRefusals: link.StaleRefusals.Within(now.Add(-staleRefusalsWithin)), lostConsumers: map[string]bool{},
|
||||
epochs: map[int64]inventory.Epoch{}}
|
||||
if w.doctor != nil {
|
||||
f.selfCheck = selfCheckFacts{last: w.doctor.lastRunEnded(), every: doctorEvery}
|
||||
}
|
||||
inv := w.open.inventory
|
||||
f.host = controlHost(ctx, inv)
|
||||
f.lease, f.leaseErr = gatherLease(ctx, inv, now)
|
||||
for _, r := range f.staleRefusals {
|
||||
if r.Epoch <= 0 {
|
||||
continue
|
||||
}
|
||||
// Named where the record has it; a writer the record cannot name is still said by its epoch.
|
||||
if e, found, err := inv.EpochOf(ctx, uint64(r.Epoch)); err == nil && found {
|
||||
f.epochs[r.Epoch] = e
|
||||
}
|
||||
}
|
||||
f.machines, f.machinesErr = w.gatherMachines(ctx, inv, now)
|
||||
f.plans, f.plansErr = gatherPlans(ctx, inv, now)
|
||||
f.loop, f.loopErr = w.gatherLoop()
|
||||
@@ -270,6 +299,19 @@ func (w *watchdogs) gather(ctx context.Context) *signalFacts {
|
||||
return f
|
||||
}
|
||||
|
||||
// gatherLease is this controller's standing to the lease and the epochs that ended within the hour.
|
||||
func gatherLease(ctx context.Context, inv *inventory.Inventory, now time.Time) (leaseFacts, error) {
|
||||
st := theLease.standing()
|
||||
f := leaseFacts{held: st.Held, epoch: st.Epoch, renewed: st.Renewed, unleased: st.Unleased, reset: st.Reset,
|
||||
resetSaid: st.ResetSaid}
|
||||
ended, err := inv.EpochsSince(ctx, now.Add(-advisoryQuiet))
|
||||
if err != nil {
|
||||
return f, fmt.Errorf("the epochs the mesh issued cannot be read: %w", err)
|
||||
}
|
||||
f.ended = ended
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// controlHost is the machine running the controller, as the mesh names it: the one the controller
|
||||
// module is assigned to, or this process's host name where that is not one machine.
|
||||
func controlHost(ctx context.Context, inv *inventory.Inventory) string {
|
||||
|
||||
Reference in New Issue
Block a user