Skip to content

fix(runs): honor Offset in ListActions so WatchActions loads all child actions#7585

Draft
pingsutw wants to merge 8 commits into
mainfrom
fix/list-actions-offset
Draft

fix(runs): honor Offset in ListActions so WatchActions loads all child actions#7585
pingsutw wants to merge 8 commits into
mainfrom
fix/list-actions-offset

Conversation

@pingsutw

Copy link
Copy Markdown
Member

Why are the changes needed?

WatchActions returned children_phase_counts capped at 100 no matter the real child count (reproduced with a 10,000-element map task).

Root cause is a pagination contract mismatch:

  • WatchActions.listAndSendAllActions (runs/service/run_service.go:1262) loads the initial snapshot by offset paging:
    const pageSize = 100
    for {
        batch := ListActions(ctx, ListResourceInput{Filter: run, Limit: pageSize, Offset: offset, sort created_at ASC})
        rsm.upsertActions(batch)            // feeds the run-state tree -> ChildPhaseCounts
        if len(batch) < pageSize { return }
        offset += pageSize
    }
  • but the repo ListActions (runs/repository/impl/action.go) only implemented keyset paging (CursorToken) and silently ignored Offset — there was no OFFSET clause in the query.

So every iteration ran the identical query and re-read the same first page. Consequences:

  1. Only the first pageSize (100) actions ever entered the run-state tree, so attachChild/ChildPhaseCounts only ever counted 100 children → children_phase_counts capped at 100.
  2. For any run with more than one page of actions, len(batch) < pageSize was never true, so listAndSendAllActions never returned and the live-update loop (updatesCh) was never reached — the watch got stuck replaying the first page. (Runs with <100 actions worked, which is why this only showed up at scale.)

The ListResourceInput struct already documents Offset as a supported, cursor-exclusive option (runs/repository/interfaces/common.go); it just wasn't wired into the query.

What changes were proposed in this pull request?

Add the OFFSET clause to ListActions when Offset > 0:

queryBuilder.WriteString(" LIMIT ?")
args = append(args, input.Limit+1)

if input.Offset > 0 {
    queryBuilder.WriteString(" OFFSET ?")
    args = append(args, input.Offset)
}

This makes the existing offset loop walk all actions and terminate. (Longer term the snapshot loop could move to the keyset CursorToken the repo also supports, but that needs the cursor direction fixed for the ASC sort plus a (created_at, name) tiebreaker — out of scope here.)

How was this patch tested?

Added TestListActionsOffsetPagination (runs/repository/impl/action_test.go): creates 150 root actions and asserts that

  1. the page at Offset=50 starts at r050 (not r000), and
  2. walking by offset covers all 150 actions exactly once and terminates.

Verified it fails without the fix (the Offset=50 page returns the first page → r000 != r050) and passes with it. Full runs/repository/impl suite passes.

Labels

  • fixed

Check all the applicable boxes

  • All new and existing tests passed.
  • All commits are signed-off.

…d actions

ListActions only implemented keyset (CursorToken) pagination and silently
ignored the Offset field. WatchActions.listAndSendAllActions pages by Offset
(offset += pageSize), so every iteration ran the identical query and re-read the
same first page. Effects:
- only the first pageSize (100) actions ever entered the run-state tree, capping
  children_phase_counts at 100 regardless of the real child count (observed with
  a 10k-element map task).
- for runs with more than one page, len(batch) < pageSize was never true, so the
  initial-snapshot loop never returned and the live-update loop was never reached.

Add the OFFSET clause to ListActions when Offset > 0. Regression test asserts
offset shifts the page window and that walking by offset covers all actions and
terminates.

Signed-off-by: Kevin Su <pingsutw@apache.org>
Copilot AI review requested due to automatic review settings June 23, 2026 23:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes WatchActions getting stuck and undercounting children_phase_counts by making the runs action repository honor ListResourceInput.Offset during ListActions pagination.

Changes:

  • Add OFFSET support to the ListActions SQL query when input.Offset > 0.
  • Add a regression test ensuring offset-based pagination returns the expected window and can walk the full result set.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
runs/repository/impl/action.go Applies OFFSET in the ListActions query so offset pagination actually advances across pages.
runs/repository/impl/action_test.go Adds a regression test validating Offset shifts the page window and supports full traversal.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread runs/repository/impl/action.go
Comment thread runs/repository/impl/action_test.go
@pingsutw pingsutw marked this pull request as draft June 24, 2026 00:06
@pingsutw pingsutw self-assigned this Jun 24, 2026
@pingsutw pingsutw added this to the V2 GA milestone Jun 24, 2026
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Kevin Su <pingsutw@gmail.com>
Copilot AI review requested due to automatic review settings June 24, 2026 06:55
pingsutw added 4 commits June 23, 2026 23:56
Signed-off-by: Kevin Su <pingsutw@apache.org>
Signed-off-by: Kevin Su <pingsutw@apache.org>
Signed-off-by: Kevin Su <pingsutw@apache.org>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comment on lines +355 to +361
if input.CursorToken != "" && input.Offset > 0 {
return nil, fmt.Errorf("CursorToken and Offset are mutually exclusive")
}
if input.Offset > 0 {
queryBuilder.WriteString(" OFFSET ?")
args = append(args, input.Offset)
}
…tions

A map task bulk-creates thousands of children with identical created_at.
listAndSendAllActions paged the snapshot by Offset sorted on created_at
alone; OFFSET over a non-unique ORDER BY has undefined order among ties, so
pages overlap/skip and the run-state tree loses rows -- children_phase_counts
came up short (e.g. 4.8K instead of 20000). Add a deterministic 'name'
tiebreaker (unique within a run) to the sort so offset paging is stable.

Signed-off-by: Kevin Su <pingsutw@apache.org>
Copilot AI review requested due to automatic review settings June 24, 2026 07:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

Comment on lines 1324 to 1327
SortParameters: []interfaces.SortParameter{
impl.NewSortParameter("created_at", interfaces.SortOrderAscending),
impl.NewSortParameter("name", interfaces.SortOrderAscending),
},
Comment on lines +349 to +357
// Offset-based pagination. Callers either page by CursorToken (keyset) or by
// Offset; the two are mutually exclusive.
if input.CursorToken != "" && input.Offset > 0 {
return nil, fmt.Errorf("CursorToken and Offset are mutually exclusive")
}
if input.Offset > 0 {
queryBuilder.WriteString(" OFFSET ?")
args = append(args, input.Offset)
}
Comment on lines +521 to +523
assert.Len(t, seen, total, "offset paging over tied created_at must cover all actions exactly once")
assert.True(t, sort1.IsSorted(sort1.StringSlice(ordered)),
"the name tiebreaker must give a total order so pages don't overlap/skip")
listAndSendAllActions now passes a (created_at ASC, name ASC) sort; update
TestListAndSendAllActionsUsesAscendingSort to assert both parameters.

Signed-off-by: Kevin Su <pingsutw@apache.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants