Tags

#Aus dem Maschinenraum

Werkstattberichte über hnz.io, technische Umbauten, Importer, Integrationen, Pannen und ihre Lösungen.

Von Trakt zu Floppy: Mein Medientracking zieht nach Hause

Von Trakt zu Floppy: Mein Medientracking zieht nach Hause

Ich hasse es, wenn Sachen nicht funktionieren. Trakt hat ja einfach so die API eingestellt und dadurch wurden bei mir plötzlich keine geschauten Folgen mehr auf der Seite veröffentlicht.

Kein Weltuntergang, aber ärgerlich. Vor allem, weil ich damals ganz bewusst die Entscheidung getroffen hatte, für diesen Teil einen Fremddienst zu nutzen.

Warum überhaupt ein Fremddienst?

Kirby ist geil. Aber für manche Zwecke sind andere Dienste einfach besser darin, Daten einzugeben.

Ich veröffentliche zum Beispiel lieber ein Bild auf Pixelfed, weil die Oberfläche genau dafür gebaut ist. Und genauso habe ich Trakt genutzt: Die nächsten Folgen einer Serie wurden mir direkt angezeigt, mit Bild, Beschreibung und einer simplen Möglichkeit, sie als gesehen zu markieren und zu bewerten.

Hätte ich das alles auch manuell auf hnz.io machen können? Klar.

Hätte ich das dauerhaft durchgehalten? Niemals.

Also hat Trakt mir die gesehenen Folgen samt TMDB-ID geliefert und ich habe die Daten auf hnz.io anschließend über die TMDB API angereichert. Von dort kamen unter anderem Beschreibungstexte und Vorschaubilder.

Trakt war für mich damit eigentlich gar nicht die Datenquelle, sondern vor allem eine bequeme Oberfläche zum Tracken.

Also Alternativen suchen

Nachdem die Trakt-API weggefallen war, musste etwas Neues her.

RYOT war nicht so meins und eigentlich wollte ich nicht noch einen Dienst selbst hosten. Also habe ich mir zunächst einen Account bei Simkl geklickt.

Sieht gut aus. Gefällt mir.

Aus Spaß habe ich dann versucht, meinen kompletten Trakt-Export hochzuladen. Schließlich wirbt Simkl genau mit dieser Möglichkeit. Und dann erschien ein Popup: Der Import funktioniert nur mit einem Bezahl-Account.

Die Begründung dafür fand ich sogar ziemlich sympathisch. Sinngemäß: Wir haben nicht damit gerechnet, dass so viele Daten importiert werden, und jetzt geht uns schlicht der Speicher aus.

Trotzdem war das für mich der letzte Tropfen.

Denn am Ende bleibt Simkl eben auch ein Fremddienst. Theoretisch können sie morgen genauso entscheiden wie Trakt. Oder plötzlich fünf Euro im Monat dafür verlangen, dass ich Sterne vergeben darf.

Also bin ich weiter auf die Suche gegangen.

Yamtrack, nur mit dem einen Feature mehr

Yamtrack sah ziemlich gut aus. Es hatte für mich allerdings ein entscheidendes Manko: Episoden lassen sich nicht bewerten.

Dafür gibt es zwar bereits einen Feature Request, aber ich wollte jetzt eine Lösung und nicht irgendwann.

Dabei bin ich auf Floppygestoßen, einen Fork von Yamtrack, der genau das bereits kann.

Also mit meiner generativen Assistenz schnell einen Build für Cloudron erstellt und installiert.

Und ja: geil. Gefällt mir.

Trakt und Letterboxd einfach importiert

Als Nächstes konnte ich meine Trakt-Daten ohne größere Komplikationen in Floppy importieren. Und weil ich schon dabei war, habe ich direkt auch meine Daten aus Letterboxd hinterhergeschoben.

Selbst meine Watchlist konnte ich damit retten.

Damit liegt jetzt ein ziemlich großer Teil meiner Medienhistorie in einer Anwendung, die ich selbst betreibe.

Podcasts kommen jetzt automatisch dazu

Noch schöner: Meine Podcast-App Pocket Casts bietet ebenfalls eine API.

Dadurch kann ich jetzt auch automatisch erfassen, welche Podcast-Episoden ich gehört habe, und sie anschließend auf hnz.io ins Audiolog übernehmen.

Bisher brauchte ich dafür noch einen Apple Shortcut. Das war nicht wahnsinnig umständlich, aber eben trotzdem ein zusätzlicher Schritt.

Der entfällt jetzt komplett.

Sobald ich eine Episode zu mehr als 50 Prozent gehört habe, landet sie automatisch im Audiolog.

Etwas gewöhnungsbedürftig ist dabei die Darstellung in Floppy. Ähnlich wie bei Serien werden nicht einfach die neuesten Episoden untereinander angezeigt. Stattdessen sieht man zunächst die einzelnen Podcasts und muss sie öffnen, um die jeweiligen Episoden zu sehen.

Das ist eine bewusste Entscheidung und damit kann ich leben.

Gut zu wissen ist außerdem: Der Sync funktioniert nur bei Podcasts, die man in Pocket Casts abonniert hat. Hört man einfach nur spontan eine einzelne Folge eines nicht abonnierten Podcasts, wird sie nicht synchronisiert.

Muss man wissen.

Und plötzlich sind da auch meine Spiele

Zusätzlich lässt sich Floppy unter anderem mit meinem Xbox-Account und Steam verbinden. Überhaupt werden ziemlich viele Dienste unterstützt.

Damit ergeben sich für hnz.io direkt neue Möglichkeiten.

Ich kann zum Beispiel endlich meinen ganz persönlichen Pile of Shame veröffentlichen: Spiele, die längst in meiner Bibliothek liegen, die ich gekauft habe und trotzdem noch nie gestartet habe.

Oder irgendwann meine meistgespielten Spiele nach Stunden.

Das sind genau diese kleinen Datenspielereien, die ich auf meiner eigenen Seite ziemlich spannend finde.

Der Umstieg war erstaunlich unspektakulär

Das eigentliche Umschalten auf hnz.io ging mithilfe meiner generativen Assistenz ziemlich einfach.

Ein paar Werte in der config.php ändern, ein neues Import-Skript schreiben, API-Zugang eintragen, fertig.

Kommentare aus Floppy werden übernommen, die Bewertung auf einer Zehn-Punkte-Skala wird für hnz.io in fünf Sterne umgerechnet und das Skript merkt sich, welche Einträge es bereits importiert hat.

Und damit bin ich wieder ziemlich genau da, wo ich hinwollte:

Die komfortable Oberfläche zum Tracken darf ruhig eine spezialisierte Anwendung übernehmen. Meine eigentlichen Daten und das, was ich daraus veröffentliche, landen aber wieder bei mir.

Und dieses Mal läuft auch die Anwendung dazwischen auf meinem eigenen Server.

Das macht man doch nicht, Trakt

Das macht man doch nicht, Trakt

Auf hnz.io werden gerade keine zuletzt gesehenen Serien mehr importiert. Und es liegt diesmal nicht an mir! Wirklich!Diesmal jedenfalls nicht. Trakt hat meiner kleinen Anbindung schlicht den Stecker gezogen.

Ich finde es völlig in Ordnung, wenn ein Dienst versucht, Geld zu verdienen. Im Gegenteil: Bei vielen Projekten drücke ich sogar die Daumen, dass sie ein tragfähiges Geschäftsmodell finden. Server, Entwicklung und Support bezahlen sich nicht von freundlichen Gedanken. Ich weiß allerdings auch, wie oft genau an diesem Punkt die Enshittification beginnt: Ein Dienst wird erst nützlich, dann unverzichtbar und schließlich Stück für Stück schlechter oder geschlossener.

Monetarisierung allein ist noch keine Enshittification. Heimliche Verschlechterung kommt ihr aber schon ziemlich nahe.

Vorübergehend, aber ohne Vorwarnung

Noch im Februar erklärte Trakt im eigenen Forum, die öffentliche API sei kostenlos und auch ihre kommerzielle Nutzung nicht grundsätzlich eingeschränkt. Anfang August sah die Welt plötzlich anders aus. Kostenlose Konten konnten keine neuen API-Anwendungen mehr anlegen. Bei einigen verschwanden sogar bestehende Anwendungen, die zuvor funktioniert hatten. Wer eine neue App anlegen wollte, bekam stattdessen den Hinweis auf Trakt VIP.

Ganz abgeschaltet hat Trakt seine API also nicht. Der Zugang für kostenlose Entwicklerkonten wurde eingeschränkt und mein Serienimport ist dabei auf der Strecke geblieben. Trakt selbst bezeichnet das als vorübergehende Maßnahme, um Missbrauch zu verhindern. Das kann ein nachvollziehbarer Grund sein. Nur gab es offenbar weder eine vorherige Ankündigung noch einen Zeitplan und nicht einmal eine brauchbare Erklärung für die Menschen, deren Anwendungen plötzlich verschwunden waren.

Das ist der Teil, der mich ärgert. <dass Trakt Geld verdienen möchte: schön und gut. Dass ein Dienst seine Regeln ändert: Schon häufiger erlebt, komme ich mit klar. Aber dass ich die Änderung erst bemerke, weil auf meiner Website nichts mehr ankommt, das ist etwas Blöde.

Und dann war auch der RSS-Feed weg

Als Ausweichroute hätte mir sogar der Verlauf als RSS-Feed gereicht, hatte ich so designt. Wenn die API down ist, war das mein Fallback. Aber wenn ich die Seite jetzt ansurfe, begrüßt mich ein nicht ganz so freundlicher 404-Fehler. Seite nicht gefunden. Und das offenbar nicht nur bei kostenlosen Konten. Selbst Menschen mit laufendem VIP-Abo berichten von plötzlich nicht mehr funktionierenden RSS-Feeds. Eine offizielle Ankündigung dazu habe ich nicht gefunden.

Die API ohne Vorwarnung einschränken und gleichzeitig den RSS-Ausgang zumauern: Das macht man doch nicht. Trakt.

Zum Glück gibt es Ryot nicht bei Cloudron

Zum Glück bietet Cloudron Ryot nicht als fertige App an. Das meine ich nur halb ironisch. Sonst hätte ich vermutlich längst einen Abend, eine Nacht und den darauffolgenden Vormittag damit verbracht, meinen eigenen Medien-Tracker aufzusetzen.

Ryot steht für „Roll Your Own Tracker“ und ist genau die Sorte Projekt, bei der ich gefährlich schnell schwach werde. Die freie, selbst hostbare Anwendung verwaltet nicht nur Filme und Serien, sondern auch Bücher, Spiele, Musik, Podcasts und sogar Trainingseinheiten. Sie kann Daten unter anderem aus Trakt übernehmen, lässt sich mit Jellyfin, Plex, Kodi oder Emby verbinden und bringt Statistiken sowie eine eigene API mit. Die Daten liegen dabei auf dem eigenen Server.

Allerdings ist Ryot kein kleiner Schalter, den ich in Cloudron umlege. Die Installation läuft regulär über Docker, braucht PostgreSQL und je nach Medienart weitere Zugangsschlüssel, etwa für TMDB. Das wäre machbar. Und genau deshalb gefährlich. Ich möchte gerade eigentlich nur wieder drei Serien auf meiner Website anzeigen und nicht aus Versehen eine neue Infrastruktur betreiben.

Erst einmal Simkl

Wahrscheinlich lande ich deshalb vorerst bei Simkl. Der Dienst kann den Trakt-Export als ZIP-Datei übernehmen, einschließlich Verlauf, Merkliste und Bewertungen. Außerdem gibt es eine öffentliche API, über die ich den Serienimport für hnz.io neu bauen kann. Das klingt nach dem deutlich kürzeren Weg.

Natürlich ziehe ich damit erst einmal nur von einem fremden Dienst zum nächsten. Das ist keine große digitale Unabhängigkeitserklärung. Es ist eine pragmatische Lösung, während ich mir in Ruhe anschaue, was dauerhaft funktioniert und wie gut ich meine Daten dort wieder herausbekomme.

Bis dahin gibt es auf hnz.io keinen Serienimport. Ich arbeite dran, da soll sich ZUkunfts-Flo drum kümmern. Und Gegenwarts-Flo ärgert sich leise weiter, dass Trakt nicht nur die Tür abgeschlossen, sondern auch noch den RSS-Fallback zugeschraubt hat.

Was ich stattdessen vorziehe

Viel wichtiger als der Serienimport, ist die neue Blockroll, die ich hier als nächstes ausrollen will. Frank hat drüben bei rivva geschrieben, warum das wichtig ist.. Und Felix hat das Plugin von Christian Pfefferle gleich schon mal für Kirby quasi in Nullzeit angepasst und auf Github veröffentlicht. Danke dir! Und wenn dass dann bei mir ausgerollt ist und funktioniert, dann kümmere ich mich um mein neues, altes Medientracking.

Update: Blockroll ist drin.

Bots, Prewarming und schlechte Queries

Bots, Prewarming und schlechte Queries

Und wieder melde ich mich gut gelaunt und überhaupt nicht gefrustet aus dem Maschinenraum. Heute: Wie meine site()->index()-Abfragen meine Seite fast gekillt hätten. Und ich wie immer selbst daran schuld gewesen bin.

Aber von vorne.

Vielleicht war es keine gute Idee, an ein, zwei Abenden bei Trakt nachzupflegen, welche Serien ich alles schon gesehen habe. Denn das hat mich auf Ideen gebracht. Zum Beispiel: Ich könnte ja dann auch gleich alle Serien und alle Filme auf gesonderten Seiten ausgeben. Und bei Serien könnte ich ja auch noch, einfach weil es so schön ordentlich ist, die Staffeln sortieren und den Serien zuordnen.

Und wenn ich schon dabei bin: Das Ganze könnte ich doch langsam über APIs aufbauen. Klar, die Watchdaten sind nur so halb akkurat, weil ich erst ab diesem Zeitpunkt angefangen habe, Serien sauber zu tracken. Aber ganz ehrlich: Ein bisschen Unschärfe ist immer noch besser als gar keine Daten.

Und wenn ich dann schon ungefähre Watchdaten habe, kann ich ja auch direkt Watchlog-Einträge erzeugen. Wer weiß, wofür die noch gut sind.

Die Konsequenz? Plötzlich hatte meine Seite über 8.000 Watchlog-Einträge. Und über 8.126 Episoden und Filme. Mit einer Watchdauer von sage und schreibe 8,5 Monaten! Aber darum soll es hier gar nicht gehen. Manche Statistiken sollte man vielleicht einfach nicht berechnen.

Worum es geht: Ich hatte plötzlich keine kleine, gemütliche Seite mehr. Sondern ein leicht zickiges Monstrum, das morgens, wenn die Botschwärme aufwachen, meinen Server zuverlässig in die Knie zwingt.

Und schuld daran war natürlich ich.

Ich habe für jede kleine Anzeige von Content einfach site()->index() benutzt. Bedeutet: Immer erst einmal die komplette Website laden, alles durchscannen, tausende Seitenobjekte bauen und dann 99,9 Prozent davon wieder wegwerfen. Klassisches Beispiel:

„Gib mir alle Seiten, filtere Reviews raus, sortiere nach Datum und zeig mir die neuesten fünf.“

Das funktioniert. Aber eben nur so lange, wie die Seite klein ist.

Mit wachsendem Content wurde das zum Problem. Nicht, weil Kirby schlecht ist, sondern weil ich bequem war. „Hol alles, filter später“ ist einfach, aber teuer.

Was ich jetzt stattdessen mache:

Ich scanne die Inhalte genau einmal, ziehe mir daraus nur die relevanten IDs und speichere diese als Cache. Und danach arbeite ich nur noch mit diesem vorbereiteten Datensatz. Keine globalen Scans mehr pro Request, sondern ein vorbereiteter, günstiger Zugriff.

Das habe ich inzwischen umgesetzt für:

  • Tag-Lookups
  • Film- und Serienverzeichnisse
  • Watchlog-Beziehungen
  • den Home-Feed
  • meine Stories-Rail

Das Prinzip ist immer gleich: einmal berechnen, dann cachen.

Dann kam die nächste „gute“ Idee: Prewarming. Also Seiten im Hintergrund vorladen, damit sie für euch schneller da sind. Klingt erstmal sinnvoll.

War es auch. Nur habe ich es natürlich übertrieben.

Ich habe das Prewarming als Self-Requests direkt bei normalen Seitenaufrufen eingebaut. Bedeutet: Ein Request erzeugt mehrere neue Requests auf sich selbst. Solange wenig Traffic da ist, geht das gut. Sobald aber Bots anfangen, parallel meine Seiten zu crawlen, kippt das System.

Und genau das ist passiert.

Bots wie GPTBot, AhrefsBot oder SemrushBot haben parallel angefangen, vor allem die Suche aggressiv abzuklappern. Gleichzeitig liefen meine eigenen Prewarm-Requests. Ergebnis: Alle Worker belegt, nichts geht mehr.

Deshalb habe ich jetzt zwei Dinge geändert:

Die Suche ist für Bots gesperrt
Prewarming passiert nur noch minimal, aktuell nur für die Startseite
Alles andere beobachte ich erstmal.

Die eigentliche Erkenntnis aus der ganzen Geschichte ist aber ziemlich simpel:

site()->index()ist nicht falsch, solange man es nicht übertreibt, so wie ich ich.

Wenn man es bewusst für globale Aufgaben nutzt, ist alles gut. Wenn man es aber im Frontend bei jedem Request mehrfach benutzt, während die Seite wächst und komplexer wird, dann baut man sich ziemlich zuverlässig einen Flaschenhals.

Feature complete? Von wegen.

Feature complete? Von wegen.

Ich musste innerlich lachen, als ich mich dabei erwischt habe, ernsthaft zu denken, dass meine kleine Kirby-Seite jetzt erst einmal feature-complete ist. Das war wirklich ein ernsthafter, ehrlicher Gedanke. Vollkommen unironisch.

Und trotzdem kenne ich mich ja schon auch ein bisschen und habe mich schon beim Denken über mich gewundert und deshalb die letzten Tage wieder etwas im Maschinenraum rumgeschraubt:

Ich habe jetzt meine Tags-Seite versucht zu durchhirnen und aufzubohren. Denn: Ich hasse es, Inhalte zu vertaggen. Aber: Ich finde es auch unglaublich hilfreich, wenn ich es denn mal gemacht habe, um schnell Sachen wiederzufinden.

Was ich dabei gemerkt habe: Selbst wenn es niemanden interessieren sollte, gebe ich mir bei den Bookmarks viel mehr Mühe, wenn ich die hier auf hnz.io reinhaue, als wenn ich sie bei Obsidian ablege oder in die Notiz-App paste. Die gefühlte Öffentlichkeit zwingt mich zu Struktur. Und das finde ich tatsächlich hilfreich.

Ich habe jetzt ein zweigliedriges Tag-System eingeführt. Ich wähle im Artikel einfach die Kategorie aus, Kirby zeigt mir die entsprechenden (vorher angelegten) Unterkategorien an und ich muss nur noch Häkchen setzen. Ich glaube, das bekomme ich hin.

Gleichzeitig habe ich zusammen mit Chatty (Codex mit GPT-5.4) eine Taxonomie-Seite gebaut: Jeder Tag hat jetzt (wie bei WordPress) einen Beschreibungstext und für die Suche noch Synonyme. Damit es egal ist, ob man bei mir nach llm, ki, ai oder so etwas sucht.

Diese Daten habe ich genutzt, um meine Tag-Seite aufzubohren, sodass sie für mich wirklich nützlich ist. Da steht jetzt erst einmal die neue Struktur mit Hauptkategorie und Unterkategorie.

Außerdem lasse ich dort meine Orte und Locations durchsuchbar ausgeben sowie alle Regie-Leute und Haupt-Casts von Serien und Filmen, die ich getrackt habe.

Teilweise nutze ich dafür auch einfach die Loupe-Suche, um Inhalte anzuzeigen. Also eigentlich für alles, was kein klassisches Thema oder Tag ist.

Dabei habe ich gemerkt: Ich muss da noch einmal ran und das besser auf meine Bedürfnisse zuschneiden. Und ich hoffe wirklich, dass ich es so umgebaut habe, dass auch andere gut damit klarkommen.

Denn, in aller Kürze: Ich habe viele Inhalte doppelt. Gefühlt und tatsächlich.

Wenn ich einen Film logge, wird der als Watchlog automatisiert importiert. Wenn ich dazu einen längeren Text schreiben will oder nur einen Kommentar, lege ich einen entsprechenden Post an und sage dem Artikel: Bitte zeige bei der Rezension die Watchcard an. Das hier ist zum Beispiel eine Rezension von mir mit einer zugeordneten Watchcard. Und das die entsprechende Watchcard.

Das führt dazu, dass bei einer Suche wie "Starfleet Academy" sowohl meine Rezensionen als auch die Watchcards aufgetaucht sind. Relevant sind aber eigentlich nur die Rezensionen, weil dort alles aus der Watchcard enthalten ist – plus meine mal mehr, mal weniger sinnvollen Gedanken.

Also: Watchcards ausblenden, wenn ein verknüpfter Text existiert.

Das gleiche Problem habe ich bei Tags. Wenn ich die Watchcard vertagge und später noch einen Beitrag schreibe, habe ich plötzlich doppelte Inhalte unter einem Tag: Watchcard und Rezension.

Das will ich nicht manuell lösen. Ich kenne mich. Das mache ich nicht.

Also habe ich auch hier eine Regel eingebaut: Wenn Inhalte verknüpft sind (Card + Content), werden die Tags übernommen und die Watchcard fliegt aus den Übersichten raus. Wie zum Beispiel hier. Man sieht nur noch die Rezensionen und Artikel – nicht mehr die Watchcards.

Ich muss mal schauen, ob ich mein Tag-Management GitHub-fähig bekomme oder ob das wieder viel zu speziell auf mein Hirn und dieses Projekt zugeschnitten ist.

Wen’s generell interessiert: Ich habe jetzt meine einfachen Plugins für die Sharecard, Exif-Import und AI-Text veröffentlicht.

Sechs Sekunden bis zur Startseite

Sechs Sekunden bis zur Startseite

Manchmal merkt man erst, wie langsam eine Website geworden ist, wenn man sie selbst aufruft und plötzlich Zeit hat, sich einen Kaffee zu holen.

Genau so ging es mir mit meiner Seite hier: Die Startseite brauchte plötzlich bis zu sechs Sekunden, bis überhaupt etwas passierte. Um mich nicht falsch zu verstehen: bis der erste HTML-Response kam. Danach liefen CSS, JavaScript und Bilder völlig normal durch. Das Problem lag also ziemlich offensichtlich vor dem Browser, irgendwo auf dem Server.

Sechs Sekunden sind nicht nur für einen Androiden, sondern generell im Web eine Ewigkeit. Also habe ich mir Chatty dazugeholt, um mir beim Debuggen zu helfen.

Die Ausgangslage war schnell klar: Der erste Request auf hnz.io/ hing. Danach ging alles zügig weiter. Das spricht meistens dafür, dass der Server beim Rendern der Seite lange beschäftigt ist.

Mein erster Reflex war die klassische Performance-Lösung: Caching.

Der erste Versuch: Staticache

Kirby hat von Haus aus schon ein Caching-Modul an Board, ich habe mich aber zusätzlich noch für Staticache entschieden. Das berechnet im ebsten Fall eine Seite bis zur nächsten Änderung einmal vor und gibt dann die Vorberrechnete Version an die Browser weiter.

Zusätzlich habe ich die gzip-Komprimierung eingeschaltet. Gzip ist eine Technik, mit der Textantworten wie HTML oder CSS kleiner gemacht werden, bevor sie übertragen werden. Das spart Bandbreite und beschleunigt die Übertragung.

Der erste Test brachte allerdings eine kleine Überraschung: Die Startseite brauchte immer noch mehrere Sekunden.

Wenn Caching nicht hilft

Das war der Punkt, an dem klar wurde, dass der Cache gar nicht das eigentliche Problem löst.

Wenn eine Seite trotz Cache mehrere Sekunden braucht, dann passiert eines von zwei Dingen:

  • Entweder der Request landet gar nicht im Cache.
  • Oder der Request muss trotzdem noch viel dynamische Logik ausführen.

Raten bringt an dieser Stelle nichts. Also haben Chatty und ich angefangen zu messen.

Ein kleiner Performance-Schalter

Ich wollte eine möglichst leichte Lösung, etwas, das direkt auf dem Server mitläuft und nur dann aktiv ist, wenn ich es brauche und habe ich einen kleinen Schalter eingebaut.

Wenn man die Seite mit ?perf=1 aufruft und eingeloggt ist, schreibt die Anwendung Performance-Daten in ein Log. Zusätzlich erscheinen Timing-Informationen im HTTP-Header, die ich dann im Browser anzeigen kann.

Gemessen wurden drei Dinge:

  • Bootstrap: der Start von PHP und Kirby
  • Renderzeit: das eigentliche Erzeugen der HTML-Seite
  • Gesamtzeit: alles zusammen

Zusätzlich habe ich kleine Marker eingebaut, mit denen man bestimmte Stellen im Code messen kann. Wenn ein Snippet oder eine Funktion auffällig langsam ist, taucht sie im Log sofort auf.

Das Ganze ist im Grunde ein sehr einfacher Profiler. Nicht besonders elegant, aber extrem hilfreich.

Die Spur führt ins Rendering

Die Messungen zeigten ziemlich eindeutig, wo die Zeit verloren ging und zwar, und das hat mir ungemein geholfen, nich im Netzwerk, DNS oder im TLS-Handshakke. Die ganze Zeit ging für das PHP-Rendering der Seite drauf: Der Server war mehrere Sekunden damit beschäftigt, die Startseite zu berechnen, bevor überhaupt etwas an den Browser geschickt wurde.

Damit war klar, dass wir uns meine Templates und Snippets anschauen müssen. War ja irgendwie auch klar.

In Kirby sind Snippets kleine wiederverwendbare Template-Blöcke. Man kann sie sich wie Komponenten vorstellen, die HTML generieren.

Viele kleine Verzögerungen

Die Logs zeigten ein interessantes Muster: Der heftigste Block war die Liste der Beiträge auf der Startseite. Das Snippet, das die Feed-Karten erzeugt, brauchte mehrere Sekunden.

Innerhalb dieses Blocks tauchten immer wieder ähnliche Zeitwerte auf. Viele einzelne Schritte lagen jeweils bei ungefähr 300 Millisekunden.

Das klingt erstmal nicht dramatisch, aber wenn ein Feed zehn Beiträge hat und jeder Beitrag mehrere solcher Schritte ausführt, summiert sich das schnell auf mehrere Sekunden.

Ein Beispiel waren Social-Links aus dem IndieConnector. Dabei handelt es sich um Funktionen, die automatisch prüfen, ob ein Beitrag auf Mastodon oder Bluesky veröffentlicht wurde und welche URL dazu gehört. Die hatte ich mal auf die Schnelle eingebaut. Und nun ja.

Man sollte nichts auf die Schnelle einbauen.

Niemals.

Zwar ist die Auflösung auf einer Detailseite völlig okay. Im Feed bedeutet sie aber, dass dieselben Informationen mehrfach berechnet werden.

Dazu kamen kleine Dinge wie Bild-Fallbacks oder Kommentarzählungen, die ebenfalls für jeden Beitrag erneut ausgeführt wurden. Teilweise mehrfach.

Der entscheidende Schritt

Die Lösung bestand letztlich darin, zwei verschiedene Kontexte sauber zu trennen: Die Startseite ist ein Listing. Sie zeigt viele Beiträge gleichzeitig. In diesem Kontext muss alles möglichst leichtgewichtig sein.

Eine Detailseite dagegen zeigt nur einen einzigen Beitrag. Dort kann man sich mehr Rechenzeit leisten.

Also habe ich einige Funktionen im Feed bewusst abgeschaltet oder vereinfacht.

  • Social-Links werden im Listing nicht mehr komplett aufgelöst.
  • Kommentarzahlen greifen auf bereits berechnete Metriken zurück.
  • Einige Bildlogiken wurden reduziert.

Die Detailseiten behalten weiterhin die vollständige Funktionalität.

Das Ergebnis ist eine klare Trennung: Der Feed bleibt schnell, während Detailseiten alle Informationen anzeigen.

Und Chatty hat dabei geholfen, die kritischen Stellen zu identifizieren und die Messungen sauber auszuwerten.

Ein kleiner Nebeneffekt

Während wir ohnehin am Feed gearbeitet haben, habe ich auch die Bildgrößen angepasst.

Die Startseite lädt jetzt kleinere Vorschaubilder. Große Originale werden erst geladen, wenn man sie tatsächlich öffnet.

Das reduziert Bandbreite und sorgt dafür, dass der Browser weniger Arbeit beim Layout hat. Der eigentliche Performance-Gewinn kam aber weiterhin vom Server.

Was ich aus der Geschichte gelernt habe

Die wichtigste Lektion ist überraschend simpel:

  • Performanceprobleme löst man nicht durch Rumratwn, sondern durch Messungen Und die Spurensuche hat echt Spaß gemacht, war mein erstes Mal.
  • Caching ist hilfreich, aber kein Allheilmittel. Wenn der Flaschenhals im Rendering liegt, muss man verstehen, was genau dort passiert.
  • Oft sind es nicht große, offensichtliche Probleme, sondern viele kleine Kosten pro Eintrag.
  • Baue niemals auf die schnelle irgendetwas ein, ohne dir vorher Gedanken zu machen oder es zumindest zu testen.