Each send is numbered, inside the signed bytes
novox/hq 04-ISSUES/107. The controller already held a per-node lock while it composed and recorded each send; the order existed and was thrown away at the wire. Each send now takes the next number for its node, one higher than the last, under that hold and before the body exists — so the number is inside what the mesh signs, and a replayed older declaration cannot borrow a newer one's. Zero is not sent. A host reads absence as "no order claimed", which is the shape of every declaration before this, so nothing that worked before changes for a machine sent nothing since numbering existed. One subtlety, and it is the one that would have read every machine as behind for ever: the mesh decides a machine is behind by comparing the digest of what it WOULD send against what it DID send, and a number changes the bytes. The read-only comparison composes with the number the machine was LAST sent, not a fresh one, so it is byte for byte what was sent when nothing else changed. Hosts went first and every machine runs one that understands the field.
This commit is contained in:
@@ -1005,3 +1005,34 @@ func (i *Inventory) RecordHostVersion(ctx context.Context, id, version string) e
|
||||
`update node set host_version = $2, last_seen = now() where id = $1`, id, version)
|
||||
return err
|
||||
}
|
||||
|
||||
// NextSequence takes the next number for a declaration to this node, one higher than the last it
|
||||
// was sent (novox/hq 04-ISSUES/107).
|
||||
//
|
||||
// **One statement, so two composers cannot take the same number.** The caller holds the node while it
|
||||
// composes and sends, so in practice there is one; the increment is atomic anyway, because a rule
|
||||
// that is true only while a lock is held somewhere else is a rule nobody can see from here.
|
||||
func (i *Inventory) NextSequence(ctx context.Context, id string) (int64, error) {
|
||||
var n int64
|
||||
err := i.store.Pool().QueryRow(ctx,
|
||||
`update node set sequence = coalesce(sequence, 0) + 1 where id = $1 returning sequence`, id).Scan(&n)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("taking the next sequence for %s: %w", id, err)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Sequence is the number of the last declaration this node was sent, and zero for one sent nothing
|
||||
// since sends were numbered. Read, not taken: what the mesh WOULD send is composed with this, so it
|
||||
// is byte for byte what it DID send when nothing else changed — a comparison that took a fresh number
|
||||
// would read every machine as behind for ever (novox/hq 04-ISSUES/107).
|
||||
func (i *Inventory) Sequence(ctx context.Context, id string) (int64, error) {
|
||||
var n *int64
|
||||
if err := i.store.Pool().QueryRow(ctx, `select sequence from node where id = $1`, id).Scan(&n); err != nil {
|
||||
return 0, fmt.Errorf("reading the sequence of %s: %w", id, err)
|
||||
}
|
||||
if n == nil {
|
||||
return 0, nil
|
||||
}
|
||||
return *n, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user