bus5 B5 (A-107): the keeper's process answers mesh-broker's snapshot verbs itself through AnswerSeat

snapshot-get, snapshot-restore, snapshot-restore-cancel and snapshot-remove are answered by
the keeper's process with the proved caller; it publishes snapshot-get's chunks and hears
their acks on its own subscription, and acts at once with no hand-over through requests
(the record of a running restore is written before it runs). The tools bundle keeps its
tools, nats_snapshots among them. The protected refusal and the whole test's case (send,
restore in place, removal) stay.

Builds: design 47 G A-107 (G-20-lead-answers), §681

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
2026-11-11 23:56:06 +01:00
parent a9dfe77b1d
commit 44cb7ad449
8 changed files with 378 additions and 254 deletions
+9 -6
View File
@@ -783,9 +783,7 @@ func TestTheKeeperMakesWhatAVerdictAsksForAndSaysIt(t *testing.T) {
}
ask := func(r Request) Request {
r.Caller = "person.jo"
put(r)
k.ActOnRequests()
return get(r.ID)
return k.AnswerSnapshot(r)
}
put(Request{ID: "r20", Verb: VerbDrop, Path: path, Size: 100, Digest: digest, Protected: true, At: k.now(), Done: k.now()})
put(Request{ID: "r21", Verb: VerbDrop, Path: path, Size: 100, Digest: "sha256:bad", At: k.now(), Done: k.now()})
@@ -837,6 +835,13 @@ func TestTheKeeperMakesWhatAVerdictAsksForAndSaysIt(t *testing.T) {
if got := ask(Request{ID: "c2", Verb: VerbSnapshotRestoreCancel, Of: "a3", Why: "w"}); got.Outcome != "error" {
t.Errorf("cancel of an ended act: %+v", got)
}
// A restore written into the state requests is no request of a person's: the keeper answers the verb on the seat alone (A-107).
before := len(bus.restored)
put(Request{ID: "z1", Verb: VerbSnapshotRestore, Of: "r20", Why: "w", Caller: "person.jo"})
k.ActOnRequests()
if got := get("z1"); len(bus.restored) != before || got.Outcome == "accepted" || got.Error == "" {
t.Errorf("a restore handed through the requests: %+v, restored %v", got, bus.restored)
}
// A restore a restart cut off is ended at the keeper's start.
put(Request{ID: "a5", Verb: VerbSnapshotRestore, Of: "r20", Outcome: "accepted", Done: k.now()})
k.EndCutOffRestores()
@@ -869,9 +874,7 @@ func TestTheKeeperMakesWhatAVerdictAsksForAndSaysIt(t *testing.T) {
put(Request{ID: "s3", Verb: VerbDrop, Snapshot: "/before/g.tar", At: old, Done: old})
ask := func(r Request) Request {
r.Caller = "person.jo"
put(r)
k.ActOnRequests()
return get(r.ID)
return k.AnswerSnapshot(r)
}
for _, r := range []Request{{ID: "x1", Verb: VerbSnapshotRemove, Why: "w"}, {ID: "x2", Verb: VerbSnapshotRemove, Of: "s1", AllExpired: true, Why: "w"}} {
if got := ask(r); got.Outcome != "error" || !strings.Contains(got.Error, "not both and not neither") {
+27 -8
View File
@@ -97,6 +97,33 @@ func (r Request) Act() occ.Act {
return occ.Act{"verb": r.Verb, "module": r.Module, "local": r.Local}
}
// AnswerSnapshot answers one call of the seat mesh-broker's snapshot-restore, snapshot-restore-cancel or snapshot-remove at once, as
// the keeper's process answers it (design 47 G §681, A-107): r names the verb, its arguments and the proved caller, and comes back
// ended with its Outcome (accepted, busy, verified or error), ActID and Error. Nothing is handed through the state requests but the
// record of a restore that runs, which its end and a restart's cut-off read.
func (k *Keeper) AnswerSnapshot(r Request) Request {
if r.At.IsZero() {
r.At = k.now()
}
var outcome string
var err error
switch r.Verb {
case VerbSnapshotRestore:
outcome, err = k.startRestore(&r)
case VerbSnapshotRestoreCancel:
outcome, err = k.cancelRestore(&r)
case VerbSnapshotRemove:
outcome, err = k.removeSnapshots(&r)
default:
outcome, err = "error", fmt.Errorf("%q is no snapshot verb", r.Verb)
}
r.Done, r.Outcome = k.now(), outcome
if err != nil {
r.Error = err.Error()
}
return r
}
// 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")
@@ -154,14 +181,6 @@ func (k *Keeper) act(r *Request) (string, error) {
}
return fmt.Sprintf("the first view answered at revision %d", s.Revision), nil
}
switch r.Verb {
case VerbSnapshotRestore:
return k.startRestore(r)
case VerbSnapshotRestoreCancel:
return k.cancelRestore(r)
case VerbSnapshotRemove:
return k.removeSnapshots(r)
}
if !k.Viewed() {
return "", fmt.Errorf("%w: the keeper holds no first view yet, so it acts on nothing", ErrNoWarrant)
}
+12
View File
@@ -111,6 +111,18 @@ func (k *Keeper) startRestore(r *Request) (string, error) {
k.restoring = run
k.mu.Unlock()
r.ActID = run.act
// The act's record is written before the restore runs, as the end of the restore and a restart's cut-off read it; a record that
// cannot be written ends the start, so no restore runs that nothing could find again.
held := *r
held.Done, held.Outcome = k.now(), "accepted"
data, _ := json.Marshal(held)
if _, err := k.Store.Put(StateRequests, r.ID, data); err != nil {
k.mu.Lock()
k.restoring = nil
k.mu.Unlock()
cancel()
return "error", fmt.Errorf("the restore's record cannot be written, so nothing is restored: %w", err)
}
k.record(r, fmt.Sprintf("restores the snapshot of the request %s (%s) in place", rec.ID, rec.Path))
go k.restore(ctx, run, rec, r.Caller, r.Why)
return "accepted", nil