perf(live): Debounce 600 ms, Sync-Versionen nur noch 5 Minuten (D79)
Gemeldet: ~3 s Verzoegerung zwischen zwei Browsern. Zerlegt statt geraten - von 1,73 s gemessenem Weg A->B entfallen 1,67 s auf die Wartezeit vor dem Senden. Alles andere sind zusammen ~70 ms. Zwei Verdaechtige sind freigesprochen: Der Server weckt den wartenden Feed 39 ms nach dem PATCH (isoliert per curl, ohne Browser), und der Apache der produktiven Instanz haelt den Long-Poll die vollen 25 s durch und schliesst sauber mit 204 - kein Fenster ohne offenen Feed, kein 5-Sekunden-Fehlerpfad. Produktiv kommen ~130 ms Rundlauf je Anfrage dazu. Der Debounce bleibt ein Debounce (kein Takt): Wer durchtippt, erzeugt weiterhin keine Version. Der Grund fuer die 1,5 s stammte aus der Rate-Limit-Disziplin des Etherpad-Konzepts - und Etherpad ist ausgebaut (D78). Die Aufbewahrung zahlt die haeufigeren Pushes: Jede Version speichert den ganzen Text, und die Frist entscheidet einzig, ob ein zurueckgefallener Client ein Diff oder den Volltext bekommt. Nutzersichtbar sind die Meilensteine, und die werden nie verdichtet; zurueckfallen kann nur ein ruhender Feed (Hintergrund-Tab). Zusammen sinkt die Spitze je aktiv getipptem Dokument von 115 MB auf 24 MB (49-kB-Plan, Dauertippen). Dabei gefunden: Eine Schreibpause laenger als die Frist war mit einer Stunde der Ausnahmefall und ist mit fuenf Minuten der Normalfall. Dass die letzte Sync-Version davor nicht verlorengeht, haengt allein daran, dass recordHistory() zuerst befoerdert und danach verdichtet - sonst loeschte die Verdichtung genau den Stand, den die Befoerderung gleich zum Meilenstein gemacht haette. Die Reihenfolge hat jetzt eine Zusicherung; vertauscht faellt genau der danach benannte Test. Werkzeuggrenze notiert: Der Automatisierungs-Browser zeigt seine Flaeche nicht an, Chrome drosselt Timer verborgener Seiten auf 1 Hz (gemessen: ein blanker setTimeout(600) feuert nach 999-1053 ms). Ein Sub-Sekunden-Debounce ist dort grundsaetzlich nicht messbar. 501 Frontend-Tests, 139 Backend-Tests.
This commit is contained in:
+1
-1
@@ -102,7 +102,7 @@ Dokumenten-Menü: Er lädt den aktiven Plan hoch, schaltet dorthin um und schrei
|
|||||||
den Link in die Adresszeile und in die Zwischenablage. Diesen Link weitergeben —
|
den Link in die Adresszeile und in die Zwischenablage. Diesen Link weitergeben —
|
||||||
die Adresse ist die Einladung, und wer sie kennt, kommt hinein.
|
die Adresse ist die Einladung, und wer sie kennt, kommt hinein.
|
||||||
|
|
||||||
**Das Textfeld bleibt beschreibbar.** Nach 1,5 s Ruhe schickt der Editor die
|
**Das Textfeld bleibt beschreibbar.** Nach 0,6 s Ruhe schickt der Editor die
|
||||||
Änderung als Zeilen-Diff; ein offener Abruf hält die Gegenrichtung bereit und
|
Änderung als Zeilen-Diff; ein offener Abruf hält die Gegenrichtung bereit und
|
||||||
spielt fremde Änderungen ein. **Die Schreibmarke wandert mit** — fügt jemand
|
spielt fremde Änderungen ein. **Die Schreibmarke wandert mit** — fügt jemand
|
||||||
oberhalb Zeilen ein, bleibt sie an ihrer Stelle im Text.
|
oberhalb Zeilen ein, bleibt sie an ihrer Stelle im Text.
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ server"**: it uploads the active plan, switches to it, and puts the link in the
|
|||||||
address bar and on your clipboard. Share that link — the address is the
|
address bar and on your clipboard. Share that link — the address is the
|
||||||
invitation, and knowing it is what grants access.
|
invitation, and knowing it is what grants access.
|
||||||
|
|
||||||
The text area stays **writable**. After 1.5 s of quiet the editor sends the
|
The text area stays **writable**. After 0.6 s of quiet the editor sends the
|
||||||
change as a line diff; an open request holds the other direction ready and plays
|
change as a line diff; an open request holds the other direction ready and plays
|
||||||
foreign changes in. **The caret travels with them** — if someone inserts lines
|
foreign changes in. **The caret travels with them** — if someone inserts lines
|
||||||
above you, it stays where it was in the text.
|
above you, it stays where it was in the text.
|
||||||
|
|||||||
@@ -301,8 +301,9 @@ Diff-Format und Konfliktlogik bleiben identisch.
|
|||||||
|
|
||||||
### Historie in zwei Ebenen
|
### Historie in zwei Ebenen
|
||||||
|
|
||||||
Mit 1,5 s Debounce wird die Historie sonst zum Transaktionslog: hunderte
|
Ohne Trennung wird die Historie zum Transaktionslog: hunderte
|
||||||
Volltext-Snapshots eines 40-kB-Dokuments je Sitzung. Getrennt werden deshalb:
|
Volltext-Snapshots eines 50-kB-Dokuments je Sitzung — bei 0,6 s Debounce (D79)
|
||||||
|
bis zu 100 je Minute Tippen. Getrennt werden deshalb:
|
||||||
|
|
||||||
- **Sync-Versionen** — tragen das Protokoll (Diffs zwischen beliebigen
|
- **Sync-Versionen** — tragen das Protokoll (Diffs zwischen beliebigen
|
||||||
Versionen), kurzlebig, werden nach einer Weile verdichtet. Danach
|
Versionen), kurzlebig, werden nach einer Weile verdichtet. Danach
|
||||||
|
|||||||
@@ -20,8 +20,16 @@ data class LiveEditingProperties(
|
|||||||
/**
|
/**
|
||||||
* Wie lange Sync-Versionen aufgehoben werden. Danach beantwortet der Feed
|
* Wie lange Sync-Versionen aufgehoben werden. Danach beantwortet der Feed
|
||||||
* ein so altes `since` mit Volltext statt mit einem Diff.
|
* ein so altes `since` mit Volltext statt mit einem Diff.
|
||||||
|
*
|
||||||
|
* **Das ist die einzige Aufgabe des Werts** – nutzersichtbar ist die
|
||||||
|
* Historie der Meilensteine, und die wird nie verdichtet. Wer einen
|
||||||
|
* offenen Feed hat, fällt gar nicht zurück; zurückfallen kann nur, wessen
|
||||||
|
* Feed **ruht** (Hintergrund-Tab, D76-Nachtrag 1) oder gerade neu
|
||||||
|
* ansetzt. Fünf Minuten decken die kurze Abwesenheit ab, alles darüber
|
||||||
|
* bekommt anstandslos den Volltext. Länger aufzuheben kostet dagegen
|
||||||
|
* echten Platz: Jede Version speichert den **ganzen** Text (D79).
|
||||||
*/
|
*/
|
||||||
val syncRetention: Duration = Duration.ofHours(1),
|
val syncRetention: Duration = Duration.ofMinutes(5),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Höchstzahl der Operationen je Anfrage. Ohne Grenze ist ein einzelner
|
* Höchstzahl der Operationen je Anfrage. Ohne Grenze ist ein einzelner
|
||||||
|
|||||||
@@ -35,8 +35,11 @@ werkbaum:
|
|||||||
# Schreibpause, nach der die letzte Version zum Meilenstein wird.
|
# Schreibpause, nach der die letzte Version zum Meilenstein wird.
|
||||||
milestone-pause: 30s
|
milestone-pause: 30s
|
||||||
# Danach wird eine Sync-Version verdichtet; der Feed antwortet auf ein so
|
# Danach wird eine Sync-Version verdichtet; der Feed antwortet auf ein so
|
||||||
# altes "since" dann mit Volltext statt mit einem Diff.
|
# altes "since" dann mit Volltext statt mit einem Diff. Nutzersichtbar ist
|
||||||
sync-retention: 1h
|
# die Historie der Meilensteine - die bleibt unberuehrt. Zurueckfallen kann
|
||||||
|
# nur ein ruhender Feed (Hintergrund-Tab); fuenf Minuten decken die kurze
|
||||||
|
# Abwesenheit ab, und jede Version kostet den ganzen Text (D79).
|
||||||
|
sync-retention: 5m
|
||||||
# Obergrenze fuers Warten am Feed. Gemessen: Apache auf der Zielumgebung
|
# Obergrenze fuers Warten am Feed. Gemessen: Apache auf der Zielumgebung
|
||||||
# haelt einen Long-Poll 30 s durch, seine Zeitgrenzen liegen bei 300 s.
|
# haelt einen Long-Poll 30 s durch, seine Zeitgrenzen liegen bei 300 s.
|
||||||
max-wait: 25s
|
max-wait: 25s
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import io.mockk.mockk
|
|||||||
import io.mockk.CapturingSlot
|
import io.mockk.CapturingSlot
|
||||||
import io.mockk.slot
|
import io.mockk.slot
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
|
import io.mockk.verifyOrder
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import java.time.Clock
|
import java.time.Clock
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
@@ -215,6 +216,37 @@ class DocumentServiceTest {
|
|||||||
verify(exactly = 0) { historyRepository.promoteToMilestone(any(), any()) }
|
verify(exactly = 0) { historyRepository.promoteToMilestone(any(), any()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Eine Schreibpause, die **länger ist als die Aufbewahrungsfrist**, war mit
|
||||||
|
* einer Stunde Frist der Ausnahmefall und ist mit fünf Minuten der
|
||||||
|
* Normalfall (D79). Dass die letzte Sync-Version davor trotzdem nicht
|
||||||
|
* verlorengeht, hängt an einer einzigen Sache: `recordHistory` **befördert
|
||||||
|
* zuerst und verdichtet danach**. In der anderen Reihenfolge löschte die
|
||||||
|
* Verdichtung genau den Stand, den die Beförderung gleich zum Meilenstein
|
||||||
|
* gemacht hätte — ein nutzersichtbarer Stand wäre still weg.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun `nach einer Pause laenger als die Frist wird zuerst befoerdert, dann verdichtet`() {
|
||||||
|
val kurz = LiveEditingProperties(
|
||||||
|
milestonePause = Duration.ofSeconds(30),
|
||||||
|
syncRetention = Duration.ofMinutes(5),
|
||||||
|
)
|
||||||
|
val dienst = DocumentService(repository, historyRepository, clock, kurz, notifier)
|
||||||
|
val doc = sampleDocument(version = 5)
|
||||||
|
every { repository.findById(doc.id) } returns doc
|
||||||
|
every { repository.save(any()) } answers { firstArg() }
|
||||||
|
every { historyRepository.findLatest(doc.id) } returns
|
||||||
|
historyEntry(doc.id, 5, ChangeType.UPDATED, milestone = false)
|
||||||
|
|
||||||
|
clock.moment = clock.moment.plusSeconds(600) // zehn Minuten Pause
|
||||||
|
dienst.update(doc.id, "T", "C", milestone = false)
|
||||||
|
|
||||||
|
verifyOrder {
|
||||||
|
historyRepository.promoteToMilestone(doc.id, 5)
|
||||||
|
historyRepository.compact(doc.id, OffsetDateTime.now(clock).minusMinutes(5))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `nach jeder Aenderung wird jenseits der Aufbewahrungsfrist verdichtet`() {
|
fun `nach jeder Aenderung wird jenseits der Aufbewahrungsfrist verdichtet`() {
|
||||||
val doc = sampleDocument(version = 1)
|
val doc = sampleDocument(version = 1)
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ reverse.
|
|||||||
|
|
||||||
## 2026-08-26
|
## 2026-08-26
|
||||||
|
|
||||||
|
- Changes in a shared document now reach the others after 0.6 s instead of 1.5 s — the wait before sending was almost the whole delay
|
||||||
|
|
||||||
- The Etherpad integration is gone: collaboration now runs through a Werkbaum backend, and an old `?etherpad=` link says so instead of doing nothing
|
- The Etherpad integration is gone: collaboration now runs through a Werkbaum backend, and an old `?etherpad=` link says so instead of doing nothing
|
||||||
- Plans can live on a Werkbaum backend now: open `?live=<document URL>` and everyone edits the same text, seeing each other's changes without reloading
|
- Plans can live on a Werkbaum backend now: open `?live=<document URL>` and everyone edits the same text, seeing each other's changes without reloading
|
||||||
- When two people change the same lines, a bar asks whose version should win — everything else the server merges by itself
|
- When two people change the same lines, a bar asks whose version should win — everything else the server merges by itself
|
||||||
|
|||||||
@@ -6744,3 +6744,78 @@ Textfeld liegt mit dem Zahlenstreifen bündig (1151 + 20 px), die Zeilennummern
|
|||||||
sitzen auf ihren Höhen, Pfad und Stationen werden gezeichnet; der
|
sitzen auf ihren Höhen, Pfad und Stationen werden gezeichnet; der
|
||||||
Legenden-Splitter teilt wie zuvor. 501 Tests (die 24 Pad-Adress-Tests sind mit
|
Legenden-Splitter teilt wie zuvor. 501 Tests (die 24 Pad-Adress-Tests sind mit
|
||||||
`remote.js` gegangen, `padGone` ist dazugekommen).
|
`remote.js` gegangen, `padGone` ist dazugekommen).
|
||||||
|
|
||||||
|
## D79 — Debounce auf 600 ms, Sync-Versionen nur noch fünf Minuten
|
||||||
|
Gemeldet: „der Delay beim Live-Editing zwischen zwei Browsern ist ca. 3 s, das
|
||||||
|
ist zu träge." Nachgemessen und zerlegt, statt am Gefühl zu drehen.
|
||||||
|
|
||||||
|
**Die Wartezeit vor dem Senden IST die Verzögerung.** Der Weg A → B in Zahlen
|
||||||
|
(lokal, Wanduhr beider Tabs):
|
||||||
|
|
||||||
|
| Abschnitt | gemessen |
|
||||||
|
|---|---|
|
||||||
|
| Tippen → PATCH raus | 1666 ms |
|
||||||
|
| PATCH-Rundlauf | 48 ms |
|
||||||
|
| Feed-Antwort bei B | 9 ms danach |
|
||||||
|
| Text steht bei B | 11 ms |
|
||||||
|
|
||||||
|
Alles außer dem Debounce sind zusammen rund 70 ms. Zwei Verdächtige sind
|
||||||
|
ausdrücklich **freigesprochen**: Der Server weckt den wartenden Feed **39 ms**
|
||||||
|
nach dem PATCH (isoliert per curl gemessen, ohne Browser), und der Apache der
|
||||||
|
produktiven Instanz hält den Long-Poll die vollen **25 s** durch und schließt
|
||||||
|
sauber mit 204 — es gibt also kein Fenster ohne offenen Feed und keinen
|
||||||
|
5-Sekunden-Fehlerpfad (`LIVE_RETRY_MS`). Produktiv kommen ~130 ms Rundlauf je
|
||||||
|
Anfrage dazu (gemessen, TLS eingeschlossen), macht ≈ 1,8 s.
|
||||||
|
|
||||||
|
Die gemeldeten 3 s liegen darüber, und der Rest steckt in der Wahrnehmung —
|
||||||
|
das ist keine Ausrede, sondern eine **Eigenschaft des Debounce**: Die Uhr
|
||||||
|
startet bei jedem Tastendruck neu. Gefühlt beginnt die Wartezeit, wenn der
|
||||||
|
Gedanke fertig ist; gerechnet beim letzten Anschlag.
|
||||||
|
|
||||||
|
**Entschieden (Nutzer): 600 ms, und es bleibt ein Debounce.** Erwogen war, aus
|
||||||
|
dem Debounce eine **Drossel** zu machen (regelmäßig senden statt nur in der
|
||||||
|
Pause) — verworfen: Wer durchtippt, soll weiterhin keine Version erzeugen. Der
|
||||||
|
Grund, aus dem D76 bei 1,5 s blieb, trägt ohnehin nicht mehr: Er stammte aus
|
||||||
|
der Rate-Limit-Disziplin des Etherpad-Konzepts, und Etherpad ist ausgebaut
|
||||||
|
(D78).
|
||||||
|
|
||||||
|
**Die zweite Hälfte ist die Aufbewahrung — und sie zahlt die erste.** Jede
|
||||||
|
Version speichert den **ganzen** Text; Sync-Versionen lagen eine Stunde. Wofür
|
||||||
|
ist die Frist überhaupt da? Für genau eines: ob ein zurückgefallener Client ein
|
||||||
|
**Diff** bekommt oder den **Volltext**. Nutzersichtbar ist die Historie der
|
||||||
|
**Meilensteine**, und die wird nie verdichtet. Wer einen offenen Feed hat,
|
||||||
|
fällt gar nicht zurück — zurückfallen kann nur, wessen Feed **ruht**
|
||||||
|
(Hintergrund-Tab, D76-Nachtrag 1). Fünf Minuten decken die kurze Abwesenheit
|
||||||
|
ab, alles darüber bekommt anstandslos den Volltext.
|
||||||
|
|
||||||
|
Zusammen **sinkt** der Platzbedarf, obwohl öfter gesendet wird — gerechnet mit
|
||||||
|
dem mitgelieferten Plan (49 kB), Dauertippen als Spitze:
|
||||||
|
|
||||||
|
| | Versionen/min | Frist | Spitze je Dokument |
|
||||||
|
|---|---|---|---|
|
||||||
|
| vorher | 40 | 60 min | **115 MB** |
|
||||||
|
| nachher | 100 | 5 min | **24 MB** |
|
||||||
|
|
||||||
|
Auf einem Host mit rund 300 MB frei (D76-Nachtrag 3) ist das der Unterschied,
|
||||||
|
der zählt.
|
||||||
|
|
||||||
|
**Dabei gefunden, und erst durch die kurze Frist gefährlich:** Eine Schreibpause
|
||||||
|
**länger als die Aufbewahrungsfrist** war mit einer Stunde der Ausnahmefall und
|
||||||
|
ist mit fünf Minuten der Normalfall. Dass die letzte Sync-Version davor
|
||||||
|
trotzdem nicht verlorengeht, hängt an einer einzigen Sache — `recordHistory()`
|
||||||
|
**befördert zuerst und verdichtet danach**. In der anderen Reihenfolge löschte
|
||||||
|
die Verdichtung genau den Stand, den die Beförderung gleich zum Meilenstein
|
||||||
|
gemacht hätte: ein nutzersichtbarer Stand wäre still weg. Die Reihenfolge war
|
||||||
|
bisher nur eine Anordnung von Anweisungen; sie hat jetzt eine Zusicherung
|
||||||
|
(`verifyOrder`). Gegenprobe: vertauscht fällt genau der danach benannte Test.
|
||||||
|
|
||||||
|
**Werkzeuggrenze, die diese Messung fast verdorben hätte.** Der
|
||||||
|
Automatisierungs-Browser zeigt seine Fläche nicht an, die Seite ist damit
|
||||||
|
wirklich verborgen — und Chrome drosselt Timer verborgener Seiten auf **1 Hz**.
|
||||||
|
Der `document.hidden`-Stub belügt die App, nicht den Scheduler. Nachgemessen:
|
||||||
|
ein blanker `setTimeout(…, 600)` feuert dort nach 999–1053 ms. Ein
|
||||||
|
Sub-Sekunden-Debounce ist in dieser Umgebung **grundsätzlich nicht messbar**;
|
||||||
|
die 600 ms sind gesetzt, und was daneben liegt (Server 39 ms, Rundlauf 130 ms,
|
||||||
|
PATCH → sichtbar 46 ms) ist einzeln gemessen. Dieselbe Lehre wie D25
|
||||||
|
(synthetische `TouchEvent`s), D17-Nachtrag 4 (Bildschirmtastatur) und D53
|
||||||
|
(synthetisches Strg+Z): Was die Umgebung stellt, stellt der Emulator nicht.
|
||||||
|
|||||||
+1
-1
@@ -783,7 +783,7 @@ Dokument-Adresse: `…?live=https://example.org/api/v1/documents/<uuid>`.
|
|||||||
Geschrieben wird **im Editor selbst**, und alle sehen die Änderungen der
|
Geschrieben wird **im Editor selbst**, und alle sehen die Änderungen der
|
||||||
anderen, ohne neu zu laden.
|
anderen, ohne neu zu laden.
|
||||||
|
|
||||||
- **Das Textfeld bleibt beschreibbar.** Nach kurzer Ruhe (1,5 s) schickt der
|
- **Das Textfeld bleibt beschreibbar.** Nach kurzer Ruhe (0,6 s) schickt der
|
||||||
Editor die Änderung als Zeilen-Diff; ein offener Abruf hält die Gegenrichtung
|
Editor die Änderung als Zeilen-Diff; ein offener Abruf hält die Gegenrichtung
|
||||||
bereit und spielt fremde Änderungen ein. Die **Schreibmarke wandert mit** —
|
bereit und spielt fremde Änderungen ein. Die **Schreibmarke wandert mit** —
|
||||||
fügt jemand oberhalb Zeilen ein, bleibt sie an ihrer Stelle im Text.
|
fügt jemand oberhalb Zeilen ein, bleibt sie an ihrer Stelle im Text.
|
||||||
|
|||||||
+2
-1
@@ -117,7 +117,8 @@ verworfene Elemente. Quelle sind ES-Module unter `src/`; `index.html` ist der
|
|||||||
- **Server-Dokumente (`?live=`, D76):** `live.js` hält die entscheidbare Hälfte
|
- **Server-Dokumente (`?live=`, D76):** `live.js` hält die entscheidbare Hälfte
|
||||||
(Adressen, Zeilen-Diff, Rebasen, Cursor-Rechnung, Feed-Regel), `app.js` die
|
(Adressen, Zeilen-Diff, Rebasen, Cursor-Rechnung, Feed-Regel), `app.js` die
|
||||||
Verdrahtung: `loadLive()` holt Text und Version und merkt beides als
|
Verdrahtung: `loadLive()` holt Text und Version und merkt beides als
|
||||||
**Schattenkopie**, `scheduleLivePush()` schickt nach 1,5 s Ruhe das Diff,
|
**Schattenkopie**, `scheduleLivePush()` schickt nach 0,6 s Ruhe das Diff
|
||||||
|
(D79 — die Wartezeit IST die gefuehlte Verzoegerung),
|
||||||
`runFeed()` hält einen Abruf offen (nur im sichtbaren Tab), `putOnServer()`
|
`runFeed()` hält einen Abruf offen (nur im sichtbaren Tab), `putOnServer()`
|
||||||
legt das aktive Dokument an („Auf den Server legen"). Die Basis-Adresse
|
legt das aktive Dokument an („Auf den Server legen"). Die Basis-Adresse
|
||||||
bestimmt `serverBase()` (live.js): `?server=` vor dem offenen Dokument vor
|
bestimmt `serverBase()` (live.js): `?server=` vor dem offenen Dokument vor
|
||||||
|
|||||||
+9
-4
@@ -4316,7 +4316,7 @@ async function loadRemoteSource(){
|
|||||||
wirklich, entscheidet der Mensch (Konflikt-Band unten). Alles andere
|
wirklich, entscheidet der Mensch (Konflikt-Band unten). Alles andere
|
||||||
verschiebt der Server selbst. */
|
verschiebt der Server selbst. */
|
||||||
const LIVE_PARAM = 'live';
|
const LIVE_PARAM = 'live';
|
||||||
const LIVE_DEBOUNCE_MS = 1500; /* Ruhe vor dem Senden; D76 */
|
const LIVE_DEBOUNCE_MS = 600; /* Ruhe vor dem Senden; D76, D79 */
|
||||||
const LIVE_WAIT_S = 25; /* Wartezeit des Feeds; der Server klemmt sie ohnehin */
|
const LIVE_WAIT_S = 25; /* Wartezeit des Feeds; der Server klemmt sie ohnehin */
|
||||||
const LIVE_RETRY_MS = 5000; /* nach einem Netzfehler, bevor der Feed erneut fragt */
|
const LIVE_RETRY_MS = 5000; /* nach einem Netzfehler, bevor der Feed erneut fragt */
|
||||||
|
|
||||||
@@ -4423,9 +4423,14 @@ function adoptLive(doc){
|
|||||||
|
|
||||||
/* ---------- Hinschicken ---------- */
|
/* ---------- Hinschicken ---------- */
|
||||||
|
|
||||||
/* Nach jeder Eingabe neu gestartet: Gesendet wird erst, wenn 1,5 s Ruhe ist.
|
/* Nach jeder Eingabe neu gestartet: Gesendet wird erst, wenn es 600 ms ruhig
|
||||||
Ohne den Takt entstünde je Tastendruck eine Version — die Historie wäre ein
|
ist. Ohne den Takt entstünde je Tastendruck eine Version — die Historie wäre
|
||||||
Transaktionslog, und das Netz hätte zu tun. */
|
ein Transaktionslog, und das Netz hätte zu tun.
|
||||||
|
|
||||||
|
Die Wartezeit **ist** die gefühlte Verzögerung: Gemessen braucht der Weg vom
|
||||||
|
Tastendruck bis zum Text des anderen 1,73 s, davon 1,67 s hier — der Server
|
||||||
|
weckt den wartenden Feed 39 ms nach dem PATCH (D79). Sie bleibt ein
|
||||||
|
Debounce, kein Takt: Wer durchtippt, erzeugt weiterhin keine Version. */
|
||||||
function scheduleLivePush(){
|
function scheduleLivePush(){
|
||||||
if(!liveActive() || liveConflict) return;
|
if(!liveActive() || liveConflict) return;
|
||||||
if(liveState.pushTimer) clearTimeout(liveState.pushTimer);
|
if(liveState.pushTimer) clearTimeout(liveState.pushTimer);
|
||||||
|
|||||||
Reference in New Issue
Block a user