G sessions: the runner serves a bundle's put and get sessions and states put-sessions

A value over buscalls.ChunkBytes is put by mesh/objects.put-chunk (written into the module's store as each chunk is
taken, sealed on the last, dropped at its bound or a disorder) and got by mesh/objects.get-chunk after a chunked answer,
for a state's object and a sent object alike; the 8 MiB BundleObjectMost cap is gone with the stateobjects constant.

Builds: design 47 G §518, §345, §457, §490, §620, §622, A-21, A-25
Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
2026-11-11 22:39:04 +01:00
parent 4bfae38a73
commit 99c36d8ddf
11 changed files with 681 additions and 50 deletions
+61 -11
View File
@@ -1,13 +1,16 @@
package runtime
import (
"context"
"encoding/json"
"fmt"
"io"
"sort"
stdio "git.novox.be/novox/mesh-sdk/go"
"git.novox.be/novox/mesh-sdk/go/buscalls"
"git.novox.be/novox/mesh-sdk/go/stateobjects"
"github.com/novox/mesh-tools/node-tools/internal/bus"
)
// The capabilities the tool runner states of the calls it answers (novox/hq ADR 0595 §9, ADR 0597; design 25 § 12.4): each is
@@ -17,6 +20,8 @@ import (
const (
CapBusStateCalls = "bus-state-calls"
CapLedgerCalls = "ledger-calls"
// CapPutSessions is the put and get sessions' word (design 47 G §518, §622), mesh-sdk's.
CapPutSessions = stdio.CapPutSessions
)
// callFamilies are the methods each capability stands for, the ones BusState and BusStateWatch answer.
@@ -25,6 +30,7 @@ var callFamilies = map[string][]string{
buscalls.ObjectsWatch, buscalls.ObjectsSentGet, buscalls.StreamRead, buscalls.StreamFill, buscalls.WorkQueueSize, buscalls.WorkQueueDead},
CapLedgerCalls: {buscalls.LedgerApply, buscalls.LedgerGet, buscalls.LedgerHistory, buscalls.LedgerWatch, buscalls.LedgerQuery,
buscalls.LedgerRebuild, buscalls.LedgerReindex, buscalls.LedgerSnapshot, buscalls.LedgerLag},
CapPutSessions: {buscalls.ObjectsPutChunk, buscalls.ObjectsGetChunk},
}
// CallCapabilities is every capability of the calls this runner answers, sorted. The delayed settle of a pulled seat
@@ -54,11 +60,10 @@ func (b *moduleBus) BusState(method string, params json.RawMessage) (json.RawMes
case buscalls.ObjectsGet:
var r buscalls.ObjectsNameRequest
if err = decode(method, params, &r); err == nil {
var got *buscalls.ObjectData
if got, err = conn.ObjectsGet(b.module, r); err == nil && got == nil {
answer, err = b.all.objectsGet(b.module, r)
if err == nil && answer == nil {
return json.RawMessage("null"), nil
}
answer = got
}
case buscalls.ObjectsDelete:
var r buscalls.ObjectsNameRequest
@@ -69,14 +74,12 @@ func (b *moduleBus) BusState(method string, params json.RawMessage) (json.RawMes
case buscalls.ObjectsSentGet:
var r buscalls.ObjectsSentGetRequest
if err = decode(method, params, &r); err == nil {
var got *buscalls.ObjectData
if r.Size > stateobjects.BundleObjectMost {
// Only the bundle's channel is limited (design 47 "Any size"): the console reads a larger one whole.
err = fmt.Errorf("the sent object %s is %d bytes and a bundle's runtime channel carries at most %d: it is not read through a bundle", r.Object, r.Size, stateobjects.BundleObjectMost)
} else if got, err = conn.SentGet(r); err == nil {
answer = got
}
answer, err = b.all.sentGet(b.module, r)
}
case buscalls.ObjectsPutChunk:
answer, err = b.all.putChunk(b.module, params)
case buscalls.ObjectsGetChunk:
answer, err = b.all.getChunk(b.module, params)
case buscalls.ObjectsList:
var r buscalls.ObjectsRequest
if err = decode(method, params, &r); err == nil {
@@ -147,3 +150,50 @@ func decode(method string, params json.RawMessage, into any) error {
}
return nil
}
// objectsGet answers a get: the object whole when it is at most one chunk, else the session that reads it chunk by chunk
// (design 47 G §490, §518); nil when there is none.
func (c *consumers) objectsGet(module string, r buscalls.ObjectsNameRequest) (any, error) {
st := c.objects()
if st == nil {
return nil, fmt.Errorf("the runner has no bus to read %s of %s from", r.Name, module)
}
ctx, cancel := context.WithTimeout(context.Background(), bus.StateTimeout)
defer cancel()
info, rd, found, err := st.ObjectsOpen(ctx, module, r)
if err != nil || !found {
return nil, err
}
if info.Size > buscalls.ChunkBytes {
return c.openGet(module, info, rd, sessionDigest(info.Digest), false)
}
defer rd.Close()
data, err := io.ReadAll(rd)
if err != nil {
return nil, err
}
return &buscalls.ObjectData{Info: info, Data: data}, nil
}
// sentGet answers a get of a sent object by its reference: whole when the reference's size is at most one chunk, else a
// session (design 47 G §620).
func (c *consumers) sentGet(module string, r buscalls.ObjectsSentGetRequest) (any, error) {
if r.Size <= buscalls.ChunkBytes {
got, err := c.conn.SentGet(r)
if err != nil {
return nil, err
}
return got, nil
}
st := c.objects()
if st == nil {
return nil, fmt.Errorf("the runner has no bus to read %s from", r.Object)
}
ctx, cancel := context.WithTimeout(context.Background(), bus.StateTimeout)
defer cancel()
info, rd, err := st.SentOpen(ctx, r)
if err != nil {
return nil, err
}
return c.openGet(module, info, rd, sessionDigest(r.Digest), true)
}