Zum Inhalt springen
Pipfold

Der Deploy war grün, der Spieler sah trotzdem das alte Spiel

· auslieferung · werkzeuge

Ein Godot-Web-Export besteht aus vier Dateien, die zusammenpassen müssen: die index.html, ein Loader, die .pck mit den Daten und die .wasm mit dem Programm. Dazu ein Service Worker, der alles ablegt, damit das Spiel auch ohne Netz startet.

Genau diese Zwischenschicht ist das Problem. Ein Service Worker ist dafür da, alte Dateien auszuliefern. Wenn er die falsche Vorstellung davon hat, welche alt sind, liefert er sie eben weiter — beliebig lange.

Der Befund

Nach dem Ausliefern lief im Browser weiter der vorherige Build. Nicht ein paar Minuten: dauerhaft, über mehrere Neuladen hinweg. Am Server lag die neue Fassung, mit no-store in der Antwort. Nachgemessen kam trotzdem die alte an.

Die Ursache

Cloudflare überschrieb Cache-Control für .js mit der Browser-TTL der Zone — vier Stunden. Damit blieb der alte index.service.worker.js im Browser gültig, der alte Cache blieb gültig, und der Spieler lief weiter im alten Build.

Das ist die unangenehme Sorte Fehler: Jede einzelne Stelle sieht korrekt aus. Der Server setzt die richtige Kopfzeile. Der Worker macht, was ein Worker macht. Der Browser hält sich an das, was ihm gesagt wurde. Nur wurde ihm etwas anderes gesagt, als wir geschrieben hatten.

Zwei Wege, und wir sind beide gegangen

Der eine braucht Cloudflare-Zugriff: browser_cache_ttl = 0 setzen — „Respect Existing Headers“. Das ist die richtige Einstellung, und sie steht jetzt so. Zurückdrehen ginge mit

PATCH /zones/<id>/settings/browser_cache_ttl   {"value": 14400}

Der andere braucht ihn nicht — und das ist der wichtigere. deploy.sh schreibt beim Export den Build-Fingerabdruck in die Dateinamen von Loader und Service Worker:

index-a58ca516be.js
index-sw-a58ca516be.js

und zieht die Verweise in index.html und im Worker mit. Die index.html selbst wird als no-store ausgeliefert — das respektiert Cloudflare —, holt also immer die neuen Namen. Ein alter Cache-Eintrag kann damit keine alte Fassung mehr am Leben halten: er zeigt auf einen Dateinamen, den niemand mehr anfragt.

Verworfen: auf die richtige Einstellung vertrauen. Sie steht jetzt richtig — aber sie steht in einem Konto, das jemand anders auch bedienen kann, und sie gilt je Zone. Der Fingerabdruck im Dateinamen ist Gürtel und Hosenträger, und er kostet zehn Zeilen im Auslieferungsskript. Eine Einstellung, deren Verletzung man erst Wochen später bemerkt, ist keine Zusicherung.

Live nachgemessen, nicht angenommen

Der Beleg: ein Browser, der noch auf dem alten Worker (index.service.worker.js) hing, lief nach einem normalen Neuladen auf index-sw-a58ca516be.js und dem neuen Loader. Kein Leeren des Caches, kein Entwicklermodus.

Das ist der Unterschied zwischen „behoben“ und „müsste behoben sein“. Am Quelltext gemessen sieht beides gleich aus.

Ein Nachtrag, der fast vier Wochen dauerte

Diese Geschichte hatte einen zweiten Teil. Dieselbe Zoneneinstellung stand für pipfold.com noch immer auf 14400, als die Domain im August dazukam — und sie hat dort dasselbe angerichtet, nur an einer anderen Datei. Aufgefallen ist es erst, als eine auf dem iPhone abgelegte Fassung bei jedem Öffnen 39 MB neu lud.

Eine Falle, die man einmal verstanden hat, schnappt beim nächsten Hostnamen trotzdem wieder zu. Der voriger Wert liegt inzwischen in ~/.config/mana/cf-browsercachettl-pipfold-vor-20260819.json — damit die nächste Zone wenigstens nachschlagen kann, wonach sie suchen muss.


Nebenbefund aus demselben Tag, weil er lange nach einem Fehler im Spiel aussah: Standbilder mitten im Würfelwurf. Der Wurf blieb hängen, die Würfel standen in der Luft. Das war kein Fehler in der Bewegung, sondern die Drosselung eines Hintergrund-Tabs in Chrome — document.hidden, requestAnimationFrame feuert nicht. Vor solchen Diagnosen erst document.visibilityState prüfen.

← Devlog