Files
mesh-catalog/modules/nats/internal/keeper/requests.go
T
jschoubben 76aab05289 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
2026-11-04 12:34:59 +01:00

218 lines
7.9 KiB
Go

package keeper
import (
"context"
"encoding/json"
"errors"
"fmt"
"strings"
"time"
"git.novox.be/novox/mesh-sdk/go/asks"
)
// A person's request (design 25 § 12.3; CAT-N8): the bus module's tools hold no grant of the seat's acting verbs, so
// each hands its request to the keeper through the keeper's own state requests, stamped with the caller the tool runner
// proved (never a name the request's body gives), and reads the keeper's answer back from the same key. create and
// apply act at once; recreate and drop only once the operator's warrant on the ask the tool raised is held, after a
// snapshot of the entry's streams. Every act is recorded as a hand act.
// The verbs a request names.
const (
VerbCreate = "create"
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.
type Request struct {
ID string `json:"id"`
Verb string `json:"verb"`
Module string `json:"module"`
Local string `json:"local"`
Why string `json:"why"`
// Caller is who asked, as the tool runner proved it (stdio.CallerOf), written by the tool.
Caller string `json:"caller"`
At time.Time `json:"at"`
// Ask is the id of the operator's ask the tool raised for a recreate or a drop; the keeper acts only on its warrant.
Ask string `json:"ask,omitempty"`
// Done is when the keeper ended it, Outcome what it did, Error why not.
Done time.Time `json:"done,omitempty"`
Outcome string `json:"outcome,omitempty"`
Error string `json:"error,omitempty"`
Snapshot string `json:"snapshot,omitempty"`
}
// Act is what an option of the operator's ask binds for a recreate or a drop: the verb and the entry (asks.ActDigest).
func (r Request) Act() asks.Act {
return asks.Act{"verb": r.Verb, "module": r.Module, "local": r.Local}
}
// ErrNoWarrant is a request that waits for the operator's word: kept, acted on once the warrant is held.
var ErrNoWarrant = errors.New("the operator has not answered yet")
// ActOnRequests acts on every request not ended yet, oldest first, and writes each one's end back to its key.
func (k *Keeper) ActOnRequests() {
keys, err := k.Store.Keys(StateRequests)
if err != nil {
k.logf("the persons' requests cannot be read: %v", err)
return
}
for _, key := range keys {
raw, _, found, err := k.Store.Get(StateRequests, key)
if err != nil || !found {
continue
}
var r Request
if json.Unmarshal(raw, &r) != nil || !r.Done.IsZero() {
continue
}
outcome, err := k.act(&r)
if errors.Is(err, ErrNoWarrant) {
continue
}
r.Done, r.Outcome = k.now(), outcome
if err != nil {
r.Error = err.Error()
}
data, _ := json.Marshal(r)
if _, err := k.Store.Put(StateRequests, key, data); err != nil {
k.logf("the end of the request %s cannot be written: %v", key, err)
}
}
}
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)
}
held, _, found := k.asked(r.Module)
if !found {
return "nothing done", fmt.Errorf("the bus module holds no entry of %s", r.Module)
}
m, made := held.Made[r.Local]
asked := false
for _, e := range held.Entries {
asked = asked || e.Name == r.Local
}
switch r.Verb {
case VerbCreate, VerbApply:
if !asked {
return "nothing done", fmt.Errorf("no accepted manifest of %s asks for %s: the bus module creates only what one asks", r.Module, r.Local)
}
for _, e := range held.Entries {
if e.Name == r.Local {
k.makeEntry(&held, e)
}
}
k.write(held)
m = held.Made[r.Local]
k.record(r, fmt.Sprintf("%s of %s's %s: created %v, drift %q, waiting %q", r.Verb, r.Module, r.Local, m.Created, m.Drift, m.Waiting))
if m.Refused != "" || m.Waiting != "" {
return "not made", fmt.Errorf("%s%s", m.Refused, m.Waiting)
}
return fmt.Sprintf("%s: the bus holds %s as asked%s", r.Verb, BusName(r.Module, r.Local), map[bool]string{true: ", but for a drift a person decides: " + m.Drift}[m.Drift != ""]), nil
case VerbRecreate, VerbDrop:
default:
return "nothing done", fmt.Errorf("%q is no verb a person asks the bus module for: create, apply, recreate or drop", r.Verb)
}
if !made || !m.Created {
return "nothing done", fmt.Errorf("the bus module made no %s of %s", r.Local, r.Module)
}
if r.Verb == VerbDrop && !m.Orphaned {
return "nothing done", fmt.Errorf("%s of %s is asked for by its manifest: drop removes an orphan alone", r.Local, r.Module)
}
if r.Verb == VerbRecreate && m.Orphaned {
return "nothing done", fmt.Errorf("%s of %s is an orphan: drop it, or ask for it in a manifest again", r.Local, r.Module)
}
if err := k.warranted(r); err != nil {
return "nothing done", err
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()
var streams []string
for _, o := range m.Plan.Objects {
streams = append(streams, o.Stream())
}
path, err := k.Bus.Snapshot(ctx, streams)
if err != nil {
return "nothing done", fmt.Errorf("the snapshot before %s failed, so nothing was removed: %w", r.Verb, err)
}
r.Snapshot = path
for _, o := range m.Plan.Objects {
if err := k.Bus.Delete(ctx, o); err != nil {
return "partly done", fmt.Errorf("%s could not be removed after its snapshot at %s: %w", o.Stream(), path, err)
}
}
if r.Verb == VerbDrop {
delete(held.Made, r.Local)
k.write(held)
k.emit(EventDropped, m.Plan, held.Revision, r.Why, nil, nil)
k.clear(conditionOrphaned, r.Module, r.Local)
k.record(r, fmt.Sprintf("dropped %s of %s on the operator's warrant (snapshot %s)", r.Local, r.Module, path))
return fmt.Sprintf("dropped %s, its snapshot at %s", BusName(r.Module, r.Local), path), nil
}
for _, o := range m.Plan.Objects {
if err := k.Bus.Create(ctx, o); err != nil {
m.Created, m.Refused = false, fmt.Sprintf("%s: %v", o.Stream(), err)
held.Made[r.Local] = m
k.write(held)
return "partly done", fmt.Errorf("%s was removed and could not be made again (its snapshot at %s): %w", o.Stream(), path, err)
}
}
wasDrift := m.Drift != ""
m.Drift = ""
held.Made[r.Local] = m
k.write(held)
k.emit(EventRecreated, m.Plan, held.Revision, r.Why, nil, nil)
if wasDrift {
k.clear(conditionDrifted, r.Module, r.Local)
}
k.record(r, fmt.Sprintf("recreated %s of %s on the operator's warrant (snapshot %s)", r.Local, r.Module, path))
return fmt.Sprintf("recreated %s as asked, its old data's snapshot at %s", BusName(r.Module, r.Local), path), nil
}
// warranted holds a recreate or a drop to the operator's warrant on the ask its tool raised: chosen by the operator,
// on that ask, binding exactly this act.
func (k *Keeper) warranted(r *Request) error {
if r.Ask == "" || k.Warrant == nil {
return fmt.Errorf("a %s acts only on the operator's warrant, and this request names no ask", r.Verb)
}
ask, w, err := k.Warrant(r.Ask)
if err != nil {
return fmt.Errorf("%w: the router's record of the ask %s cannot be read now: %v", ErrNoWarrant, r.Ask, err)
}
if ask == nil || w == nil {
return ErrNoWarrant
}
o, err := w.For(Module, *ask)
if err != nil {
return fmt.Errorf("no warrant: %w", err)
}
if o.Level != asks.Destroy {
return fmt.Errorf("the operator chose %s, so nothing is removed", o.Label)
}
return o.Performs(r.Act())
}
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_"+strings.ReplaceAll(r.Verb, "-", "_")); err != nil {
k.logf("the hand act %q could not be recorded: %v", what, err)
}
}