fix(protokoll): IDs ohne Zweierschritte vergeben (#032)

Spalten-Default (BIGSERIAL) und Trigger protokoll_verketten zogen je
eine Nummer aus der Sequenz, jede Zeile verbrauchte zwei IDs. Die
Nummer aus dem Default darf der Trigger aber nicht einfach
uebernehmen, denn sie wird vor der Advisory-Sperre gezogen: Eine parallele
Transaktion mit groesserer ID koennte zuerst verketten, und die Kette
liesse sich in ID-Reihenfolge nicht mehr pruefen.

Deshalb zieht nur noch der Trigger unter der Sperre; der Default ist
ein Platzhalter 0 (Prisma: @default(0)), damit create ohne ID
funktioniert. Die Sequenz bleibt der Spalte zugeordnet. Bestehende
Zeilen werden nicht umnummeriert, weil die ID in die Hash-Eingabe
eingeht.

Tests: drei Eintraege nacheinander ergeben n, n+1, n+2; eine
mitgegebene ID wird ueberschrieben; 20 parallele Eintraege belegen
genau 20 aufeinanderfolgende IDs bei intakter Kette.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-12 17:19:28 +02:00
co-authored by Claude Opus 5.5
parent 75077f9101
commit b4804390c4
4 changed files with 42 additions and 3 deletions
+3 -1
View File
@@ -483,7 +483,9 @@ model Vorgang {
/// Revisionssicheres Protokoll (#005). Append-only per Trigger; `hash` verkettet jede Zeile
/// mit ihrer Vorgaengerin und wird in der Datenbank berechnet.
model Protokoll {
id BigInt @id @default(autoincrement())
/// Vergibt der Trigger `protokoll_verketten` unter der Advisory-Sperre aus der Sequenz; der
/// Default 0 ist nur ein Platzhalter, damit `create` ohne ID auskommt (#032).
id BigInt @id @default(0)
zeitpunkt DateTime @default(now())
akteurId String?
/// Name zum Zeitpunkt der Aktion – bleibt lesbar, auch wenn das Konto spaeter umbenannt wird.