remarkbox/docs/tickets/2.md
russell@unturf.com f1cffe2e79 Resolve all 14 tracked tickets (T0-T13)
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.
2026-02-01 20:02:47 -05:00

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:

  1. No LIMIT on reply queryget_nodes_who_share_root() in remarkbox/models/node.py:502-508 loads 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()
    
  2. Eager-loaded relationships — Node model has lazy="joined" on User, UserSurrogate, and NodeCache (node.py:91-95, 124-128), multiplying data per row

  3. Python-side visibility filteringapi/views.py:173-177 loads all nodes then filters in Python with namespace.can_see_node(), instead of filtering in SQL

  4. No pagination — The thread detail endpoint accepts no limit/offset parameters, unlike api_list_threads which does

  5. WSGI timeout — The combined query + serialization exceeds the reverse proxy timeout, producing 502

Proposed Fix

  1. Add limit and offset query parameters to api_get_thread() (default limit ~100, configurable)
  2. Move visibility filtering into SQL (use .filter() for disabled/approved/verified checks before .all())
  3. Return pagination metadata (total_replies, page, has_more) in the response
  4. Consider adding .limit() to get_nodes_who_share_root() as a safety net

Files

  • remarkbox/api/views.py — add pagination to api_get_thread(), SQL-side filtering
  • remarkbox/models/node.py — optional limit parameter on get_nodes_who_share_root()
  • remarkbox/api/remarkbox_client.py — add limit/offset params to get_thread()
  • remarkbox/tests/test_api_views.py — test pagination on thread detail
  • docs/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