Issues 203 and 206: an assignment issues its credential; the controller owns a worker's shape; the build seat's holder follows the controller #233

Merged
mesh-admin merged 4 commits from fix/issues-203-206 into main 2026-10-03 09:49:54 +00:00
2 changed files with 42 additions and 12 deletions
Showing only changes of commit c294949f2a - Show all commits
+13 -5
View File
@@ -235,14 +235,22 @@ func (j *JetStream) EnsureConsumer(c Consumer) error {
return fmt.Errorf("asking about stream %s to re-make consumer %s: %w", c.Stream, c.Name, err) return fmt.Errorf("asking about stream %s to re-make consumer %s: %w", c.Stream, c.Name, err)
} }
if info.Config.Retention != nats.WorkQueuePolicy { if info.Config.Retention != nats.WorkQueuePolicy {
j.note("consumer %s on %s is %s and should be %s; not re-made, because %s keeps its history "+ // **A stream that keeps its history is re-made from now on, never from the start.**
"and a re-made consumer replays what this one acknowledged (novox/hq issue 156). Re-make it by hand", // Left for a hand, the hand re-makes it with the server's default — everything the
c.Name, c.Stream, shape(havePush), shape(wantPush), c.Stream) // stream holds — which on 2026-10-03 replayed every build ask since 1 October and
return nil // re-registered nine modules from the past (novox/hq issue 207). What this consumer
} // had not yet acknowledged is lost with it, and said: on a history stream that is
// the smaller cost, and the asks in flight are visible to whoever asked.
j.note("consumer %s on %s changes from %s to %s delivery on a stream that keeps its history: "+
"re-made to deliver from now on, so nothing this one acknowledged comes back (novox/hq issue "+
"207); %d ask(s) it had not acknowledged are not carried over and must be asked again",
c.Name, c.Stream, shape(havePush), shape(wantPush), have.NumPending+uint64(have.NumAckPending))
want.DeliverPolicy = nats.DeliverNewPolicy
} else {
j.note("consumer %s on %s changes from %s to %s delivery: re-made where it left off, nothing "+ j.note("consumer %s on %s changes from %s to %s delivery: re-made where it left off, nothing "+
"acknowledged comes back and nothing pending is lost (novox/hq issue 206); a holder bound to "+ "acknowledged comes back and nothing pending is lost (novox/hq issue 206); a holder bound to "+
"the old shape binds again", c.Name, c.Stream, shape(havePush), shape(wantPush)) "the old shape binds again", c.Name, c.Stream, shape(havePush), shape(wantPush))
}
if err := j.js.DeleteConsumer(c.Stream, c.Name); err != nil { if err := j.js.DeleteConsumer(c.Stream, c.Name); err != nil {
return fmt.Errorf("re-making consumer %s on %s as %s: %w", c.Name, c.Stream, shape(wantPush), err) return fmt.Errorf("re-making consumer %s on %s as %s: %w", c.Name, c.Stream, shape(wantPush), err)
} }
+27 -5
View File
@@ -108,9 +108,10 @@ func TestAWorkerOfTheWrongTypeIsRemadeOnAWorkQueueAndAPullThenBinds(t *testing.T
} }
} }
// On a stream that keeps its history, a worker of the wrong type is said and left: re-making it would // On a stream that keeps its history, a worker of the wrong type is re-made to deliver from now on:
// replay what it acknowledged (novox/hq issue 156), and that is a person's call. // re-making it from the start would replay what it acknowledged (novox/hq issue 156), and leaving it
func TestAWorkerOfTheWrongTypeOnAHistoryStreamIsLeftAndSaid(t *testing.T) { // for a hand re-made it exactly that way on 2026-10-03 (issue 207).
func TestAWorkerOfTheWrongTypeOnAHistoryStreamIsRemadeFromNowOn(t *testing.T) {
url := os.Getenv("MESH_TEST_NATS") url := os.Getenv("MESH_TEST_NATS")
if url == "" { if url == "" {
t.Skip("MESH_TEST_NATS unset") t.Skip("MESH_TEST_NATS unset")
@@ -132,6 +133,12 @@ func TestAWorkerOfTheWrongTypeOnAHistoryStreamIsLeftAndSaid(t *testing.T) {
}); err != nil { }); err != nil {
t.Fatal(err) t.Fatal(err)
} }
// History the old consumer would have acknowledged long ago, and must not come back.
for i := 0; i < 3; i++ {
if _, err := js.js.Publish("mesh.t.event.old", []byte("old")); err != nil {
t.Fatal(err)
}
}
if err := js.EnsureConsumer(Consumer{Name: worker, Stream: stream, Filters: []string{filter}, AckWaitSeconds: 60}); err != nil { if err := js.EnsureConsumer(Consumer{Name: worker, Stream: stream, Filters: []string{filter}, AckWaitSeconds: 60}); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -139,7 +146,22 @@ func TestAWorkerOfTheWrongTypeOnAHistoryStreamIsLeftAndSaid(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if have.Config.DeliverSubject == "" { if have.Config.DeliverSubject != "" {
t.Fatal("a history stream's consumer was re-made, which replays what it acknowledged") t.Fatal("a history stream's consumer of the wrong type was left as it was")
}
if have.Config.DeliverPolicy != nats.DeliverNewPolicy || have.NumPending != 0 {
t.Fatalf("re-made consumer delivers %v with %d pending; it must deliver from now on with nothing of the past", have.Config.DeliverPolicy, have.NumPending)
}
// And what arrives from now on is delivered.
if _, err := js.js.Publish("mesh.t.event.new", []byte("new")); err != nil {
t.Fatal(err)
}
sub, err := js.js.PullSubscribe(filter, worker, nats.Bind(stream, worker))
if err != nil {
t.Fatal(err)
}
got, err := sub.Fetch(1, nats.MaxWait(3*time.Second))
if err != nil || len(got) != 1 || string(got[0].Data) != "new" {
t.Fatalf("the re-made consumer delivered %v, %v; want the one new message", got, err)
} }
} }