fix: bound empty aitable pagination
This commit is contained in:
1 parent
e14de2b4c2
commit
f486105836
3 files changed
+37
-1
No files matched your search
@@ -15,7 +15,10 @@ import (
|
||||
// drop the envelope fields used by the record projector, turning a successful
|
||||
// read into an empty collection. Keep every remote request on one service page
|
||||
// and aggregate only after each page has passed the CLI's shape validation.
|
||||
const recordQueryServicePageSize = 20
|
||||
const (
|
||||
recordQueryServicePageSize = 20
|
||||
recordQueryMaxConsecutiveEmptyPages = 3
|
||||
)
|
||||
|
||||
type recordQueryWindow struct {
|
||||
Records []map[string]any
|
||||
@@ -36,6 +39,7 @@ func queryRecordWindow(rt *shortcut.RuntimeContext, params map[string]any, limit
|
||||
seen[cursor] = true
|
||||
}
|
||||
window := recordQueryWindow{Records: make([]map[string]any, 0, limit)}
|
||||
consecutiveEmptyPages := 0
|
||||
|
||||
for len(window.Records) < limit {
|
||||
pageSize := minInt(recordQueryServicePageSize, limit-len(window.Records))
|
||||
@@ -67,6 +71,17 @@ func queryRecordWindow(rt *shortcut.RuntimeContext, params map[string]any, limit
|
||||
window.NextCursor = ""
|
||||
return window, nil
|
||||
}
|
||||
if len(records) == 0 {
|
||||
consecutiveEmptyPages++
|
||||
if consecutiveEmptyPages >= recordQueryMaxConsecutiveEmptyPages {
|
||||
return recordQueryWindow{}, fmt.Errorf(
|
||||
"query_records made no progress for %d consecutive pages",
|
||||
consecutiveEmptyPages,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
consecutiveEmptyPages = 0
|
||||
}
|
||||
next := responseCursor(data)
|
||||
if next == "" {
|
||||
return recordQueryWindow{}, fmt.Errorf("query_records page %d reports more data but no next cursor", window.Pages)
|
||||
|
||||
@@ -404,6 +404,26 @@ func TestCrossPlatformCoverageRecordQueryFailureAndContinuationBranches(t *testi
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("changing cursors cannot hide sustained empty pages", func(t *testing.T) {
|
||||
caller := &upsertByKeyCaller{callFn: func(call int, _, tool string, _ map[string]any) (string, error) {
|
||||
if tool != "query_records" {
|
||||
return "", fmt.Errorf("unexpected tool %s", tool)
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
`{"success":true,"data":{"records":[],"hasMore":true,"nextCursor":"empty-%d"}}`,
|
||||
call,
|
||||
), nil
|
||||
}}
|
||||
helpers.InitDepsForTest(t, caller)
|
||||
rt := shortcut.RuntimeContextForTest(&cobra.Command{Use: "query"}, RecordQuery)
|
||||
if _, err := queryRecordWindow(rt, map[string]any{}, 1); err == nil || !strings.Contains(err.Error(), "no progress") {
|
||||
t.Fatalf("sustained empty pages error = %v", err)
|
||||
}
|
||||
if len(caller.calls) != recordQueryMaxConsecutiveEmptyPages {
|
||||
t.Fatalf("query calls = %d, want %d", len(caller.calls), recordQueryMaxConsecutiveEmptyPages)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid shortcut limit", func(t *testing.T) {
|
||||
caller := &upsertByKeyCaller{}
|
||||
helpers.InitDepsForTest(t, caller)
|
||||
|
||||
@@ -232,6 +232,7 @@ func TestCrossPlatformCoverageMinutesWorkflowCompletion(t *testing.T) {
|
||||
}
|
||||
for _, data := range []map[string]any{
|
||||
{"success": false, "errorMsg": "denied"},
|
||||
{"success": "yes"},
|
||||
{"success": true, "result": "bad"},
|
||||
{"success": true, "result": map[string]any{}},
|
||||
{"success": true, "result": map[string]any{"resultMap": map[string]any{}}},
|
||||
|
||||
Reference in new issue
Block a user