CodamAIDocs
Themafertig

Gleichzeitige Änderungen

Es gibt kein save und bei normalen Modellen kein optimistisches Locking: die letzte Änderung gewinnt. Nur Datei-Modelle erkennen einen Konflikt.

Ausprägungen
normales Modell: last write winsPATCH statt PUT verkleinert KonflikteDatei-Modell: 409 bei überlappenden AnfragenClient entscheidet create/update anhand der id

Worum es geht

Zwei Personen öffnen dasselbe Objekt, beide ändern etwas, beide speichern. Was passiert?

Last write wins

sequenceDiagram
    participant A as Anna (Client)
    participant C as CDMS
    participant B as Ben (Client)
    A->>C: liest Kunde (email = alt@…, phone = 111)
    B->>C: liest Kunde (email = alt@…, phone = 111)
    A->>C: PUT { email: anna@…, phone: 111 }
    C-->>A: 200
    B->>C: PUT { email: alt@…, phone: 222 }
    C-->>B: 200
    Note over C: email = alt@… (Annas Änderung ist weg)<br/>phone = 222

Ben hat Annas neue E-Mail nie gesehen. Sein PUT beschreibt den ganzen Zielzustand, also auch die alte E-Mail. CDMS prüft nicht, ob sich das Objekt seit Bens Lesen geändert hat, und speichert Bens Stand.

PATCH verkleinert den Konflikt

Mit PATCH schickt jeder nur, was er geändert hat. Dann überschreiben sich zwei Personen nur, wenn sie dasselbe Feld ändern.

Anna ändert die E-Mail, Ben das Telefon
beide mit PUTbeide mit PATCH
Anna schicktemail und phonenur email
Ben schicktemail und phonenur phone
ErgebnisAnnas E-Mail ist verlorenbeide Änderungen bleiben

Ändern beide dasselbe Feld, gewinnt auch bei PATCH die letzte Anfrage. Siehe PUT oder PATCH?

Datei-Modelle: 409 bei überlappenden Anfragen

Ein Datei-Modell hat ein internes Systemfeld _version, einen Zähler. Der Client sieht es nicht und schickt es nicht mit. CDMS nutzt es, um zwei Anfragen zu erkennen, die zur selben Zeit dasselbe Objekt schreiben:

Zwei gleichzeitige Uploads in dasselbe Datei-Objekt
  1. 1
    Client→CDMS
    Anfrage 1: PUT /update/{id}/upload mit einer neuen Datei
  2. 2
    Client→CDMS
    Anfrage 2, kurz danach: ebenfalls ein Upload in dasselbe Objekt
  3. 3
    CDMS→Database
    Anfrage 1 beansprucht das Objekt und erhöht _version, bevor sie die Datei schreibt
  4. 4
    CDMS→Database
    Anfrage 2 will dasselbe, findet aber schon eine höhere _version vor
  5. 5
    CDMS→Client
    Anfrage 2 → 409 CDMS_OPTIMISTIC_LOCK_CONFLICT, ihre Datei wird nicht übernommen
  6. 6
    CDMS→Client
    Anfrage 1 → 200
Die Antwort auf die zweite Anfrage
Antwort 409
{
  "error": "CodamaiPersistenceException",
  "messageKey": "CDMS_OPTIMISTIC_LOCK_CONFLICT",
  "code": "409",
  "layer": "database"
}
Was der Client tun sollte
Objekt neu lesen,
prüfen, ob der Upload noch nötig ist,
dann erneut schicken.

Das gilt auch für überlappende Änderungen der übrigen Felder eines Datei-Objekts. Zwei Anfragen nacheinander erkennt es nicht: Liegt zwischen Lesen und Speichern eine Pause, gewinnt auch hier die letzte.

Normales Modell und Datei-Modell im Vergleich

Schutz vor gleichzeitigen Änderungen
Normales ModellDatei-Modell
Versionsfeldkeins_version, intern
Client schickt etwas mitneinnein
zwei überlappende Anfragendie spätere gewinntdie zweite bekommt 409
gelesen vor 10 Minuten, jetzt gespeichertdie spätere gewinntdie spätere gewinnt

Anlegen oder ändern entscheidet der Client

Einen Endpunkt, der selbst entscheidet, ob er anlegt oder ändert (save, „upsert“), gibt es nicht. Der Client entscheidet anhand der id:

Speichern im Client
Objekt hat eine idAnfrage
neinPOST /create, danach die id aus der Antwort merken
jaPUT oder PATCH /update/{id}

Klickt jemand zweimal auf „Speichern“, bevor die erste Antwort da ist, entstehen zwei Objekte. Sperre den Knopf, bis die Antwort da ist. Bei Singletons verhindert CDMS ein zweites Objekt selbst: Das zweite create liefert 400 object-already-exists|use-update.

Fallen

Wie es weitergeht

Quellen im Code und in der Wissensdatenbank
  • CDMS/cdms-generator – EntityProcessor (@Version _version nur bei Datei-Modellen), ApiProcessor (Endpunktliste, kein save)
  • CDMS/cdms-persistence-database – AbstractDatabasePersistence.lockForUpdate (PESSIMISTIC_FORCE_INCREMENT), flush; OptimisticLockMultiTest
  • commons-persistence – OptimisticLockTranslator, PersistenceErrorCode (CDMS_OPTIMISTIC_LOCK_CONFLICT)
  • CDMS/cdms-system-layer – AbstractLayer.recursivePrepare (lockForUpdate vor saveFile)
  • documentation/05-api-guide/06-schreiben.md
Suchen