Loading
Paginate the Duo Agentic Chat history list
What does this MR do and why?
Depends on
@gitlab/duo-ui16.2.0, released from duo-ui!691 (merged) and bumped in this MR.
The Duo Agentic Chat history tab (/agentic-chat/history, ee/app/assets/javascripts/ai/duo_agentic_chat/components/duo_agentic_chat_history_view.vue) loaded the full list of chat sessions in one request, using getUserWorkflows with first: 99999 and fetch policy network-only. This caused two problems:
- GraphQL clamps
firstto 100, becauseduoWorkflowWorkflowsdoes not overridemax_page_size. Users with more than 100 threads got a silently truncated list. - The query also selected
stalled, which runs a batched checkpoint-presence check against the partitioned checkpoint tables on every list request. In production p95db_durationfor the query is 2-4 seconds.
This MR paginates the history list instead:
- The query asks for
first: 20and returnspageInfo { endCursor hasNextPage }. DuoChatThreadsrenders a load-more sentinel inside its own scroll area through thehas-next-pageandloading-moreprops andload-moreevent added in duo-ui 16.2.0. When it fires, the view callsfetchMorewith the current end cursor (infinite scroll).- New pages are merged into the existing list and deduplicated by thread id.
- Loading a page shows a small inline spinner. The skeleton loader only shows on first load, not on load-more.
stalledis removed from the list query. Stalled threads no longer get dimmed styling in the list, but opening one still shows the inactive state, because the chat state manager checksstalledthrough its own single-workflow query.archivedstays in the query. On the backend this is a plaincreated_atcomparison, with no database cost.- The query file now has the
# @feature_category: duo_agent_platformheader, matching its siblings. - Fetch policy stays
network-only, but now setsnextFetchPolicy: cache-first. In Apollo Client 3.5,fetchMoreends with a reobserve. Under plainnetwork-onlythat reobserve would refetch page one and drop the merged pages. - Deleting a thread now removes its edge from the Apollo cache with
cache.updateQuery, instead of filtering a local array.fetchMoremerges pages from the cache, so a local-only filter would let the deleted thread come back on the next page. - The sentinel has to live inside
DuoChatThreads: the component owns the scroll container, so a trigger placed below it sits under the pinned banner, is always visible, and would load every page immediately. ee/spec/frontend/fixtures/ai_duo_panel_integration.rbnow generates fixtures withfirst: 20, so the MSW fixtures match the real query.@gitlab/duo-uimoves from^16.1.0to^16.2.0in a separate commit. The only change between those versions that this code base uses is the newDuoChatThreadsAPI.
Offset cursors
The resolver uses offset pagination, not keyset. Two known effects:
- A thread created between two page loads shifts offsets by one, so a page boundary can show the same node twice. The merge step deduplicates by id, so this is harmless.
- A thread deleted between two page loads can cause one thread to be skipped, until the tab is reopened.
Accepted for now.
Out of scope / follow-ups
- Server-side search using the existing
search:argument. The duo-ui MR above adds asearchevent with the debounced query; a follow-up can pass it to the query and add a duo-ui prop to skip the client-side filter. Until then the search box only covers loaded pages, and the load-more sentinel hides while a search is active. - Proposal item 4 from the issue (
cache-and-networkplusupdatedAfterdelta refresh). With paginated pages,cache-and-networkwould snap a long cached list back to page one after each refetch, and delete handling needs more cache work. Better reviewed separately. - Proposal item 1 (hydrate the active thread with a single-workflow query). The file named in the issue,
duo_agentic_chat_state_manager_light.vue, is only imported by its own spec. The production state manager,duo_agentic_chat_state_manager.vue, already hydrates from the single-workflow query and does not rungetUserWorkflows. - Database index work continues in !253146 (merged).
References
- Issue: #627320 (closed)
- Related index MR: !253146 (merged)
Screenshots or screen recordings
How to set up and validate locally
- Have a user with more than 20 Duo Agentic Chat threads. Use the rake task
gitlab:duo_workflow:populateto seed workflows, or create threads by chatting. - Open the Duo panel and go to the History tab.
- Confirm 20 threads render. Scroll to the bottom and confirm a small spinner appears and the next 20 threads load, with no skeleton flash.
- In the browser network tab, confirm one
getUserWorkflowsrequest per page, each withfirst: 20, and, for later pages, anaftercursor. - Delete a thread from the list and confirm it disappears without a full list refetch.
- Open a thread that has no checkpoints (stalled) and confirm it still shows the inactive state.
MR acceptance checklist
Check this MR against the Acceptance Checklist before merging.
Edited by Rafa Frederico