An empty declaration is sent, so a node drops what it last held (hq 127)

push skipped any node whose declaration composed to zero resources. A
node that HELD something before — the broker opening a placement gave
an adopted node, say — then kept it forever: the empty declaration that
would drop it was never sent, and the node's own heartbeat re-applied
the stale resource with no way for the mesh to say it is gone. Now the
empty declaration is sent; the host drops what the mesh owned and keeps
what it found. A node that never held anything applies it as a no-op.
Surfaced on ace: the foundation-opening fix (#74) removed its only
resource, and the correction could not reach it until this.
This commit is contained in:
2026-09-26 23:32:30 +02:00
parent 98d348d5b9
commit 0a8a592ef4
2 changed files with 14 additions and 6 deletions
+8 -2
View File
@@ -502,8 +502,14 @@ func composeEach(names []string,
continue
}
if len(declared.Resources) == 0 {
fmt.Printf("%s is assigned nothing — skipped\n", name)
continue
// Sent, not skipped (novox/hq issue 127). A node whose declaration composes to
// nothing may have HELD something before — the broker opening a placement gave it,
// say — and skipping the empty declaration leaves that last resource in force
// forever, re-applied by the node's own heartbeat, with no way for the mesh to say
// it is gone. An empty declaration is the correction: the host drops what the mesh
// owned and keeps what it found (the adoption envelope still rides along). A node
// that never held anything applies it as the no-op it is.
fmt.Printf("%s owns nothing now — sent so it drops what it last held\n", name)
}
sending = append(sending, readyNode{name, declared})
}
+6 -4
View File
@@ -39,12 +39,14 @@ func TestOneUnresolvableNodeStillLetsTheRestBeSent(t *testing.T) {
}
}
// And a machine assigned nothing is neither sent nor a refusal — it is nothing to say.
func TestAMachineAssignedNothingIsNotARefusal(t *testing.T) {
// A machine whose declaration composes to nothing is SENT the empty declaration, not skipped
// (novox/hq issue 127): it may have held something before, and only sending the empty
// declaration tells it to drop what the mesh owned. It is never a refusal.
func TestAnEmptyDeclarationIsSentSoTheNodeDropsWhatItHeld(t *testing.T) {
sending, refusals := composeEach([]string{"spare"},
func(string) (sendable, error) { return sendable{}, nil })
if len(sending) != 0 || len(refusals) != 0 {
t.Errorf("a machine assigned nothing was treated as something: %v / %v", sending, refusals)
if len(sending) != 1 || len(refusals) != 0 {
t.Errorf("an empty declaration must be sent, not skipped or refused: %v / %v", sending, refusals)
}
}