Read what a machine has from its haves: reported-and-current, the bus list, a given value's first good start; drop the digest of a send (migration 0110) (release 6, issue 1945 B8)
This commit is contained in:
+8
-163
@@ -906,16 +906,6 @@ type BusyWait struct {
|
||||
// BusyWaiting is the state of a container left running because it said busy (link.BusyWaiting).
|
||||
const BusyWaiting = "waiting"
|
||||
|
||||
// Waiting says whether any container waits because it said busy.
|
||||
func (d Doing) Waiting() bool {
|
||||
for _, b := range d.Busy {
|
||||
if b.State == BusyWaiting {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// FailedResource is one thing a node could not do.
|
||||
type FailedResource struct {
|
||||
ID string `json:"id"`
|
||||
@@ -1143,11 +1133,10 @@ type Reported struct {
|
||||
Outcome string
|
||||
// At is when it last reported; nil if it never has.
|
||||
At *time.Time
|
||||
// Sent is when the current declaration went to it; nil if nothing ever did.
|
||||
// Sent is not kept: nothing is sent (ADR 0539 §5). Always nil.
|
||||
Sent *time.Time
|
||||
// Current is whether the last report names the declaration last sent — the machine has
|
||||
// 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 is whether the machine has everything the assigner wants of it, read from its haves beside the wants
|
||||
// (cmd/mesh-controller lastReports); false here, as this reads neither.
|
||||
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
|
||||
@@ -1158,67 +1147,12 @@ type Reported struct {
|
||||
Busy []BusyWait
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// At is when the controller sent it to that machine: the node's own send time, which is where that machine's
|
||||
// `apply` duration starts. A walk measures its rest from it, not from its own record of the send, made only after
|
||||
// every machine of the rest was sent (novox/hq issue 582). Zero for a send kept before it was recorded.
|
||||
type SentDeclaration struct {
|
||||
Digest string `json:"digest"`
|
||||
Sequence int64 `json:"sequence,omitempty"`
|
||||
At time.Time `json:"at,omitzero"`
|
||||
// Provisioned is, for each module the send handed to the provisioner, the digest of the templates its plan
|
||||
// named (novox/hq ADR 0349 §4, ADR 0362): the build the machine's provisioner client reports `prov.applied` on.
|
||||
// The machine has applied the send of such a module only once both it and the client reported it.
|
||||
Provisioned map[string]string `json:"provisioned,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
|
||||
var at *time.Time
|
||||
var plans []byte
|
||||
err := i.store.Pool().QueryRow(ctx, `select sent, sequence, sent_at, sent_provisioning from node where name = $1`,
|
||||
name).Scan(&digest, &seq, &at, &plans)
|
||||
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
|
||||
}
|
||||
if at != nil {
|
||||
s.At = at.UTC()
|
||||
}
|
||||
if s.Provisioned, err = provisionedBy(plans); err != nil {
|
||||
return SentDeclaration{}, false, fmt.Errorf("what %s was sent of the provisioner's plans: %w", name, err)
|
||||
}
|
||||
return s, true, nil
|
||||
}
|
||||
|
||||
// LastReports is every machine's last report beside when it was last sent a declaration.
|
||||
// LastReports is every machine's last report: when, what it did, which declaration it was about and what it holds busy. Sent
|
||||
// and Current are not kept here: nothing is sent (ADR 0539 §5), and whether a machine has what is wanted of it is read from
|
||||
// its haves (cmd/mesh-controller lastReports).
|
||||
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,
|
||||
coalesce(r.declared is not null and r.declared <> '' and r.declared = n.sent, false),
|
||||
`select n.name, coalesce(r.outcome, ''), r.at,
|
||||
coalesce(r.declared, ''), coalesce(r.reported_sequence, 0), coalesce(r.busy, '[]')
|
||||
from node n left join node_report r on r.node = n.id
|
||||
order by n.name`)
|
||||
@@ -1230,8 +1164,7 @@ func (i *Inventory) LastReports(ctx context.Context) ([]Reported, error) {
|
||||
for rows.Next() {
|
||||
var r Reported
|
||||
var busy []byte
|
||||
if err := rows.Scan(&r.Node, &r.Outcome, &r.At, &r.Sent, &r.Current, &r.Declared, &r.ReportedSequence,
|
||||
&busy); err != nil {
|
||||
if err := rows.Scan(&r.Node, &r.Outcome, &r.At, &r.Declared, &r.ReportedSequence, &busy); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := json.Unmarshal(busy, &r.Busy); err != nil {
|
||||
@@ -1242,94 +1175,6 @@ func (i *Inventory) LastReports(ctx context.Context) ([]Reported, error) {
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
// AwaitingSince is since when a machine has had something waiting for its next send (novox/hq issue
|
||||
// 275): the oldest assignment on it made after it was last sent, or — when nothing was assigned since —
|
||||
// when it was last sent, or when it joined if it never was. The moment a bound on "not pushed yet" is
|
||||
// read from: every change a push carries happened after the last push, and an assignment is the one
|
||||
// that says when.
|
||||
func (i *Inventory) AwaitingSince(ctx context.Context, name string) (time.Time, error) {
|
||||
return i.chainAwaitingSince(ctx, name)
|
||||
}
|
||||
|
||||
// RecordSentBusUsers keeps a digest of the bus's user list a machine was just sent, by its name
|
||||
// (novox/hq issue 249): whether the machine holding the bus must go first is whether this differs
|
||||
// from the list composed now.
|
||||
func (i *Inventory) RecordSentBusUsers(ctx context.Context, name, digest string) error {
|
||||
_, err := i.store.Pool().Exec(ctx,
|
||||
`update node set sent_bus_users = $2 where name = $1`, name, digest)
|
||||
return err
|
||||
}
|
||||
|
||||
// SentBusUsers is the digest of the bus's user list a machine was last sent, empty for none.
|
||||
func (i *Inventory) SentBusUsers(ctx context.Context, name string) (string, error) {
|
||||
var sent string
|
||||
err := i.store.Pool().QueryRow(ctx,
|
||||
`select sent_bus_users from node where name = $1`, name).Scan(&sent)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return "", nil
|
||||
}
|
||||
return sent, err
|
||||
}
|
||||
|
||||
// Outstanding is the digest of the declaration a machine was last sent, by its name, and empty
|
||||
// for one that has never been sent anything.
|
||||
//
|
||||
// **By name rather than by id**, because the caller is the serving loop and what a node puts in a
|
||||
// report is its name. Asked of one machine rather than read from Waiting's sweep, because it is
|
||||
// asked per message: a report names the declaration it is about, and a report about one the mesh
|
||||
// has already moved past is not acted on (design 25 §3).
|
||||
func (i *Inventory) Outstanding(ctx context.Context, name string) (string, error) {
|
||||
var sent string
|
||||
err := i.store.Pool().QueryRow(ctx,
|
||||
`select coalesce(sent, '') from node where name = $1`, name).Scan(&sent)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
// Not an error worth carrying up: a report from a machine the mesh has no record of has
|
||||
// nothing to be stale against, and whatever is wrong with it is the listener's to say.
|
||||
return "", nil
|
||||
}
|
||||
return sent, err
|
||||
}
|
||||
|
||||
// Waiting is every machine whose declaration has changed since it was last sent one.
|
||||
//
|
||||
// The caller works out what each machine should be now, because only it can — resolution is the
|
||||
// control plane's and this context holds records. What is answered here is the comparison.
|
||||
//
|
||||
// **A machine that has never been sent anything is waiting**, and says so differently: it is not
|
||||
// out of date, it has never been told, and the remedy is the same push while the situation is not
|
||||
// the same at all.
|
||||
func (i *Inventory) Waiting(ctx context.Context, would map[string]string) ([]Machine, error) {
|
||||
rows, err := i.store.Pool().Query(ctx,
|
||||
`select name, coalesce(sent, ''), sent_at from node order by name`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var out []Machine
|
||||
for rows.Next() {
|
||||
var m Machine
|
||||
var at *time.Time
|
||||
if err := rows.Scan(&m.Node, &m.Sent, &at); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.SentAt = at
|
||||
wanted, known := would[m.Node]
|
||||
if !known {
|
||||
// Nothing was computed for it — it resolves to nothing, or the caller did not ask.
|
||||
// Silence rather than a guess: saying "waiting" about a machine nobody worked out
|
||||
// would be inventing a comparison.
|
||||
continue
|
||||
}
|
||||
if m.Sent == wanted {
|
||||
continue
|
||||
}
|
||||
m.Never = m.Sent == ""
|
||||
out = append(out, m)
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
// Machine is one machine that has not been sent what it should be.
|
||||
type Machine struct {
|
||||
Node string
|
||||
|
||||
Reference in New Issue
Block a user