A page reads the bucket bound once and stops at its time budget, one value at least; an unknown verb is told by its code (issue 1966)
Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i
This commit is contained in:
@@ -145,21 +145,72 @@ type StatePage struct {
|
||||
Next string `json:"next,omitempty"`
|
||||
}
|
||||
|
||||
// StatePage reads the values in key order after `after` in one pass, as many as fit maxBytes (each key and value) and
|
||||
// at least one, so a bundle asks one request for many values, where StateGet is one request each. It is a read like
|
||||
// StateGet: served to the owner and to every reader, and writes nothing. Next is the last key given when more follow.
|
||||
func (c *Conn) StatePage(module, name, after string, maxBytes int) (StatePage, error) {
|
||||
keys, err := c.StateKeys(module, name)
|
||||
// StatePageBudget is the time a page spends reading when the asker names none: well under the 30 s a reader waits.
|
||||
const StatePageBudget = 10 * time.Second
|
||||
|
||||
// PageClock is the time a page's budget counts by, replaced by a test.
|
||||
var PageClock = time.Now
|
||||
|
||||
// StatePage reads the values in key order after `after` in one pass, as many as fit maxBytes (each key and value),
|
||||
// stopping when budget is spent, at least one value, so a bundle asks one request for many values, where StateGet
|
||||
// is one request each. Access is checked once and the bucket bound once. It is a read like StateGet: served to the
|
||||
// owner and to every reader, and writes nothing. Next is the last key given when more follow.
|
||||
func (c *Conn) StatePage(module, name, after string, maxBytes int, budget time.Duration) (StatePage, error) {
|
||||
s, err := c.issuedState(module, name)
|
||||
if err != nil {
|
||||
return StatePage{}, err
|
||||
}
|
||||
if budget <= 0 {
|
||||
budget = StatePageBudget
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), StateTimeout)
|
||||
defer cancel()
|
||||
var keys []string
|
||||
var get func(key string) (*StateEntry, error)
|
||||
if s.PerNode {
|
||||
fam := c.family(s)
|
||||
if keys, err = fam.Keys(ctx); err != nil {
|
||||
return StatePage{}, err
|
||||
}
|
||||
get = func(key string) (*StateEntry, error) {
|
||||
e, err := fam.Get(ctx, key)
|
||||
if err != nil || e == nil {
|
||||
return nil, err
|
||||
}
|
||||
return &StateEntry{Key: e.Key(), Value: valueOf(e.Value()), Revision: e.Revision(), At: e.Created().UTC()}, nil
|
||||
}
|
||||
} else {
|
||||
kv, err := c.bucket(ctx, s)
|
||||
if err != nil {
|
||||
return StatePage{}, err
|
||||
}
|
||||
if keys, err = keysOf(ctx, kv); err != nil {
|
||||
return StatePage{}, err
|
||||
}
|
||||
get = func(key string) (*StateEntry, error) {
|
||||
e, err := kv.Get(ctx, key)
|
||||
if errors.Is(err, jetstream.ErrKeyNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &StateEntry{Key: e.Key(), Value: valueOf(e.Value()), Revision: e.Revision(), At: e.Created().UTC()}, nil
|
||||
}
|
||||
}
|
||||
sort.Strings(keys)
|
||||
page := StatePage{Entries: []StateEntry{}}
|
||||
started := PageClock()
|
||||
size := 0
|
||||
for _, k := range keys {
|
||||
if after != "" && k <= after {
|
||||
continue
|
||||
}
|
||||
e, err := c.StateGet(module, name, k)
|
||||
if len(page.Entries) > 0 && PageClock().Sub(started) >= budget {
|
||||
page.Next = page.Entries[len(page.Entries)-1].Key
|
||||
break
|
||||
}
|
||||
e, err := get(k)
|
||||
if err != nil {
|
||||
return StatePage{}, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user