Files
maarcadetweet/migrations/appview
tomdeboneandClaude Opus 5 73da8f0140 perf(appview): Profil, Cold-Start-Feed und Follow-Timeline entlasten
Gemessen gegen die Dev-Instanz (3,3 Mio. Posts):

* GET /api/profile/<handle>  9,5 s → 0,04 s
* Cold-Start-Timeline        7,4 s → 0,006 s
* Timeline mit 2300 Follows   28 s → 0,02 s

Drei unabhängige Ursachen, alle drei ein Seq-Scan über die posts-Tabelle:

1. resolve_profile sucht die DID über profiles.LOWER(handle) und, als
   Fallback, über posts.handle. Für beides gab es keinen Index. Auf
   profiles hatte Migration 0007 genau diesen Index entfernt, mit der
   Begründung, jeder Aufrufer leite ohnehin zuerst eine DID ab — das
   stimmt nicht mehr, seit resolve_profile den profiles-Cache zuerst
   befragt.
2. Der Cold-Start-Feed filtert `collection IN (…)` und sortiert nach
   indexed_at. Der vorhandene (collection, indexed_at, uri)-Index taugt
   dafür nicht: mit zwei führenden Werten liefert er keine
   indexed_at-Ordnung mehr. Ein partieller Index über genau das
   Prädikat schiebt den Filter in die Definition und lässt
   (indexed_at DESC, uri DESC) als Sortierschlüssel übrig.
3. Genau dieser neue Index wurde dann zur Falle für den Graph-Zweig:
   der Planer sah einen Index, der schon in indexed_at-Ordnung liefert,
   und nahm an, er treffe früh genug auf n passende Zeilen — bei dünn
   besetzten Followees hieß "früh" 2,87 Mio. verworfene Zeilen. Je nach
   Anzahl bisheriger Ausführungen des Prepared Statements kippte er
   zwischen diesem und dem guten Plan, was intermittierend aussah.

Der Graph-Zweig formuliert die Absicht jetzt aus: pro Followee die
neuesten Posts über ein LATERAL, dann mergen. Damit ist der globale
Scan kein wählbarer Plan mehr, und jede Iteration ist ein begrenzter
Range-Scan auf posts_did_indexed_at_uri_idx. Korrekt ist das, weil die
globalen Top-N immer eine Teilmenge der Vereinigung der Top-N je
Followee sind — deshalb wird pro Followee limit+1 geholt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013HC9HLrUU1LNwkzp8nkDLX
2026-09-09 23:02:47 +02:00
..
2026-07-05 20:01:31 +02:00