Apply only the node's own compositions, never the controller's
mesh/delivery merged: source.main moved to novox/mesh-host@84737f4cb26d
mesh/check being carried to 38d666f22390 on 84737f4cb26d | from 14a12cc7b6f9
mesh/delivery merged: source.main moved to novox/mesh-host@84737f4cb26d
mesh/check being carried to 38d666f22390 on 84737f4cb26d | from 14a12cc7b6f9
The controller ended by its unassignment, so a node that falls back to the compositions in its bucket follows what nobody writes (issue 1126, A7). The node agent now always runs its own composer: without a placement key it composes nothing and keeps what runs, a placement it cannot watch stops the agent and is said, and a candidate taking its placement away is refused instead of answered as a return to the controller. The start-time placement read, the end-when-placed watchers and the returning answer go, with their tests and the three integration tests of the controller-composed path. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
@@ -798,8 +798,8 @@ func (c *Composer) compose(ctx context.Context) []agent.Update {
|
||||
r := reader{c: c}
|
||||
c.mu.Unlock()
|
||||
if !placed {
|
||||
// The catalogue no longer places it: the agent returns to the controller's compositions (endWhenUnplaced), and
|
||||
// this is no refusal of what it runs (issue 1126, the seed's revert).
|
||||
// The catalogue does not place it: it composes nothing, applies no composition made elsewhere, and this is no
|
||||
// refusal of what it runs (issue 1126).
|
||||
c.sayOnce("this node's key in the seat's placement state is gone: it composes nothing of its own, and keeps running what it runs")
|
||||
c.paid() // nothing of its own to compose, so no pass is owed and no timer armed again
|
||||
return nil
|
||||
@@ -1496,74 +1496,6 @@ func (r reader) Credentials(ctx context.Context, node string, asConsumer, asProv
|
||||
return creds, issued, nil
|
||||
}
|
||||
|
||||
// Placed reads whether the catalogue places node now, for the node agent choosing whose compositions it applies at
|
||||
// each start: true when its placement key holds a value, false only when a read succeeds and finds no key or a delete
|
||||
// marker. A read that fails chooses neither (review of the shadow, round 1: a timeout or a bus not ready at a restart
|
||||
// flipped a self-composing node to the controller's compositions for a whole run): it is tried again after wait(try),
|
||||
// each distinct failure said once, until ctx ends, which is then its error.
|
||||
//
|
||||
// switched says the node composed from its own placement key before (its have says it composes its own,
|
||||
// delivery.ComposesOwn, and that is kept on the bus, so it survives a restart). Only a node that never switched takes a
|
||||
// read that fails (a timeout, or a state its account may not read yet, as the seat's before its grant arrives) as "not
|
||||
// placed": it stays on the controller's compositions, says why once, and switches on a positive read of its key (the
|
||||
// watch endWhenPlaced keeps). A node that switched never goes back on a failed read, however long it lasts.
|
||||
func Placed(ctx context.Context, get func(ctx context.Context, bucket, key string) ([]byte, error), node string,
|
||||
wait func(try int) time.Duration, say func(string), switched bool) (bool, error) {
|
||||
key, err := delivery.PlacementNodeKey(node)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
legacy, _ := delivery.LegacyPlacementKey(node)
|
||||
said := ""
|
||||
for try := 1; ; try++ {
|
||||
// Its node key, or, before the seat's holder converted it, its legacy key (research 072 row 37).
|
||||
raw, err := get(ctx, delivery.PlacementBucket, key)
|
||||
if err == nil && len(raw) == 0 {
|
||||
raw, err = get(ctx, delivery.PlacementBucket, legacy)
|
||||
}
|
||||
if errors.Is(err, nats.ErrBucketNotFound) {
|
||||
return false, nil // the seat has written no placement state yet: nothing places it, read and known
|
||||
}
|
||||
if err == nil {
|
||||
return len(raw) > 0, nil
|
||||
}
|
||||
if ctx.Err() != nil {
|
||||
return false, ctx.Err()
|
||||
}
|
||||
if !switched {
|
||||
say(fmt.Sprintf("this node's key in the seat's placement state cannot be read (%s): it has never composed its own, so it applies the controller's compositions and switches when its key is read: %v", key, err))
|
||||
return false, nil
|
||||
}
|
||||
if why := err.Error(); why != said {
|
||||
say(fmt.Sprintf("this node's key in the seat's placement state cannot be read (%s): the node agent waits, choosing neither its own compositions nor the controller's: %v", key, err))
|
||||
said = why
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return false, ctx.Err()
|
||||
case <-time.After(wait(try)):
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ComposedItsOwn says whether the node's own have, kept on the bus, says it composed from its placement key at its last
|
||||
// start. A have that cannot be read says yes: the safe side, a node waits and does not fall back.
|
||||
func ComposedItsOwn(ctx context.Context, get func(ctx context.Context, bucket, key string) ([]byte, error), node string) bool {
|
||||
key, err := delivery.HaveNodeKey(node)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
raw, err := get(ctx, delivery.HaveBucket, key)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
if len(raw) == 0 {
|
||||
return false
|
||||
}
|
||||
n, err := delivery.DecodeHaveNode(raw)
|
||||
return err != nil || n.Composes == delivery.ComposesOwn
|
||||
}
|
||||
|
||||
// composeAgain are the waits before a pass that met a bus not answering is tried again.
|
||||
var composeAgain = []time.Duration{time.Second, 2 * time.Second, 4 * time.Second}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user