Say in grants' log how long each first-view page took (issue 1966, recovery)
mesh/delivery merged: source.main moved to novox/mesh-delivery@7d8e2060a740
mesh/check checking 160fca6a4767 on 7d8e2060a740
mesh/delivery merged: source.main moved to novox/mesh-delivery@7d8e2060a740
mesh/check checking 160fca6a4767 on 7d8e2060a740
The wants page failed 226 times on a call's bound with nothing in the log to show how far from it the answer was. Claude-Session: https://claude.ai/code/session_01NcbgfFfQ4Z9UE5jK32PmE5
This commit is contained in:
@@ -28,6 +28,7 @@ import (
|
|||||||
"os/signal"
|
"os/signal"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/nats-io/nats.go"
|
"github.com/nats-io/nats.go"
|
||||||
"github.com/nats-io/nats.go/jetstream"
|
"github.com/nats-io/nats.go/jetstream"
|
||||||
@@ -117,7 +118,7 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro
|
|||||||
// followed on the owners' views families, which the hearing below hands them.
|
// followed on the owners' views families, which the hearing below hands them.
|
||||||
sayCond := sayVia(ctx, js, account.Module)
|
sayCond := sayVia(ctx, js, account.Module)
|
||||||
fv := grants.NewViews(func(tool string) func(context.Context, string) (views.Answer, error) {
|
fv := grants.NewViews(func(tool string) func(context.Context, string) (views.Answer, error) {
|
||||||
return views.AskVia(grants.Unwrapped(conn.Call), tool)
|
return grants.TimedAsk(tool, views.AskVia(grants.Unwrapped(conn.Call), tool), time.Now, logger.Printf)
|
||||||
}, sayCond, f.Wake, logger.Printf)
|
}, sayCond, f.Wake, logger.Printf)
|
||||||
fv.Rest, fv.Watching = bus, bus
|
fv.Rest, fv.Watching = bus, bus
|
||||||
byState := map[string]*views.Follower{}
|
byState := map[string]*views.Follower{}
|
||||||
|
|||||||
@@ -3905,6 +3905,28 @@ func TestGrants(t *testing.T) {
|
|||||||
t.Errorf("the kept tool runner was not said: %+v", last)
|
t.Errorf("the kept tool runner was not said: %+v", last)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Each first view's page says in the log how long it took (issue 1966: the wants page outgrew the call's bound unseen).
|
||||||
|
t.Run("a first view's page says how long it took", func(t *testing.T) {
|
||||||
|
at := time.Date(2026, 11, 8, 22, 0, 0, 0, time.UTC)
|
||||||
|
now := func() time.Time { at = at.Add(1500 * time.Millisecond); return at }
|
||||||
|
for _, row := range []struct {
|
||||||
|
name string
|
||||||
|
err error
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{"answered", nil, `the first view mesh-delivery-assigner.wants, the page after "", took 1.5s: 2 keys at revision 9, next ""`},
|
||||||
|
{"failed", errors.New("context deadline exceeded"), `the first view mesh-delivery-assigner.wants, the page after "", failed after 1.5s: context deadline exceeded`},
|
||||||
|
} {
|
||||||
|
var lines []string
|
||||||
|
ask := grants.TimedAsk(grants.ReadWants, func(context.Context, string) (views.Answer, error) {
|
||||||
|
return views.Answer{Revision: 9, Values: map[string]json.RawMessage{"a": nil, "b": nil}}, row.err
|
||||||
|
}, now, func(f string, a ...any) { lines = append(lines, fmt.Sprintf(f, a...)) })
|
||||||
|
if _, err := ask(context.Background(), ""); !errors.Is(err, row.err) || len(lines) != 1 || !strings.Contains(lines[0], row.want) {
|
||||||
|
t.Errorf("%s: said %q (%v), want %q", row.name, lines, err, row.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// fakeObjects is the part's object bucket of sealed lists.
|
// fakeObjects is the part's object bucket of sealed lists.
|
||||||
|
|||||||
@@ -479,6 +479,26 @@ func (p *Part) AskFirstViewAgain(caller, state, why string, record func(what, wh
|
|||||||
"says": "the part's process asks it once more at this ask; grants_first_views shows its answer"}, nil
|
"says": "the part's process asks it once more at this ask; grants_first_views shows its answer"}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TimedAsk is ask, saying in the log how long each page took and how many keys it brought, or after how long it failed:
|
||||||
|
// a first view that never answers (issue 1966: the wants page outgrew the call's bound) shows how far from its bound it is.
|
||||||
|
func TimedAsk(tool string, ask func(context.Context, string) (views.Answer, error), now func() time.Time,
|
||||||
|
logf func(string, ...any)) func(context.Context, string) (views.Answer, error) {
|
||||||
|
return func(ctx context.Context, after string) (views.Answer, error) {
|
||||||
|
start := now()
|
||||||
|
a, err := ask(ctx, after)
|
||||||
|
took := now().Sub(start).Round(time.Millisecond)
|
||||||
|
switch {
|
||||||
|
case logf == nil:
|
||||||
|
case err != nil:
|
||||||
|
logf("[mesh-grants] the first view %s, the page after %q, failed after %s: %v", tool, after, took, err)
|
||||||
|
default:
|
||||||
|
logf("[mesh-grants] the first view %s, the page after %q, took %s: %d keys at revision %d, next %q", tool, after, took,
|
||||||
|
len(a.Values), a.Revision, a.Next)
|
||||||
|
}
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Unwrapped is a call whose answer is read as the tool's own text where the runtime wraps it as content (unwrap), as
|
// Unwrapped is a call whose answer is read as the tool's own text where the runtime wraps it as content (unwrap), as
|
||||||
// a first view's answer is decoded (views.AskVia).
|
// a first view's answer is decoded (views.AskVia).
|
||||||
func Unwrapped(call func(key string, body any) (json.RawMessage, error)) func(key string, body any) (json.RawMessage, error) {
|
func Unwrapped(call func(key string, body any) (json.RawMessage, error)) func(key string, body any) (json.RawMessage, error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user