CAT-N2b: nats_first_view_again asks the indexer's first view at once, a hand act
The bus module's tool hands the ask to the keeper through its own state requests (design 25 § 12.2), as the caller the tool runner proved; the keeper asks indexer_bus_state at once beside its backoff, applies what it answers, records the hand act and writes back what it answered and at which revision. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
@@ -111,21 +111,47 @@ func requestTools(rs requestStore) []stdio.Tool {
|
||||
if err := rs.Put(r.ID, r); err != nil {
|
||||
return nil, fmt.Errorf("the request cannot be handed to the keeper: %w", err)
|
||||
}
|
||||
// The keeper acts on its watch of requests at once; its end is read back for at most half a minute.
|
||||
for wait := 100 * time.Millisecond; wait < 20*time.Second; wait *= 2 {
|
||||
rs.Wait(wait)
|
||||
if got, err := rs.Get(r.ID); err == nil && got != nil && !got.Done.IsZero() {
|
||||
return got, nil
|
||||
}
|
||||
}
|
||||
return map[string]any{"request": r.ID, "state": "handed to the keeper, not ended yet",
|
||||
"read": fmt.Sprintf("nats_%s with request %s", verb, r.ID)}, nil
|
||||
return awaitEnd(rs, r, "nats_"+verb), nil
|
||||
}}
|
||||
}
|
||||
firstView := stdio.Tool{Name: "nats_first_view_again",
|
||||
Description: "Ask the bus module's keeper to ask the indexer's indexer_bus_state, its first view of every accepted manifest's bus state, at once (design 25 § 12.2): by hand, beside its own backoff, recorded as a hand act; answers what the first view answered and its revision.",
|
||||
Input: map[string]any{"why": str("why, in plain words: kept with the act"), "request": str("a request made before: its end is read")},
|
||||
Run: func(a map[string]any) (any, error) {
|
||||
if id := arg(a, "request"); id != "" {
|
||||
r, err := rs.Get(id)
|
||||
if err != nil || r == nil {
|
||||
return nil, fmt.Errorf("no request %s is held (%v)", id, err)
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
r := keeper.Request{Verb: keeper.VerbFirstViewAgain, Why: strings.TrimSpace(arg(a, "why")), Caller: stdio.CallerOf(a), At: rs.Now()}
|
||||
if r.Why == "" {
|
||||
return nil, fmt.Errorf("nats_first_view_again says why")
|
||||
}
|
||||
r.ID = requestID(r.At)
|
||||
if err := rs.Put(r.ID, r); err != nil {
|
||||
return nil, fmt.Errorf("the request cannot be handed to the keeper: %w", err)
|
||||
}
|
||||
return awaitEnd(rs, r, "nats_first_view_again"), nil
|
||||
}}
|
||||
return []stdio.Tool{
|
||||
firstView,
|
||||
tool(keeper.VerbCreate, "Create one entry of a module's kept data on the bus now, as its accepted manifest asks for it (design 25 § 12.3): handed to the bus module's keeper, which alone may create, and recorded as a hand act."),
|
||||
tool(keeper.VerbApply, "Apply what a module's manifest asks of one entry where the bus holds less (a larger cap, a longer age): only a difference that grows; handed to the keeper and recorded as a hand act."),
|
||||
tool(keeper.VerbRecreate, "Rebuild one entry whose settings drifted in a way that does not grow: the operator is asked first, and the keeper acts only on their warrant, after a snapshot of the entry. Its data is lost from the bus (the snapshot keeps it). Dangerous."),
|
||||
tool(keeper.VerbDrop, "Remove one orphan, an entry no accepted manifest asks for: the operator is asked first, and the keeper acts only on their warrant, after a snapshot of the entry. Dangerous."),
|
||||
}
|
||||
}
|
||||
|
||||
// awaitEnd reads a request's end back: the keeper acts on its watch of requests at once, so the end is read for at most
|
||||
// half a minute, and otherwise how to read it later is answered.
|
||||
func awaitEnd(rs requestStore, r keeper.Request, tool string) any {
|
||||
for wait := 100 * time.Millisecond; wait < 20*time.Second; wait *= 2 {
|
||||
rs.Wait(wait)
|
||||
if got, err := rs.Get(r.ID); err == nil && got != nil && !got.Done.IsZero() {
|
||||
return got
|
||||
}
|
||||
}
|
||||
return map[string]any{"request": r.ID, "state": "handed to the keeper, not ended yet", "read": fmt.Sprintf("%s with request %s", tool, r.ID)}
|
||||
}
|
||||
|
||||
@@ -510,6 +510,25 @@ func TestTheKeeperMakesWhatAVerdictAsksForAndSaysIt(t *testing.T) {
|
||||
t.Errorf("held %+v, said %v %v: rebuilt as asked, the drift cleared", bus.held["m_log"], out.events, out.conds)
|
||||
}
|
||||
}},
|
||||
// CAT-N2b: nats_first_view_again asks the first view at once, by hand, recorded as a hand act.
|
||||
{"the first view asked again by hand", func(t *testing.T, k *Keeper, bus *fakeBus, out *said) {
|
||||
store := k.Store.(*memStore)
|
||||
var acts []string
|
||||
k.Act = func(what, why, cause string) error { acts = append(acts, cause+": "+what); return nil }
|
||||
k.View = func() (FirstView, error) {
|
||||
return FirstView{Revision: 8, Modules: []Accepted{{Module: "m", Revision: 8, BusState: []catalogue.BusStateEntry{drafts}}}}, nil
|
||||
}
|
||||
raw, _ := json.Marshal(Request{ID: "r9", Verb: VerbFirstViewAgain, Why: "a verdict thought missed", Caller: "person.jo"})
|
||||
_, _ = store.Put(StateRequests, "r9", raw)
|
||||
k.ActOnRequests()
|
||||
raw, _, _, _ = store.Get(StateRequests, "r9")
|
||||
var got Request
|
||||
_ = json.Unmarshal(raw, &got)
|
||||
if got.Outcome != "the first view answered at revision 8" || fmt.Sprint(bus.created) != "[KV_m_drafts]" ||
|
||||
len(acts) != 1 || !strings.Contains(acts[0], "nats_first_view_again: asked mesh-delivery-indexer.indexer_bus_state again by hand") {
|
||||
t.Errorf("ended %+v, created %v, recorded %v", got, bus.created, acts)
|
||||
}
|
||||
}},
|
||||
} {
|
||||
t.Run(row.name, func(t *testing.T) {
|
||||
k, bus, _, out := newKeeper(FirstView{Revision: 1})
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/asks"
|
||||
@@ -22,6 +23,8 @@ const (
|
||||
VerbApply = "apply"
|
||||
VerbRecreate = "recreate"
|
||||
VerbDrop = "drop"
|
||||
// VerbFirstViewAgain asks the indexer's first view at once, by hand (design 25 § 12.2), beside the backoff.
|
||||
VerbFirstViewAgain = "first-view-again"
|
||||
)
|
||||
|
||||
// Request is one person's request, the value of its key in StateRequests.
|
||||
@@ -83,6 +86,15 @@ func (k *Keeper) ActOnRequests() {
|
||||
}
|
||||
|
||||
func (k *Keeper) act(r *Request) (string, error) {
|
||||
if r.Verb == VerbFirstViewAgain {
|
||||
_, err := k.AskFirstView(0)
|
||||
s := k.Status().FirstView
|
||||
k.record(r, fmt.Sprintf("asked %s.%s again by hand: answered %v at revision %d", s.Owner, s.Verb, err == nil, s.Revision))
|
||||
if err != nil {
|
||||
return "asked, not answered", err
|
||||
}
|
||||
return fmt.Sprintf("the first view answered at revision %d", s.Revision), nil
|
||||
}
|
||||
if !k.Viewed() {
|
||||
return "", fmt.Errorf("%w: the keeper holds no first view yet, so it acts on nothing", ErrNoWarrant)
|
||||
}
|
||||
@@ -199,7 +211,7 @@ func (k *Keeper) record(r *Request, what string) {
|
||||
if k.Act == nil {
|
||||
return
|
||||
}
|
||||
if err := k.Act(what+", asked by "+r.Caller, r.Why, "nats_"+r.Verb); err != nil {
|
||||
if err := k.Act(what+", asked by "+r.Caller, r.Why, "nats_"+strings.ReplaceAll(r.Verb, "-", "_")); err != nil {
|
||||
k.logf("the hand act %q could not be recorded: %v", what, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +120,8 @@
|
||||
"nats_apply",
|
||||
"nats_recreate",
|
||||
"nats_drop",
|
||||
"nats_keeper_health"
|
||||
"nats_keeper_health",
|
||||
"nats_first_view_again"
|
||||
],
|
||||
"guards": [
|
||||
8222
|
||||
|
||||
Reference in New Issue
Block a user