A gate judges its own send and the build it sent, and never puts the controller back behind its store (hq issue 352)
On 2026-10-09 a release's gate on the control node read the machine's
report against a newer send another plan had just made there, failed
three builds the machine had reported healthy, and put them back on
every machine to a controller older than the store's schema; that
controller then passed the newer plan's gate from its own health.
- A gate keeps what its send carried (digest, sequence) and reads the
report against it; a report on the last send is on it too.
- A gate judges only the build the machine was last sent: another build
there supersedes the judging — no verdict, nothing put back.
- A controller is told its build (MESH_CONTROLLER_VERSION, ${version}
in a process's env) and records how far it reads the store's schema;
a put-back to a build that reaches less, or never said, is refused
and the current build kept, said as urgent.
- A release's open gate holds other sends of its modules there, and a
plan's own first send waits on it.
This commit is contained in:
@@ -1054,13 +1054,57 @@ type Reported struct {
|
||||
// acted on the current words, not merely spoken after they were written. False also covers
|
||||
// a machine that has not said which, which is every host from before reports carried it.
|
||||
Current bool
|
||||
// Declared is the digest of the declaration the last report was about, and ReportedSequence that
|
||||
// declaration's sequence as the report claimed it (zero from an engine that claims none): what a
|
||||
// gate holds against the send it made, rather than against the send made last (novox/hq issue 352).
|
||||
Declared string
|
||||
ReportedSequence int64
|
||||
}
|
||||
|
||||
// SentDeclaration is what one send carried to a machine, as a gate keeps it: the declaration's digest and
|
||||
// its sequence (novox/hq issue 352). A report about this declaration, or about one sequenced after it, is a
|
||||
// report on what the gate sent — whatever the machine was sent since.
|
||||
type SentDeclaration struct {
|
||||
Digest string `json:"digest"`
|
||||
Sequence int64 `json:"sequence,omitempty"`
|
||||
}
|
||||
|
||||
// ReportsOn says a report is about this send: the declaration itself; one the same machine was sequenced
|
||||
// after it; or the declaration the machine was sent last (Current), which is this send or a later one —
|
||||
// sends to a machine are made one after another. A send kept without a sequence is matched by its digest
|
||||
// and by the last send alone.
|
||||
func (s SentDeclaration) ReportsOn(r Reported) bool {
|
||||
if r.Current || (s.Digest != "" && r.Declared == s.Digest) {
|
||||
return true
|
||||
}
|
||||
return s.Sequence > 0 && r.ReportedSequence >= s.Sequence
|
||||
}
|
||||
|
||||
// SentTo is the declaration a machine was last sent, by name: its digest and sequence, and false when it
|
||||
// was never sent one.
|
||||
func (i *Inventory) SentTo(ctx context.Context, name string) (SentDeclaration, bool, error) {
|
||||
var digest *string
|
||||
var seq *int64
|
||||
err := i.store.Pool().QueryRow(ctx, `select sent, sequence from node where name = $1`, name).Scan(&digest, &seq)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return SentDeclaration{}, false, fmt.Errorf("%w: %s", ErrNoSuchNode, name)
|
||||
}
|
||||
if err != nil || digest == nil || *digest == "" {
|
||||
return SentDeclaration{}, false, err
|
||||
}
|
||||
s := SentDeclaration{Digest: *digest}
|
||||
if seq != nil {
|
||||
s.Sequence = *seq
|
||||
}
|
||||
return s, true, nil
|
||||
}
|
||||
|
||||
// LastReports is every machine's last report beside when it was last sent a declaration.
|
||||
func (i *Inventory) LastReports(ctx context.Context) ([]Reported, error) {
|
||||
rows, err := i.store.Pool().Query(ctx,
|
||||
`select n.name, coalesce(r.outcome, ''), r.at, n.sent_at,
|
||||
r.declared is not null and r.declared <> '' and r.declared = n.sent
|
||||
r.declared is not null and r.declared <> '' and r.declared = n.sent,
|
||||
coalesce(r.declared, ''), coalesce(r.reported_sequence, 0)
|
||||
from node n left join node_report r on r.node = n.id
|
||||
order by n.name`)
|
||||
if err != nil {
|
||||
@@ -1070,7 +1114,7 @@ func (i *Inventory) LastReports(ctx context.Context) ([]Reported, error) {
|
||||
var out []Reported
|
||||
for rows.Next() {
|
||||
var r Reported
|
||||
if err := rows.Scan(&r.Node, &r.Outcome, &r.At, &r.Sent, &r.Current); err != nil {
|
||||
if err := rows.Scan(&r.Node, &r.Outcome, &r.At, &r.Sent, &r.Current, &r.Declared, &r.ReportedSequence); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, r)
|
||||
|
||||
Reference in New Issue
Block a user