Der Server rebased selbst: Ist die Basis veraltet, ueberschneiden sich die
Operationen aber nicht mit den zwischenzeitlichen, verschiebt er sie und
akzeptiert. Reines Ablehnen fuehrte zu Starvation — ein Client mit hoher
Latenz kaeme bei fleissigen Mitschreibern womoeglich nie durch. 409 gibt es
nur bei echter Ueberschneidung, mit allem, was der Client zum Weiterarbeiten
braucht, ohne neu zu laden.
Pruefsumme ist Pflicht (422 bei Abweichung): Die Versionsnummer bestaetigt
nur, dass die Basis dieselbe Version ist, nicht dass beide Seiten sie gleich
lesen. clientId + seq machen den Aufruf wiederholbar — im Mobilnetz ist die
verlorene Antwort der Normalfall.
Die Sperre je Dokument liegt ausserhalb der Transaktion: innen gaebe der
Proxy sie vor dem Commit frei, und der naechste Schreiber laese einen Stand,
der noch nicht steht. Deshalb ist LiveEditingService nicht transaktional und
schreibt ueber DocumentService.
Was das Konzept offenliess, ist jetzt entschieden und in D76-Nachtrag 4
begruendet: die Randfaelle der Einfuege-Ueberschneidung, die Trennung von
400 und 422, die gedeckelte Idempotenz im Speicher.
104 Tests, davon 8 Cucumber-Szenarien fuer das Live-Editing. Gegenprobe:
Pruefsumme nicht geprueft, Idempotenz entfernt, veraltete Basis abgelehnt
statt verschoben -> es fallen jeweils genau die danach benannten.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nachtrag 2 nannte "832 MB frei" - ein Schnappschuss, und dazu mehrdeutig
zwischen `free` und `available`. Nachgemessen: 326-358 MB free, 978-1004 MB
available von 3915 MB, dazu ~1 GB Swap belegt.
Falsch war ausserdem die Annahme, ein Managed Webspace koenne den Verbrauch
nicht zuordnen. `hidepid=invisible` verbirgt zwar fremde Prozesse, aber das
systemd-cgroup-Accounting (`systemctl status pacs-<paket>.slice`, so auch im
Hostsharing-Wiki) und die world-readable atop-Aufzeichnungen unter
/var/log/atop/ geben sie her.
Befund: nicht die Datenbanken. clamav-daemon 988 MB gegen mariadb 150 MB und
postgresql 111 MB; alle zwoelf Webspaces zusammen 100 MB. MariaDB haelt
602 MB im Swap - die DBs sind auf je 25 % des RAM provisioniert und verlieren
gegen den Virenscanner.
Fuers Deployment: pacs-mih00.slice hat MemoryMax=3147M - eine Erlaubnis, keine
Reservierung. `-Xmx` gehoert gegen das Freie bemessen; die JVM-Voreinstellung
(~980 MB) ist genau die Groesse, die MariaDBs Puffer verdraengt hat.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RewriteRule [P] ist in der .htaccess erlaubt - gemessen mit einer temporaeren
Regel auf einen lokalen Testprozess, danach vollstaendig zurueckgebaut.
Sofortige Antwort HTTP 200 nach 0,13 s, absichtlich um 30 s verzoegerte
Antwort HTTP 200 nach 30,1 s: Apache haelt die Verbindung durch und puffert
nichts weg. Long Polling mit wait=25 ist damit gemessen tragfaehig, nicht nur
rechnerisch. Die Regel gehoert nach scripts/prod.htaccess, weil deploy-prod.sh
die Datei mit rsync --delete spiegelt.
JDK: eigenes 21 ins Home statt Toolchain auf die installierte 17 senken.
GraalVM Native Image ist vorgemerkt statt verworfen - es spart den Grossteil
des knappen RAM (nur 832 MB frei auf einem geteilten Server), scheitert aber
vorerst an der glibc-Differenz, an Liquibase/Hibernate-Metadaten und an einem
offenen Boot-4-Fehler fuer genau diese Kombination. Kotlin/Native scheidet
grundsaetzlich aus: Spring, Hibernate und JDBC sind JVM-Bibliotheken.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die offenen Punkte der beiden Entwuerfe sind beantwortet und in beide
Dokumente eingearbeitet. Die wichtigsten Aenderungen am Protokoll:
- Der Server rebased selbst; 409 nur noch bei echter Ueberlappung. Reines
Ablehnen fuehrt zu Starvation - ein Client mit hoeherer Latenz kommt bei
fleissigen Mitschreibern womoeglich nie durch.
- Der Feed arbeitet auf der Historie statt am Dokument, sonst koennte er
ausgerechnet das Loeschen nicht melden, das er melden soll.
- Historie in zwei Ebenen: kurzlebige Sync-Versionen und nutzersichtbare
Meilensteine (nach Schreibpause und auf Knopfdruck, wie D54).
- Pruefsumme als Pflichtfeld, clientId + seq gegen doppelt angewendete
Patches, Volltext im Feed fuer zu alte Staende, eigener Aenderungstyp fuer
den Rollback, Titel im Feed-Ereignis.
- Zugriff ueber die unerratbare UUID; GET /documents bekommt ein
Master-Passwort (Spring Security).
Die Client-Instruktion war fuer ein anderes Frontend geschrieben: Sie nannte
CodeMirror, TypeScript, jsdiff und MSW. Werkbaum hat eine rohe textarea,
Vanilla JS und keine Laufzeit-Abhaengigkeiten - CodeMirror bleibt eine eigene
Entscheidung (gemessen: 120 kB gzip Einstieg, danach nur 18 kB fuer alles
Weitere).
Zielumgebung vermessen: Apache 2.4.68 mit MPM event, 1024 Worker,
Timeout 300 - Long Polling traegt dort. Offen bleiben fehlendes HTTP/2,
nur Java 17 statt 21 und der ungeklaerte Weg vom Apache zum Backend.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Liquibase-Schema (`document`, `document_history`) + Rollback-scripts
- Spring Boot with JPA-repositories, entities and services
- REST-API (`/documents`, `/documents/{id}`, `/documents/{id}/history`)
- OpenAPI-specifikation for CRUD-Operationen and history
- config files (`application.yaml`, `Liquibase`, H2 im PostgreSQL-Modus)
- preps for future live-editing/delta-updates
- Exceptions for conflikt- and not-found cases (409/404)
- keeping document hostory even after `delete` for RESTORE functionality