Der Newsfeed liest eine Seite statt der ganzen posts-Tabelle (v7.231.2) - #1325
Merged
Conversation
Die Hauptabfrage des Feeds ("meine Posts plus die der Leute, denen ich
folge", neueste zuerst) hat bisher bei jedem Aufruf die komplette
posts-Tabelle gelesen und per Top-N-Sort auf 21 Zeilen eingedampft. Auf
einer Kopie der Produktionsdaten, aufgefüllt auf 200.000 Posts, dauerte
das 76,7 ms; danach sind es 0,53 ms. Wichtiger als der Faktor ist die
Form: vorher wuchs die Abfrage linear mit jedem jemals geschriebenen
Post, jetzt bleibt sie flach.
Zwei Ursachen, beide additiv behoben, also N-1-sicher in einem Deploy:
1. Auf den Sortierschlüssel (inserted_at DESC, id DESC) gab es keinen
Index. Vorhanden waren nur (user_id, inserted_at) und (user_id,
published_on), und die ODER-Verknüpfung "eigene Posts oder die
meiner Followees" macht die unbrauchbar. Neu: posts_recency_index,
den auch die Tag-Timeline und die Entdecken-Leiste nutzen.
2. Das Moderations-Gate fragt über account_hidden/1 nach den
*versteckten* Accounts (frozen, deactivated, unreachable,
suspended). Der vorhandene users_visible_covering_index deckt genau
die Gegenmenge ab und kann dafür nichts tun, also hat Postgres für
ein paar hundert IDs die ganze users-Tabelle gescannt, einmal pro
Post-Abfrage: fünfmal auf einem einzigen /feed. Neu ist der partielle
users_hidden_index; sein Prädikat sagt bewusst `suspended_until IS
NOT NULL` statt `> now()`, weil ein Index-Prädikat immutable sein
muss.
Dazu die billigere Schreibweise desselben Gates: Vutuv.Moderation.Query
hält seit jeher beide Varianten bereit, und scope_visible/2 hat immer
die teure genommen, obwohl die Feed-Abfrage die Autorenzeile ohnehin
schon joint. Jetzt entscheidet das benannte Binding: wer seinen
Autoren-Join `as: :author` nennt, bekommt account_hidden_row/1 auf die
bereits gelesene Zeile, alle anderen behalten das EXISTS. Das ändert
nichts daran, welche Posts sichtbar sind, nur wie die Frage gestellt
wird. Auf /feed und in der Entdecken-Leiste verschwindet der
users-Scan damit komplett (5 → 0 pro Seitenaufbau).
Der Test für den unreachable-Arm des Gates fehlte bisher als einziger
der vier und kam dazu, weil er genau den neuen Pfad absichert. Die
Indizes selbst haben einen eigenen Regressionstest: sie ändern kein
Ergebnis, also würde ihr Verlust in keinem anderen Test auffallen, die
Seite würde nur mit den Jahren immer langsamer.
Diesen Text hat ein KI-Agent in meinem Namen geschrieben, ungeprüft von mir. Die Arbeit dahinter ist meine, nur das Aufschreiben habe ich delegiert.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Die Hauptabfrage des Feeds ("meine Posts plus die der Leute, denen ich folge", neueste zuerst) hat bisher bei jedem Aufruf die komplette posts-Tabelle gelesen und per Top-N-Sort auf 21 Zeilen eingedampft.
Gemessen auf einer Kopie der Produktionsdaten, aufgefüllt auf 200.000 Posts:
Wichtiger als der Faktor ist die Form: vorher wuchs die Abfrage linear mit jedem jemals geschriebenen Post (bei 200k: 84.513 Treffer geprüft, 115.487 verworfen), jetzt liest sie 60 Zeilen und hört auf.
Die beiden Indizes (rein additiv, N-1-sicher)
posts_recency_indexauf(inserted_at DESC, id DESC), den Sortierschlüssel von Feed, Tag-Timeline und Entdecken-Leiste. Bisher gab es dafür keinen Index: vorhanden waren nur(user_id, inserted_at)und(user_id, published_on), und die ODER-Verknüpfung "eigene Posts oder die meiner Followees" macht die unbrauchbar.users_hidden_index, partiell auf die vier Spalten, die einen Account verstecken.Moderation.Query.account_hidden/1fragt nach den versteckten Accounts,users_visible_covering_indexdeckt genau die Gegenmenge ab und kann dafür nichts tun, also hat Postgres für ein paar hundert IDs die ganze users-Tabelle gescannt — einmal pro Post-Abfrage, fünfmal auf einem einzigen/feed. Das Prädikat sagt bewusstsuspended_until IS NOT NULLstatt> now(), weil ein Index-Prädikat immutable sein muss.Dieselbe Frage, billiger gestellt
Vutuv.Moderation.Queryhält seit jeher beide Schreibweisen des Gates bereit, undscope_visible/2hat immer die teure genommen, obwohl die Feed-Abfrage die Autorenzeile ohnehin schon joint. Jetzt entscheidet das benannte Binding: wer seinen Autoren-Joinas: :authornennt, bekommtaccount_hidden_row/1auf die bereits gelesene Zeile, alle anderen behalten das EXISTS. Welche Posts sichtbar sind, ändert das nicht — nur wie die Frage gestellt wird. Auf/feedund in der Entdecken-Leiste verschwindet der users-Scan damit komplett (5 → 0 pro Seitenaufbau).Tests
unreachable-Arm des Gates hatte als einziger der vier keinen Feed-Test; er kam dazu, weil er genau den neuen Pfad absichert.test/vutuv/repo/feed_indexes_test.exs): sie ändern kein Ergebnis, ihr Verlust würde also in keinem anderen Test auffallen — die Seite würde nur mit den Jahren immer langsamer.Deploy: enthält eine Migration, aber rein additiv (zwei neue Indizes) und damit N-1-sicher.
scripts/deploy.shmigriert im Blue/Green-Ablauf ohnehin vor dem Slot-Wechsel, eine Sonderbehandlung braucht es nicht.Version: 7.231.2
mix precommit: grün (6937 Tests).Diesen Text hat ein KI-Agent in meinem Namen geschrieben, ungeprüft von mir. Die Arbeit dahinter ist meine, nur das Aufschreiben habe ich delegiert.