Merge pull request 'The seat list's reader asks again while its ask reached no one (issue 1966; group fix/1966-seat-list-unread, merge first)' (#184) from fix/1966-seat-list-unread into main
This commit was merged in pull request #184.
This commit is contained in:
+15
-3
@@ -7,7 +7,9 @@
|
||||
// **What it asks and hears.** The seat's verb with no seat (every trunk definition's name and digest, Listing) and with
|
||||
// one seat (that definition, Answer); the seat's two events, whose bodies name the seat (Changed). It calls the seat,
|
||||
// never the module that holds it, and only trunk definitions are held: a pull request's are the indexer's to judge.
|
||||
// Nothing polls and nothing waits on a timer: a reader that could not ask says so in its Status and reads again on the
|
||||
// An ask that reached no one (busretry.Undelivered: the bus's "no responders", as while the seat's holder re-serves) is
|
||||
// asked again within the bus's retry bound (busretry.Bound, about 30 s, backing off; issue 774's form, issue 1966).
|
||||
// Nothing else polls or waits on a timer: a reader that could not ask says so in its Status and reads again on the
|
||||
// next event or on Reload, which the host offers as a tool (Tools).
|
||||
//
|
||||
// **A reader that has not heard acts on nothing new** (ADR 0636 §1): MayAct says what a host may start, compose, grant
|
||||
@@ -27,6 +29,7 @@ import (
|
||||
"sync"
|
||||
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/busretry"
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
"git.novox.be/novox/mesh-sdk/go/seatdef"
|
||||
)
|
||||
@@ -284,7 +287,7 @@ func (r *Reader) seatsLocked() []catalogue.Seat {
|
||||
// cannot be read is returned and changes nothing; one definition that does not parse leaves the one held standing and is
|
||||
// said in Status.
|
||||
func (r *Reader) Reload() error {
|
||||
raw, err := r.cfg.Bus.Ask(Key(), map[string]any{})
|
||||
raw, err := r.ask(map[string]any{}, "the seat list")
|
||||
if err != nil {
|
||||
return r.failed(fmt.Errorf("seatlist: asking %s: %w", Key(), err))
|
||||
}
|
||||
@@ -358,9 +361,18 @@ func (r *Reader) ReloadIfUnread() <-chan struct{} {
|
||||
return done
|
||||
}
|
||||
|
||||
// ask asks the seat's verb, and again while the ask reached no one, within the bus's retry bound: a holder that re-serves
|
||||
// for a few seconds while the reader starts leaves no list unread (issue 1966). An answer, a refusal or a timeout is never
|
||||
// asked again.
|
||||
func (r *Reader) ask(body map[string]any, what string) (json.RawMessage, error) {
|
||||
return busretry.Do(what, func() (json.RawMessage, error) {
|
||||
return r.cfg.Bus.Ask(Key(), body)
|
||||
}, nil)
|
||||
}
|
||||
|
||||
// fetch asks the seat for one definition and holds it, or drops it when the seat holds none.
|
||||
func (r *Reader) fetch(seat string) error {
|
||||
raw, err := r.cfg.Bus.Ask(Key(), map[string]any{"seat": seat})
|
||||
raw, err := r.ask(map[string]any{"seat": seat}, "the seat "+seat)
|
||||
if err != nil {
|
||||
return fmt.Errorf("seatlist: asking %s for %s: %w", Key(), seat, err)
|
||||
}
|
||||
|
||||
@@ -7,17 +7,21 @@ import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/busretry"
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
"git.novox.be/novox/mesh-sdk/go/seatdef"
|
||||
"git.novox.be/novox/mesh-sdk/go/seatlist/seatlisttest"
|
||||
"github.com/nats-io/nats.go"
|
||||
)
|
||||
|
||||
// fakeOwner is the seat mesh-catalog: its verb answers from its table and the events are delivered by the test.
|
||||
type fakeOwner struct {
|
||||
table map[string]string // seat -> definition JSON, trunk
|
||||
down bool
|
||||
silent int // when set, so many asks first reach no one (the bus's no responders), as while the holder re-serves
|
||||
gate chan struct{} // when set, an ask waits for it to be closed
|
||||
asked []string // "key seat"
|
||||
handlers map[string]func(string, json.RawMessage) error
|
||||
@@ -30,6 +34,10 @@ func (f *fakeOwner) Ask(key string, body any) (json.RawMessage, error) {
|
||||
args, _ := body.(map[string]any)
|
||||
seat, _ := args["seat"].(string)
|
||||
f.asked = append(f.asked, key+" "+seat)
|
||||
if f.silent > 0 {
|
||||
f.silent--
|
||||
return nil, fmt.Errorf("not asked: nothing on this machine answers it (%w)", nats.ErrNoResponders)
|
||||
}
|
||||
if f.down {
|
||||
return nil, errors.New("no answer")
|
||||
}
|
||||
@@ -266,6 +274,19 @@ func TestStartReturnsAtOnceAndTheReaderSaysItIsReadingUntilTheSeatAnswers(t *tes
|
||||
if st := r.Status(); st.Reading || !st.Heard || !r.MayAct("node-b") {
|
||||
t.Fatalf("after the read: %+v", st)
|
||||
}
|
||||
|
||||
// Issue 1966: the first read falls into the holder's re-serve (the bus answers no responders twice); it is asked again
|
||||
// within the bus's retry bound, with no event and no hand act, and the reader is heard.
|
||||
t.Cleanup(busretry.Waits(10*time.Second, time.Millisecond, 4*time.Millisecond))
|
||||
owner = &fakeOwner{silent: 2, table: map[string]string{"node-b": def("node-b", "node", "put")}}
|
||||
r, err = Start(Config{Bus: owner, Bootstrap: Bootstrapped()})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
<-r.Ready()
|
||||
if st := r.Status(); !st.Heard || st.LastError != "" || !r.Has("node-b") || len(owner.asked) != 4 {
|
||||
t.Fatalf("after a holder that re-served: %+v, asked %q: want heard on the third ask of the list", st, owner.asked)
|
||||
}
|
||||
}
|
||||
|
||||
// The bootstrap and the tests' seat list are copies of the same contracts files: where a seat is in both, they are equal,
|
||||
|
||||
Reference in New Issue
Block a user