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>
Der Zielserver bietet kein HTTP/2, also gilt das Browser-Limit von sechs
Verbindungen je Herkunft - ein dauerhaft offener Long-Poll je Tab engt bei
mehreren Tabs alles andere ein. Der Feed schliesst deshalb bei
visibilitychange und holt beim Zurueckkommen mit dem eigenen since nach.
Ein Hintergrund-Tab braucht keinen Live-Feed; das spart nebenbei
Server-Worker und Akku. Ein SharedWorker waere sauberer, ist aber eine eigene
Baustelle fuer ein Problem, das die einfache Loesung praktisch beseitigt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
backend/CLAUDE.md sah Kotest-Assertions vor, der Code benutzte aber die
JUnit-Assertions. Umgestellt bei 25 Aufrufen in zwei Dateien - jetzt billig,
und Test-Abhaengigkeiten sind unkritisch, weil sie in keinem Artefakt landen.
Wo Kotest spezifischer ist, ersetzt es die handgeschriebenen Meldungen:
shouldContain statt assertTrue(contains, "..."), withClue nur noch dort, wo
der Kontext wirklich hilft.
Gegenprobe per Mutation: eine falsche Erwartung im Service-Test und eine im
BDD-Schritt lassen genau die danach benannten Tests fallen.
CLAUDE.md berichtigt: Das Backend ist nicht mehr "noch nicht bootstrapped",
die Konfiguration heisst application.yaml, die Schichten entsprechen dem
tatsaechlichen Aufbau, und die drei Spring-Boot-4-Fallen von heute sind
festgehalten, damit sie niemanden erneut treffen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Das Backend-Geruest kam unter der Platzhalter-Wurzel com.example.editor
herein und widersprach backend/CLAUDE.md. Umgezogen nach de.werkbaum: 17
Kotlin-Dateien, Gradle-group, apiPackage/modelPackage der OpenAPI-Generierung,
die jacoco-Ausschluesse und das Cucumber-glue-Paket. Jetzt war es billig, mit
jeder Woche Entwicklung waere es teurer geworden.
Die BDD-Tests nutzen jetzt RestTestClient statt TestRestTemplate, das in
Spring Boot 4 als Auslaufmodell gilt. Weil Cucumber Senden und Pruefen
trennt, wird die fluent API nicht fuer Zusicherungen genutzt, sondern ueber
returnResult das Ergebnis festgehalten.
Nachgemessen: 22 Tests gruen, check inklusive Coverage-Verifikation besteht,
91,7 % Zeilenabdeckung, generierter Code weiterhin ausgeschlossen.
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