frontend: Pad auf Knopfdruck holen statt zu takten — Etherpad drosselt
Der Hintergrund-Takt aus dem letzten Commit funktionierte in der Praxis nicht:
im Netzwerk-Mitschnitt stapelten sich Anfragen und wurden abgebrochen
("cancelled"). Die Ursache ist nicht Langsamkeit, sondern ein Rate Limit —
Etherpads `importExportRateLimiting` ist serienmäßig an und lässt 10 Abrufe je
90 s und IP zu; der Takt wollte 36. Jenseits davon antwortet die Gegenseite
nicht mit 429, sondern hält die Verbindung ohne Antwort offen. Nachgemessen:
12:48:12 Abbruch nach 25 s (0 Bytes)
Abbruch nach 25 s (0 Bytes)
12:50:23 HTTP 200 nach 0,436 s <- nach ~40 s Pause
Zwei Minuten totgestellt, dann sofort in 0,4 s da. Gegen eine Drosselung kann
ein Takt nicht gewinnen, er erzeugt sie. Also ein Knopf. Damit entfallen
Stabilitätstakt, Sichtbarkeits-Wächter, Anti-Stapel-Riegel und Wiederanlauf.
Der Knopf greift zudem gut mit "Was ist neu?" (D28) zusammen: drücken, und was
seither in Produktion ging, leuchtet auf.
Verhindern UND melden, beides:
- Mindestabstand 10 s zwischen zwei Abrufen. Das ergibt höchstens 9 je 90 s und
bleibt damit beweisbar unter Etherpads Budget — die Drosselung wird gar nicht
erst ausgelöst. Nach einem Abbruch sind wir schon drüber: dann das ganze
Fenster (90 s) abwarten statt weiter dagegen zu rennen.
- Der Knopf bleibt in der Sperre **klickbar** (nur `aria-disabled`, nicht
`disabled`): Ein Klick meldet den Grund und nennt die Grenze samt Restzeit.
Die Meldung landet in `#warn`, das eine Live-Region ist, wird also angesagt.
Ein `disabled` erklärt nichts.
- Eigener Warnungstyp `sourceTimeout` für den Abbruch: `sourceLoad` zeigt auf
CORS und schickte hier auf die falsche Fährte.
- `setPadBusy()` dreht das Symbol während des Abrufs — das können bis zu 20 s
sein, und ohne Zeichen wirkt der Knopf kaputt.
Zwei Fehler des Vorgänger-Commits fielen dabei auf:
- `padSource` und der Takt standen HINTER dem `await` des ersten Abrufs. Ein
einziger Fehlschlag — bei dieser Gegenseite der Normalfall — ließ das Dokument
tot liegen, ohne Knopf und ohne Wiederversuch, bis zum Neuladen der Seite.
Genau das war die vom Nutzer gemeldete Fehlermeldung.
- Kein Erfolg räumte die Warnung des ersten Versuchs weg; sie hätte gestanden,
während der Text längst da war.
Dazu: `deleteDoc()` beendet die Pad-Quelle, sonst legte ein späterer Abruf das
gelöschte Dokument wieder an und aktivierte es auch gleich.
Nebenbefunde, beide nachgemessen und in D31 festgehalten:
- Das *Tippen* im Etherpad-Editor war der letzte offene Punkt. Der Nutzer hat
` - [ ] Layout` eingerückt eingetippt; der Export gibt es zeichengenau
zurück (vier echte Leerzeichen, kein Tab, kein Listen-Markup).
- Einbetten verweigert der Server nicht: kein `X-Frame-Options`, keine CSP mit
`frame-ancestors`, und im Versuch baute das Pad in einem fremdstämmigen Rahmen
seine Editorframes auf. Preis ist `SameSite=Lax` am Autoren-Cookie (jedes
Laden ein neuer Autor; nur serverseitig zu beheben) und der Verlust beider
Richtungen von D25, falls das Textpanel ersetzt wird.
86 Tests (85 -> 86); Schreibschutz zeigt den Text jetzt in Tinte statt grau —
hier wird gelesen, der Plantext ist der Hauptinhalt.
This commit is contained in:
+16
-13
@@ -142,19 +142,22 @@ verworfene Elemente. Quelle sind ES-Module unter `src/`; `index.html` ist der
|
||||
`tests/remote.test.js`): Export-/Timeslider-Pfad, Query, Fragment und
|
||||
Schrägstriche fallen weg, verlangt wird `/p/<name>` am Ende. Name und id sind
|
||||
die **vollständige** Pad-URL (Pad-Namen sind nur pro Instanz eindeutig).
|
||||
`padSource` muss **vor** `loadActiveIntoEditor()` gesetzt werden — daran hängt
|
||||
der Schreibschutz. `pollPad()` hat drei Riegel, jeder aus einem echten Fehler:
|
||||
`padBusy` (höchstens ein Abruf unterwegs — sonst stapeln sich Anfragen und eine
|
||||
spät eintreffende alte Antwort überschreibt neueren Text, im
|
||||
Netzwerk-Mitschnitt beobachtet), `PAD_FETCH_TIMEOUT_MS` (ohne Abbruch bliebe
|
||||
`padBusy` bei hängender Gegenseite für immer zu) und `visibilityState`
|
||||
(+ `visibilitychange`-Handler, der bei Rückkehr sofort holt). Fehlschläge im
|
||||
Takt bleiben **stumm**; nur der erste Ladeversuch warnt. Der Stabilitätstakt
|
||||
(`padPending`) übernimmt erst beim zweiten gleichen Abruf — sonst sieht man die
|
||||
anderen mitten im Tippen. Beim Prüfen im Vorschau-Browser: `visibilityState`
|
||||
ist dort `hidden` (Polling also aus) und HMR lädt bei jeder Quelländerung neu —
|
||||
ein Reload sieht wie eine geglückte Übernahme aus. Marker auf `window` setzen
|
||||
und hinterher prüfen, sonst beweist der Test nichts.
|
||||
`padSource` muss **vor** dem ersten Abruf und vor `loadActiveIntoEditor()`
|
||||
gesetzt werden: Es trägt den Schreibschutz, und scheitert der erste Abruf
|
||||
(Normalfall bei dieser Gegenseite), bleibt so der Neu-laden-Knopf erreichbar
|
||||
statt das Dokument tot liegen zu lassen. Geholt wird **nur auf Knopfdruck**
|
||||
(`refreshPad`) — **kein Hintergrund-Takt wieder einbauen**: Etherpads
|
||||
`importExportRateLimiting` lässt serienmäßig 10 Abrufe je 90 s und IP zu, und
|
||||
jenseits davon hält die Gegenseite die Verbindung ohne Antwort offen (kein
|
||||
`429`), bis der eigene Abbruch sie abreißt — gemessen: zwei Minuten tot, danach
|
||||
0,4 s. Ein Takt gewinnt dagegen nicht, er erzeugt die Drosselung. Deshalb auch
|
||||
`setPadBusy()` (Symbol dreht, bis zu 20 s) und ein **eigener** Warnungstyp
|
||||
`sourceTimeout` für den Abbruch — `sourceLoad` zeigt auf CORS und schickte hier
|
||||
auf die falsche Fährte. `deleteDoc()` ruft `stopPad()`, sonst legte ein späterer
|
||||
Abruf das gelöschte Dokument wieder an und aktivierte es auch gleich.
|
||||
Beim Prüfen im Vorschau-Browser: HMR lädt bei jeder Quelländerung neu, und ein
|
||||
Reload holt den Text beim Erstabruf — sieht wie eine geglückte Übernahme aus.
|
||||
Marker auf `window` setzen und hinterher prüfen, sonst beweist der Test nichts.
|
||||
- Sprung Diagramm ↔ Text (D25): `render.js` schreibt die Parser-Zeilennummer als
|
||||
`data-line` an jeden Knoten (Geister-Knoten bekommen keine). `jumpToLine()` in
|
||||
`app.js` klappt bei Bedarf das Editor-Panel auf (`revealEditor()`), markiert die
|
||||
|
||||
Reference in New Issue
Block a user