A first view's page is read in one request and bounded by time (issue 1966, recovery) #175

Merged
mesh-tools merged 4 commits from fix/1966-views-page-time into main 2026-11-09 08:13:01 +00:00
5 changed files with 15 additions and 14 deletions
Showing only changes of commit 7f8a3d6fc2 - Show all commits
+3 -7
View File
@@ -4,7 +4,6 @@ import (
"encoding/json"
"errors"
"fmt"
"strings"
"time"
"git.novox.be/novox/mesh-sdk/go/busretry"
@@ -115,15 +114,12 @@ type StatePage struct {
Next string `json:"next,omitempty"`
}
// Page is the current values in key order after the key `after`, as many as fit maxBytes (keys and values), at least
// Page is the current values in key order after the key `after`, as many as fit maxBytes (keys and values) or the time budget allows, at least
// one: one request for many values, where Get is one request each. A runtime that does not answer it gives an error
// satisfying errors.Is(err, errors.ErrUnsupported), and the reader asks Get per key instead.
func (s KeptState) Page(after string, maxBytes int) (StatePage, error) {
raw, err := s.ask("page", map[string]any{"after": after, "max_bytes": maxBytes})
func (s KeptState) Page(after string, maxBytes int, budget time.Duration) (StatePage, error) {
raw, err := s.ask("page", map[string]any{"after": after, "max_bytes": maxBytes, "budget_ms": budget.Milliseconds()})
if err != nil {
if strings.Contains(err.Error(), "answers no mesh/state.page") {
return StatePage{}, fmt.Errorf("%w: %v", errors.ErrUnsupported, err)
}
return StatePage{}, err
}
var p StatePage
+6 -1
View File
@@ -150,7 +150,12 @@ type refused struct {
func (r *refused) Error() string { return r.message }
// Is makes a compare-and-set the runtime refused because another writer was first ErrChanged.
func (r *refused) Is(target error) bool { return target == ErrChanged && r.code == CodeChanged }
func (r *refused) Is(target error) bool {
return target == ErrChanged && r.code == CodeChanged || target == errors.ErrUnsupported && r.code == CodeNoMethod
}
// CodeNoMethod is the error code the runtime answers a verb it does not have with (JSON-RPC's method not found).
const CodeNoMethod = -32601
// ErrChanged is a compare-and-set that lost: the key was made or changed by another writer first (novox/hq
// ADR 0259). Told apart by the runtime's error code, never by its words.
+2 -2
View File
@@ -26,8 +26,8 @@ func (k KeptSource) Get(key string) (*Entry, bool, error) {
}
// Page is the values after a key in one request (stdio.KeptState.Page), as FirstView prefers them to Get per key.
func (k KeptSource) Page(after string, maxBytes int) ([]KeyedEntry, string, error) {
p, err := k.State.Page(after, maxBytes)
func (k KeptSource) Page(after string, maxBytes int, budget time.Duration) ([]KeyedEntry, string, error) {
p, err := k.State.Page(after, maxBytes, budget)
if err != nil {
return nil, "", err
}
+3 -3
View File
@@ -201,10 +201,10 @@ type KeyedEntry struct {
}
// Pager is a Source that answers many values in one request: the values in key order after a key, as many as fit
// maxBytes and at least one, and the key to ask after for the rest. FirstView uses it when its source has it, unless
// maxBytes or the time budget and at least one, and the key to ask after for the rest. FirstView uses it when its source has it, unless
// it answers errors.Is(err, errors.ErrUnsupported), when it reads per key.
type Pager interface {
Page(after string, maxBytes int) ([]KeyedEntry, string, error)
Page(after string, maxBytes int, budget time.Duration) ([]KeyedEntry, string, error)
}
// FirstView answers one page of state from its owner's source: the mark first, so no change after the revision it
@@ -226,7 +226,7 @@ func FirstView(owner, state string, values, marks Source, after string, now time
}
}
if p, ok := values.(Pager); ok {
got, next, err := p.Page(after, PageBytes)
got, next, err := p.Page(after, PageBytes, PageBudget)
if err == nil {
for _, e := range got {
a.put(e.Key, e.Entry)
+1 -1
View File
@@ -676,7 +676,7 @@ func (s *pagedSource) Get(k string) (*Entry, bool, error) {
return s.mapSource.Get(k)
}
func (s *pagedSource) Page(after string, _ int) ([]KeyedEntry, string, error) {
func (s *pagedSource) Page(after string, _ int, _ time.Duration) ([]KeyedEntry, string, error) {
if s.unsupported {
return nil, "", errors.ErrUnsupported
}