RN17 (fill): a bundle reads a state's fill through KeptState.Info
A bundle's state call mesh/state.info is served by the tool runner (node-tools launch.go:425, runtime.go:1093); the module library had no wrapper for it. Builds: design 47 G §623, §366, A-214 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
+32
@@ -126,6 +126,38 @@ func (s KeptState) Page(after string, maxBytes int, budget time.Duration) (State
|
||||
return p, json.Unmarshal(raw, &p)
|
||||
}
|
||||
|
||||
// StateInfo is how full a state's bucket is against its caps, as the tool runner answers `mesh/state.info`
|
||||
// (design 47 G §623): a bundle's tools say fill against cap from it. A cap of -1 is none.
|
||||
type StateInfo struct {
|
||||
State string `json:"state"`
|
||||
Bucket string `json:"bucket"`
|
||||
// Bytes is what the bucket holds now; Keys how many keys it holds.
|
||||
Bytes uint64 `json:"bytes"`
|
||||
Keys uint64 `json:"keys"`
|
||||
// MaxBytes and MaxValueBytes are the bucket's caps.
|
||||
MaxBytes int64 `json:"max_bytes"`
|
||||
MaxValueBytes int32 `json:"max_value_bytes"`
|
||||
// History is how many values a key keeps; TTLSeconds the bucket's age for a value (0 none); KeyTTL whether a key
|
||||
// may carry its own time to live.
|
||||
History int64 `json:"history"`
|
||||
TTLSeconds int64 `json:"ttl_seconds"`
|
||||
KeyTTL bool `json:"key_ttl"`
|
||||
}
|
||||
|
||||
// Info is the bucket's fill against its caps: runtime verb `mesh/state.info`. A read, like Get: the owner's
|
||||
// instances and its readers may ask it, and it writes nothing.
|
||||
func (s KeptState) Info() (*StateInfo, error) {
|
||||
raw, err := s.ask("info", map[string]any{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var i StateInfo
|
||||
if err := json.Unmarshal(raw, &i); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &i, nil
|
||||
}
|
||||
|
||||
// History is every value the bucket still keeps for key, oldest first — as many as the state declared it
|
||||
// keeps, deletes included — and empty for a key it holds nothing for (novox/hq ADR 0307). A read, like Get:
|
||||
// the owner's instances and its readers may ask it, and it writes nothing.
|
||||
|
||||
@@ -141,3 +141,37 @@ func TestHistoryIsEveryKeptValueOldestFirst(t *testing.T) {
|
||||
t.Fatalf("a state not issued answered %v", refused)
|
||||
}
|
||||
}
|
||||
|
||||
// Info asks mesh/state.info with the state alone and hands back the bucket's fill against its caps, as the tool
|
||||
// runner answers it (design 47 G §623); a refusal is the runtime's error.
|
||||
func TestInfoIsTheBucketsFillAgainstItsCaps(t *testing.T) {
|
||||
var got *StateInfo
|
||||
var refused error
|
||||
asked := historying(t, func(m message) map[string]any {
|
||||
var p map[string]any
|
||||
_ = json.Unmarshal(m.Params, &p)
|
||||
if p["state"] != "issues" {
|
||||
return map[string]any{"error": map[string]any{"code": -32000, "message": "issues was issued no state"}}
|
||||
}
|
||||
return map[string]any{"result": map[string]any{"state": "issues", "bucket": "issues_issues", "bytes": 300, "keys": 4,
|
||||
"max_bytes": 1000, "max_value_bytes": 262144, "history": 1, "ttl_seconds": 0, "key_ttl": true}}
|
||||
}, func() {
|
||||
var err error
|
||||
if got, err = State("issues").Info(); err != nil {
|
||||
t.Errorf("info: %v", err)
|
||||
}
|
||||
var i *StateInfo
|
||||
if i, refused = State("elsewhere.issues").Info(); i != nil {
|
||||
t.Errorf("a refused info handed back %v", i)
|
||||
}
|
||||
})
|
||||
if len(asked) != 2 || asked[0].Method != "mesh/state.info" {
|
||||
t.Fatalf("asked %+v", asked)
|
||||
}
|
||||
if got == nil || got.Bytes != 300 || got.MaxBytes != 1000 || got.Keys != 4 || got.Bucket != "issues_issues" || !got.KeyTTL {
|
||||
t.Errorf("info is %+v", got)
|
||||
}
|
||||
if refused == nil {
|
||||
t.Error("an info of a state the module was not issued is not refused")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user