Skip to content

perf(a2a): push ListTasks all-sessions query down to SQL#1

Closed
QuentinBisson wants to merge 1 commit into
stack/2197-basefrom
fix/2197-list-user-tasks-sql
Closed

perf(a2a): push ListTasks all-sessions query down to SQL#1
QuentinBisson wants to merge 1 commit into
stack/2197-basefrom
fix/2197-list-user-tasks-sql

Conversation

@QuentinBisson

Copy link
Copy Markdown
Owner

Follow-up to kagent-dev#2187, addressing issue kagent-dev#2197 (Copilot review finding). Stacked on the kagent-dev#2187 branch (feat/a2a-store-backed-task-queries, mirrored here as base stack/2197-base) — review only the single perf(a2a) commit; retarget to main once kagent-dev#2187 merges.

What

collectUserTasks served an all-sessions ListTasks query by loading every session for the user, then every task per session, then filtering, sorting, and paginating in memory — an N+1 query whose latency and memory scale with the user's total task count even for a small page.

This replaces that path with a single ListUserTasks sqlc query that joins session to task, filters, orders by task.id, and paginates server-side, returning COUNT(*) OVER() as the total.

Status filtering (issue option 1)

status and statusTimestampAfter are filtered in SQL via a jsonb cast on task.data — no schema migration (option 1 from the issue, per the maintainer's request to "start with option 1 for status for now").

Rows exist in two persisted shapes and both are matched:

shape protocol_version state string
v1 'v1' TASK_STATE_WORKING
legacy NULL working

Both spellings are passed (status_v1, status_legacy) and matched with IN (...); the two vocabularies never overlap, so no row is silently dropped. data is always a JSON object (json.Marshal output) so ::jsonb never errors; the timestamp cast is guarded by a CASE (ordered evaluation) so a present-but-malformed value can't error the query.

Option 2 (generated column + index) remains the durable path for a later migration.

Behavior preserved

The single-session path keeps its fail-closed GetSession ownership check (a missing/other-user context is an error, not an empty page); the join scopes every path to the caller's own sessions.

Tests

Adds a Postgres integration test (TestListUserTasks) covering both row shapes, cross-user isolation, the single-session predicate, status and timestamp filters, and LIMIT/OFFSET pagination with a stable total. Existing task_query_store unit tests pass unchanged against a fake that mirrors the query semantics.

collectUserTasks loaded every session for a user, then every task per
session, then filtered, sorted, and paginated in memory (an N+1 query
whose latency and memory scaled with the user's total task count even
for a small page). Replace it with a single ListUserTasks sqlc query
that joins session to task, filters, orders by task id, and paginates
server-side, returning COUNT(*) OVER() as the total.

Status and statusTimestampAfter are filtered in SQL via a jsonb cast on
task.data (no schema migration). Rows exist in two shapes — v1
(protocol_version 'v1', state "TASK_STATE_WORKING") and legacy
(protocol_version NULL, state "working") — so both spellings are passed
and matched; the vocabularies never overlap. The timestamp cast is
guarded by a CASE so a malformed value cannot error the query.

The single-session path keeps its fail-closed GetSession ownership
check; the join scopes every path to the caller's own sessions.

Adds a Postgres integration test covering both row shapes, cross-user
isolation, the session predicate, status/timestamp filters, and
pagination.
@QuentinBisson

Copy link
Copy Markdown
Owner Author

Superseded by kagent-dev#2302 (opened on upstream as a draft).

@QuentinBisson
QuentinBisson deleted the fix/2197-list-user-tasks-sql branch July 20, 2026 14:08
@QuentinBisson
QuentinBisson restored the fix/2197-list-user-tasks-sql branch July 20, 2026 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant