Grants part: reads the seat list again when its own consumer is heard, since the seat's event before it is lost to it (whole case for the unread phase; ADR 0636 §1; issue 1966)
This commit is contained in:
@@ -193,7 +193,7 @@ func TestTheGrantsPartWhole(t *testing.T) {
|
||||
// has heard the seat and holds the bootstrapped definitions, and every module is composed. (The unit case holds a module
|
||||
// while the list is unread.)
|
||||
var seatAnswering atomic.Bool
|
||||
seatAnswering.Store(true)
|
||||
seatAnswering.Store(false)
|
||||
for _, subject := range []string{"mesh.seat.mesh-catalog.call.seat-definitions", "mesh.seat.mesh-catalog.call.seat-definitions.>"} {
|
||||
if _, err := nc.Subscribe(subject, func(m *nats.Msg) {
|
||||
if !seatAnswering.Load() {
|
||||
@@ -239,6 +239,14 @@ func TestTheGrantsPartWhole(t *testing.T) {
|
||||
if _, err := kvs[grants.Module+"."+grants.StateMemberships].Get(ctx, "a.mod-c"); err == nil {
|
||||
t.Error("a membership composed before the owners answered its first views")
|
||||
}
|
||||
// ADR 0636 §1, the unread phase: the seat was silent when the part started and speaks now, and says so on its event
|
||||
// before the part's own consumer exists (the part makes it from its first composition, which waits on its owners'
|
||||
// first views). The event is lost to that consumer, which starts at the stream's end, so the part reads the list again
|
||||
// when its consumer is heard; mod-c, which the unread list held, is composed after that and not before.
|
||||
seatAnswering.Store(true)
|
||||
if _, err := js.Publish(ctx, "mesh.seat.mesh-catalog.event.seat-definition-changed", []byte(`{"seat":"mesh-broker","source":"trunk"}`)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
answering.Store(true)
|
||||
until("a.mod-c's membership in the part's own state memberships", func() bool {
|
||||
e, err := kvs[grants.Module+"."+grants.StateMemberships].Get(ctx, "a.mod-c")
|
||||
|
||||
@@ -183,6 +183,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
|
||||
if err := hub.Start(ctx, js, account); err != nil {
|
||||
return err
|
||||
}
|
||||
gate.eventsLive()
|
||||
hears, err := grants.OwnHears(ownStore{conn}, account.Node)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -49,6 +49,18 @@ func (g *seatGate) follow(bus seatlist.Bus, changed func(), logf func(string, ..
|
||||
return reader
|
||||
}
|
||||
|
||||
// eventsLive is called when the part's own consumer on EVENTS is heard. A seat that was silent at the part's start may have said it
|
||||
// speaks before that consumer existed, which starts at the stream's end, so its event is never heard: while the list is unread the
|
||||
// part reads it now (ADR 0636 §1), on a goroutine of its own so the pass is not held; the reader changes the catalogue and wakes
|
||||
// the part when the seat answers, and stays unread, to be read at the seat's next event, when it does not.
|
||||
func (g *seatGate) eventsLive() {
|
||||
r := g.reader.Load()
|
||||
if r == nil || r.Status().Heard {
|
||||
return
|
||||
}
|
||||
go func() { _ = r.Reload() }()
|
||||
}
|
||||
|
||||
// tools are the two tools over whatever reader the gate holds at the call.
|
||||
func (g *seatGate) tools() []stdio.Tool {
|
||||
names := []string{grants.ToolSeatList, grants.ToolSeatListAgain}
|
||||
|
||||
Reference in New Issue
Block a user