Checker: reads the seat list when its hub is heard while it is unread, since the seat's event before its consumer is lost to it (ADR 0636 §1; issue 1966)

This commit is contained in:
2026-11-05 16:12:49 +01:00
parent 8c685160ea
commit cde060d523
3 changed files with 53 additions and 0 deletions
@@ -465,6 +465,8 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
// check fails at its publish bound until the checker starts again.
if err := hub.Start(ctx, js, cred); err != nil {
logger.Printf("%v, so no package registry's published is heard and a group's check fails at its publish bound until the checker starts again", err)
} else {
readIfUnread(seats.reader)
}
defer hub.Stop()
// The checks, the tries and the hand asks, once every first view is answered (the lead's ruling for DLV-M2): a check
@@ -49,3 +49,13 @@ func (s seatList) Definitions() ([]json.RawMessage, error) {
}
return out, nil
}
// readIfUnread is called when the checker's hub is heard. The seat may have answered, and said that it does, before the
// checker's consumer existed, whose events start at the stream's end: the first read found it silent and nothing else reads
// again until its next event, so while the list is unread it is read now, on a goroutine of its own (ADR 0636 §1).
func readIfUnread(reader *seatlist.Reader) {
if reader == nil || reader.Status().Heard {
return
}
go func() { _ = reader.Reload() }()
}
@@ -0,0 +1,41 @@
package main
import (
"encoding/json"
"errors"
"sync/atomic"
"testing"
"time"
"git.novox.be/novox/mesh-sdk/go/seatlist"
)
// silentThenAnswering is a seat that is silent at the reader's first read and answers after.
type silentThenAnswering struct{ answering atomic.Bool }
func (b *silentThenAnswering) Ask(string, any) (json.RawMessage, error) {
if !b.answering.Load() {
return nil, errors.New("not connected yet")
}
return json.RawMessage(`{"revision":1,"seats":[]}`), nil
}
func (b *silentThenAnswering) Subscribe(string, func(string, json.RawMessage) error) error { return nil }
// A seat that spoke before the checker's consumer existed is read when the hub is heard (ADR 0636 §1).
func TestTheSeatListIsReadWhenTheHubIsHeard(t *testing.T) {
bus := &silentThenAnswering{}
reader := followSeatList(bus, t.Logf)
<-reader.Ready()
if reader.Status().Heard {
t.Fatal("a silent seat was heard")
}
bus.answering.Store(true)
readIfUnread(reader)
for deadline := time.Now().Add(5 * time.Second); !reader.Status().Heard; time.Sleep(10 * time.Millisecond) {
if time.Now().After(deadline) {
t.Fatal("the list was not read when the hub was heard")
}
}
var _ seatlist.Bus = bus
}