GET /api/v2/paperdex times out on unfiltered requests #102

Closed
opened 2026-03-17 20:52:47 +00:00 by cal · 1 comment
Owner

Description

GET /api/v2/paperdex (no filters) times out and never returns. Confirmed on 2026.3.2 — pre-existing, not a regression from the 2026.3.17 release.

Reproduction

curl -s --max-time 15 -H "Authorization: Bearer $TOKEN" "https://pd.manticorum.com/api/v2/paperdex?limit=5"
# status=000 (connection timeout after 15s)

Root Cause (suspected)

get_paperdex in app/routers_v2/paperdex.py does two full-table operations before any filters are applied:

  1. all_dex.count() — runs a COUNT(*) across the entire paperdex table before filters
  2. Iterates the full result set with model_to_dict(x, recurse=True) — serializes every nested relation for every row

The paperdex table is likely large enough that the unfiltered count + full serialization loop exceeds the nginx proxy timeout.

Fix

  • Move the count() call after filters are applied (or remove it — the response already returns count: len(items))
  • Add a hard limit parameter (e.g. default 500) to prevent unbounded result sets
  • Consider adding a required filter (team_id or cardset_id) for unfiltered requests
## Description `GET /api/v2/paperdex` (no filters) times out and never returns. Confirmed on `2026.3.2` — pre-existing, not a regression from the 2026.3.17 release. ## Reproduction ```bash curl -s --max-time 15 -H "Authorization: Bearer $TOKEN" "https://pd.manticorum.com/api/v2/paperdex?limit=5" # status=000 (connection timeout after 15s) ``` ## Root Cause (suspected) `get_paperdex` in `app/routers_v2/paperdex.py` does two full-table operations before any filters are applied: 1. `all_dex.count()` — runs a `COUNT(*)` across the entire `paperdex` table before filters 2. Iterates the full result set with `model_to_dict(x, recurse=True)` — serializes every nested relation for every row The `paperdex` table is likely large enough that the unfiltered count + full serialization loop exceeds the nginx proxy timeout. ## Fix - Move the `count()` call after filters are applied (or remove it — the response already returns `count: len(items)`) - Add a hard `limit` parameter (e.g. default 500) to prevent unbounded result sets - Consider adding a required filter (team_id or cardset_id) for unfiltered requests
Claude added the
ai-working
bug
performance
labels 2026-03-17 21:31:33 +00:00
Claude added
ai-pr-opened
and removed
ai-working
labels 2026-03-17 21:34:31 +00:00
Collaborator

PR #103 opened: #103

Fix approach:

  • Removed the pre-filter all_dex.count() call (was a full-table COUNT(*) before any WHERE clauses)
  • Added limit parameter (default 500) applied after all filters to cap result sets
  • Materialized the queryset once with list(all_dex) and used len() for the count field — eliminates the second COUNT(*) query
PR #103 opened: https://git.manticorum.com/cal/paper-dynasty-database/pulls/103 **Fix approach:** - Removed the pre-filter `all_dex.count()` call (was a full-table `COUNT(*)` before any `WHERE` clauses) - Added `limit` parameter (default 500) applied after all filters to cap result sets - Materialized the queryset once with `list(all_dex)` and used `len()` for the count field — eliminates the second `COUNT(*)` query
Sign in to join this conversation.
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: cal/paper-dynasty-database#102
No description provided.