High priority fixes: - T0: Profile page now filters comments by namespace (was leaking cross-site) - T1: URI hostnames and namespace names normalized to lowercase (was causing duplicate threads and "stock comments" bug). Includes merge script. - T2: Thread detail API now paginated with SQL-side filtering (was 502 on 267+ reply threads) Features: - T3: GDPR account deletion (tombstone user with scrubbed PII) and data export - T4: Customizable button text and comment labels per namespace - T5: Self-service namespace deletion for owners - T6: @mention notifications with profile links - T7: Webmention receiving endpoint with h-card extraction - T8: Configurable max nesting depth and collapse depth per namespace - T9: AJAX thread title search to prevent duplicates - T10: Browser push notification support (VAPID/service worker) Docs and housekeeping: - T11: Documented thread_uri behavior when moving embeds - T12/T13: Drafted community replies for resolved feature requests - Collapse depth defaults to infinite (load-more disabled unless configured) 364 tests pass, 4 skipped.
2.3 KiB
T2: Large thread fetch causes 502
Status: resolved Priority: high Source: production (www.remarkbox.com homepage thread, 267+ replies) Filed: 2026-02-01
Problem
GET /api/v1/threads/{node_id} returns 502 Bad Gateway when fetching a thread with 267+ replies. The www.remarkbox.com homepage thread is unfetchable through the API.
Root Cause
Multiple compounding issues:
-
No LIMIT on reply query —
get_nodes_who_share_root()inremarkbox/models/node.py:502-508loads ALL replies with no limit:def get_nodes_who_share_root(dbsession, root_node, order="oldest-first"): nodes = dbsession.query(Node).filter(Node.root_id == root_node.id) # ... order by ... return nodes # no .limit() -
Eager-loaded relationships — Node model has
lazy="joined"on User, UserSurrogate, and NodeCache (node.py:91-95, 124-128), multiplying data per row -
Python-side visibility filtering —
api/views.py:173-177loads all nodes then filters in Python withnamespace.can_see_node(), instead of filtering in SQL -
No pagination — The thread detail endpoint accepts no
limit/offsetparameters, unlikeapi_list_threadswhich does -
WSGI timeout — The combined query + serialization exceeds the reverse proxy timeout, producing 502
Proposed Fix
- Add
limitandoffsetquery parameters toapi_get_thread()(default limit ~100, configurable) - Move visibility filtering into SQL (use
.filter()for disabled/approved/verified checks before.all()) - Return pagination metadata (
total_replies,page,has_more) in the response - Consider adding
.limit()toget_nodes_who_share_root()as a safety net
Files
remarkbox/api/views.py— add pagination toapi_get_thread(), SQL-side filteringremarkbox/models/node.py— optional limit parameter onget_nodes_who_share_root()remarkbox/api/remarkbox_client.py— addlimit/offsetparams toget_thread()remarkbox/tests/test_api_views.py— test pagination on thread detaildocs/api.md— document pagination parameters
Acceptance Criteria
GET /api/v1/threads/{id}returns paginated replies with a default limit- The www.remarkbox.com homepage thread (267+ replies) is fetchable
- Response includes pagination metadata
- Client updated to support pagination