feat(backend): Aenderungsfeed per Long Polling (Schritt 4)
GET /documents/{id}/changes haelt die Anfrage offen und antwortet, sobald
sich etwas tut — kumuliertes Diff seit der bekannten Version, dazu die
Ereignisse mit ihrem Absender. Ist die Basis verdichtet oder hat der Client
noch gar nichts, kommt der Volltext statt der Operationen: ein Roundtrip und
ein Sonderzustand weniger als ein eigener Fehlerpfad.
Der Feed arbeitet auf der Historie, nicht am Dokument — ein geloeschtes
Dokument muss sein DELETED noch zustellen koennen.
Blockierend auf virtuellen Threads statt DeferredResult (D76-Nachtrag 5):
So behaelt der Endpunkt die aus der Spezifikation generierte Signatur, und
API-First bleibt fuer ihn unangetastet; ein Wartender kostet trotzdem fast
nichts. Geweckt wird nach dem Commit, nie davor, und ueber einen Stempel, den
der Aufrufer VOR dem Nachsehen liest — sonst ginge ein Signal aus der Luecke
dazwischen verloren.
122 Tests. Das Szenario "ein Wartender wird geweckt" misst die Dauer: Ohne
das bestuende es auch dann, wenn der Wartende bloss in den Timeout liefe und
danach die Aenderung vorfaende. Gegenprobe: Benachrichtigung entfernt ->
genau dieses Szenario faellt; Volltext-Rueckfall entfernt -> genau jenes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ae85503b79
commit
741b41ef11
+33
-2
@@ -133,6 +133,37 @@ Notation nicht (D14).
|
||||
Die Änderung eines Dokuments läuft strikt sequenziell (Sperre je UUID,
|
||||
**außerhalb** der Transaktion — innen gäbe der Proxy sie vor dem Commit frei).
|
||||
|
||||
## Live-Editing: der Änderungsfeed
|
||||
|
||||
`GET /api/v1/documents/{uuid}/changes?since={version}&wait={sekunden}` liefert
|
||||
alles, was seit `since` geschehen ist. Gibt es nichts, hält der Server die
|
||||
Anfrage offen und antwortet **sofort**, sobald eine Änderung eintrifft; sonst
|
||||
**204**, und der Client fragt erneut. Kosten im Leerlauf: eine offene Anfrage
|
||||
je Beobachter, rund 2,4 Requests pro Minute.
|
||||
|
||||
- **Der Feed arbeitet auf der Historie, nicht am Dokument.** Ein gelöschtes
|
||||
Dokument muss sein `DELETED` noch zustellen können — **404** gibt es deshalb
|
||||
nur bei gänzlich unbekannter UUID.
|
||||
- **Ist `since` verdichtet** (oder `0`, also Erstkontakt), kommt statt `ops`
|
||||
der **Volltext**; `fromVersion` fehlt dann. Ein Roundtrip und ein
|
||||
Sonderzustand weniger als ein eigener Fehlerpfad — und über hunderte
|
||||
Versionen hinweg wäre der Cursor ohnehin nicht zu retten.
|
||||
- **Ereignisse:** `CREATED`, `UPDATED`, `DELETED`, `RESTORED`, `ROLLED_BACK`,
|
||||
je mit `clientId` und `displayName` des Absenders. (`RENAMED` kommt mit
|
||||
`PATCH /title`; solange es das nicht gibt, wäre der Typ eine Zusage ohne
|
||||
Deckung.)
|
||||
- **`Cache-Control: no-store`** ist Pflicht: Ein Proxy dürfte sonst eine 204
|
||||
zwischenspeichern, und der Feed stünde still.
|
||||
- `wait` wird serverseitig geklemmt (`werkbaum.live-editing.max-wait`, 25 s).
|
||||
|
||||
Umgesetzt **blockierend auf virtuellen Threads** (`spring.threads.virtual`),
|
||||
nicht mit `DeferredResult`: So behält der Endpunkt die aus der Spezifikation
|
||||
generierte Signatur, und ein Wartender kostet trotzdem fast nichts.
|
||||
Geweckt wird **nach dem Commit** — davor läse ein Beobachter einen Stand, der
|
||||
noch nicht steht. Voraussetzung ist eine **Einzelinstanz**; hinter einem Load
|
||||
Balancer erführe ein Beobachter auf der zweiten Instanz nichts. Begründung:
|
||||
D76-Nachtrag 5.
|
||||
|
||||
## Vorbereitete Erweiterungen
|
||||
|
||||
**Autorisierung**
|
||||
@@ -143,8 +174,8 @@ Die Änderung eines Dokuments läuft strikt sequenziell (Sperre je UUID,
|
||||
(„Angenommen ich bin als … angemeldet").
|
||||
|
||||
**Live-Editing** (Konzept: `docs/live-editing-proposal.md`, Entscheidung: D76)
|
||||
- **Offen:** der Änderungsfeed per Long Polling (`GET /changes`),
|
||||
Master-Passwort für `GET /documents`, Client-Anpassung.
|
||||
- **Offen:** Umbenennen per `PATCH /title` (und damit das Ereignis
|
||||
`RENAMED`), Master-Passwort für `GET /documents`, Client-Anpassung.
|
||||
- `DocumentUpdateRequest.expectedVersion` ist im Vertrag vorgesehen, wird aber
|
||||
noch nicht ausgewertet.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user