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.

Wenn Trauma auf der Bühne landet

Diese Episode ist schwierig. Ich kann alle verstehen, die mit Erzählweise und Pacing nicht klarkommen und sie deshalb eher frustrierend als gelungen finden. Ich war selbst lange nicht sicher, wo die Folge eigentlich hinwill.

Das liegt vor allem daran, dass sie gleich drei große Themen gleichzeitig aufmacht. Das ist mindestens eines zu viel. Da ist zunächst Lieutenant Tilly, die aufgrund ihrer offenen und freundlichen Art, diesmal nicht als Wissenschaftlerin, sondern als Traumtherapeutin von der Leitung gerufen wird, um eine Theater-AG zu gründen. Dann ist da Tarima, die mit den Ereignissen auf der Miyazaki überhaupt nicht klarkommt und deren posttraumatische Belastungsstörung immer deutlicher hervortritt. Und schließlich Sam, deren Programmierung offenbar einen fundamentalen Designfehler enthält – etwas, das sich nicht einfach reparieren lässt und das sie emotional schwer belastet.

Wir haben also Tarima, die mit der Situation auf der Miyazaki ringt und den Verlust von B'Avi irgendwie verarbeiten muss. Wir haben Sam, die ebenfalls noch an den Folgen leidet. Wir haben die übrigen Kadetten, die nicht wissen, wohin mit ihrer Überforderung. Und wir haben eine Akademieleitung, die selbst nicht so richtig weiß, was man damit eigentlich anfangen soll und deshalb externe Hilfe organisiert. Das ist schon extrem viel Stoff für eine einzige Episode. Und dann kommt noch das Theaterstück Our Town dazu, ein kultureller Referenzpunkt, der für amerikanische Zuschauerinnen und Zuschauer vermutlich sofort verständlich ist. Für mich hingegen überhaupt nicht. Ich kannte das Stück nicht, hatte nie davon gehört und habe beim ersten Schauen viele Anspielungen schlicht nicht verstanden. Und hier wird dann auch klar, warum Star Trek so oft Shakespeare oder andere Klassiker zitiert: Diese Texte funktionieren auch außerhalb der USA. Das alles sorgt bei mir dafür, dass sich die erste Hälfte der Episode ziemlich orientierungslos anfühlt.

Und trotzdem gibt es in dieser Folge Momente, die mich komplett umgehauen haben.

Allen voran Tarima. Die Darstellung ihrer posttraumatischen Belastungsstörung ist großartig und gleichzeitig herzzerreißend. Dass sie nach den Ereignissen vom War College zur Academy versetzt wurde, lässt sich erzählerisch nur halb erklären, aber ich verstehe auch, warum die Autorinnen nicht noch zusätzliche War-College-Figuren in diese ohnehin schon überfüllte Episode beleuchten wollten. Tarima steht nun vor einer Reihe ungelöster Konflikte. Sie muss sich darüber klar werden, was Caleb für sie bedeutet. Sie muss verarbeiten, was auf der Miyazaki passiert ist und vor allem mit den Konsequenzen, die daraus für sie entstanden sind. Und sie muss damit klarkommen, dass das, was ihr bisher Halt gegeben hat, plötzlich verschwunden ist: Struktur, Regeln, Sicherheit.

Besonders deutlich wird das in der Szene im Quartier zwischen ihr und Caleb. Zoë Steiner spielt Tarima hier absolut fantastisch. Der betrunkenen Version ihrer Figur gelingt etwas sehr Schwieriges: Sie bleibt vollkommen ernst. Tarima wirkt in diesem Moment wie jemand, der sich völlig fremdbestimmt fühlt. Wahrscheinlich war es nie ihre eigene Entscheidung, sich nach dem Vorfall mit ihrem Vater diesen Inhibitor-Knopf einsetzen zu lassen. Und das ihr eine neue Version nach der Miyazaki, eingesetzt wurde, wahrscheinlich noch weniger. Und der Wunsch für ihre Versetzung zur Academy stammt definitiv nicht von ihr. Sie fühlt sich allein, missverstanden und kontrolliert. Sie spürt, dass andere an ihrer Autonomie kratzen. Sie will Caleb küssen und selbst das wird ihr verwehrt. Und deshalb stößt sie ihn dann weg. Das Wegstoßen bezieht sich nicht auf Caleb, sondern auf sie selbst: Ich werde dich auf kurz oder lang verletzen. Deshalb werde dich schützen. Ich bin nicht gut für dich. Der Subtext dieser Szene schreit förmlich: Lasst mich doch bitte versuchen, selbst damit klarzukommen. Nehmt mich so, wie ich bin, ich schaffe das schon. Ich brauche dafür kein dummes Theaterstück. Und vor allem brauche ich keine Leute um mich herum, die sich Sorgen machen, weil ich einfach weiß, dass alle Leute, die mir was bedeuten, früher oder später sowieso verschwinden oder Schaden nehmen. Genau das macht diese Szene so traurig und gleichzeitig so ehrlich. Traumata funktionieren selten rational. Natürlich ist aus der Distanz klar, dass alle versuchen, ihr zu helfen. Aber Tarima kann das in diesem Moment nicht sehen.

Parallel dazu erzählt die Folge Sams Geschichte. Sam ist ein photonisches Lebewesen, das mit Glitches kämpft, die niemand erklären kann, und das trotzdem versucht, für seine Freundinnen immer ein Lächeln auf den Lippen zu behalten. Und auch hier gibt es eine Szene, die mich komplett erwischt hat: den Moment, in dem Sam den Doktor bittet, ihre Hand zu halten. Eigentlich ist das ein kleiner Moment, aber er steckt voller Bedeutung. Sam sucht Sicherheit, vielleicht Geborgenheit, vielleicht einfach das Gefühl, dass der Doktor nicht nur Arzt, sondern auch ihr Mentor ist. Und der Doktor verweigert es. Ich saß davor und dachte nur: Du bist ein fast tausend Jahre altes Hologramm, jetzt gib ihr doch einfach deine verdammte Hand! Natürlich stellt sich heraus, dass der Doktor selbst traumatisiert ist, und zwar durch eine alte Voyager-Episode, die ich ehrlich gesagt längst vergessen hatte. Dass ausgerechnet dieses Erlebnis seine Blockade erklärt, wirkt erzählerisch ein wenig konstruiert, passt aber immerhin zum übergeordneten Thema der Staffel: Verlust von Kindern und die Traumata, die daraus entstehen. Gefilmt wurde die Szene auch wieder im Atrium der Academy. Alles andere wäre vermutlich zu teuer gewesen. Regisseurin Andi Armaganian war das sehr bewusst, und sie hat sich deshalb entschieden, die Szenen dort alle in schwarz-weiß ausstrahlen zu lassen.

Die anschließende Montage in Farbe wirkt wie eine moderne Version von „The Inner Light“, unterlegt mit dem Voiceover aus Our Town. Für mich funktionierte das beim ersten Schauen nur teilweise. Die Bilder waren stark, die Atmosphäre ebenfalls, aber ich habe schlicht nicht verstanden, welchen Subtext das alles hat. Was ich verstanden habe: Der Doktor kann Sam heilen, in dem er ihr Vater ist und sie eine Kindheit und Jugend erleben kann, um für das Jetzt die notwendige Resilienz aufbauen zu können. Und dem Doktor hilft es, weil er dadurch auch sein Trauma bezwingen kann.

Interessanterweise verändert das auch den Blick auf die vorherige Episode. Die wirkt jetzt noch unnötiger, durch diese Folge. Nichts hätte sich geändert, wenn dass die Anschlussfolge zu Miyazaki gewesen wäre. Schade.

Am Ende bleibt für mich deshalb eine Folge, die in der ersten Hälfte ziemlich verloren wirkt und in der zweiten Hälfte plötzlich zeigt, was sie eigentlich sein wollte. Eine Episode über Trauma, über Nähe und darüber, wie schwer es sein kann, Hilfe anzunehmen, selbst wenn sie direkt vor einem steht.