Files
pad/internal/server
xarmian d5177e7a1f fix(server): the account export's store reads are cancelled at its deadline (BUG-3632) (#2223)
* fix(server): the account export's store reads are cancelled at its deadline (BUG-3632)

The export stopped waiting for a stalled read at its 60s deadline
(BUG-3626, BUG-3659), but the read ran on: Query and QueryRow on a
*sql.DB or *sql.Tx hand the driver context.Background(), even inside a
snapshot begun with the deadline context, so the query kept its
goroutine and pool connection until the lock behind it let go.

store.WithContext(ctx, q) wraps a *sql.DB or *sql.Tx as a Queryer whose
reads run under ctx, so the existing *Q readers bind to a deadline with
no parallel set of Context variants. The export routes all of its reads
through it: the workspace listing (new GetUserWorkspacesQ), the access
preflight, and every read inside the per-workspace snapshot.
withinExportDeadline stays as the backstop for anything that does not
honour the context.

Claude-Session: https://claude.ai/code/session_016Msqj8nRQUNbgdsXxhBBVR

* fix(store): a cancelled guest-grant read reports its error instead of a short list (BUG-3632)

GuestVisibleCollectionIDsQ never checked rows.Err(). Now that the
account export's reads are cancelled at its deadline, a read ended
mid-iteration returned a truncated visible set and no error, which the
export's preflight would read as a restricted workspace (403) rather
than a timeout (503). Both loops now check it.

The store test also pins that a cancelled read's error wraps
context.DeadlineExceeded on both dialects (measured: SQLite returns it
bare, pgx as *pgconn.errTimeout), which is what the export's 503
classification relies on.

Claude-Session: https://claude.ai/code/session_016Msqj8nRQUNbgdsXxhBBVR

* fix(server): a read failing past the export deadline reads as the deadline; the preflight keeps its fail-closed seam (BUG-3632)

Review round r2:
- withinExportDeadline: when a cancelled read's own error wins the race
  with ctx.Done(), it is reported wrapping the deadline, so the export's
  503-vs-500 choice no longer depends on how a driver wraps its cancel
  error.
- accountExportVisibleCollections keeps the BUG-3334
  visibleCollectionIDsFault seam that visibleCollectionIDs honours; the
  ctx-bound read had dropped it.
- store.WithContext's comment says how far cancellation reaches per
  driver: modernc SQLite interrupts only a query's first step.
- The store test's idle check compares against the connections in use
  before the read, not zero.

Claude-Session: https://claude.ai/code/session_016Msqj8nRQUNbgdsXxhBBVR

* test(store): a cancelled read stops an endless result set at the next row (BUG-3632)

Review round r3 (Codex Medium): modernc SQLite interrupts only a query's
first step, so does iteration over a large result set outlive the
deadline? Measured, it does not: database/sql closes the rows at the
next row boundary once the context is done, on SQLite and Postgres. The
new row iterates an endless recursive CTE under a 200ms deadline and
requires the deadline error and the connection back. With the adapter
dropping ctx it runs past 5s.

Claude-Session: https://claude.ai/code/session_016Msqj8nRQUNbgdsXxhBBVR
2026-10-17 19:32:06 -04:00
..