What this is about
Two people open the same object, both change something, both save. What happens?
Last write wins
sequenceDiagram
participant A as Anna (Client)
participant C as CDMS
participant B as Ben (Client)
A->>C: reads customer (email = alt@…, phone = 111)
B->>C: reads customer (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@… (Anna's change is gone)<br/>phone = 222
Ben never saw Anna’s new email. His PUT describes the whole target state, so it also contains the old email. CDMS does not check whether the object changed since Ben read it, and it saves Ben’s version.
PATCH makes the conflict smaller
With PATCH, each person only sends what they changed. Then two people only overwrite each other if they change the same field.
| both with PUT | both with PATCH | |
|---|---|---|
| Anna sends | email and phone | only email |
| Ben sends | email and phone | only phone |
| Result | Anna's email is lost | both changes stay |
If both change the same field, the last request wins with PATCH too. See PUT or PATCH? The null trap
File models: 409 on overlapping requests
A file model has an internal system field _version, a counter. The client does not see it and does not send it. CDMS uses it to detect two requests that write the same object at the same time:
-
1Client→CDMSRequest 1:
PUT /update/{id}/uploadwith a new file -
2Client→CDMSRequest 2, shortly after: also an upload into the same object
-
3CDMS→DatabaseRequest 1 claims the object and increases
_versionbefore it writes the file -
4CDMS→DatabaseRequest 2 wants the same, but already finds a higher
_version -
5CDMS→ClientRequest 2 → 409
CDMS_OPTIMISTIC_LOCK_CONFLICT, its file is not accepted -
6CDMS→ClientRequest 1 → 200
{
"error": "CodamaiPersistenceException",
"messageKey": "CDMS_OPTIMISTIC_LOCK_CONFLICT",
"code": "409",
"layer": "database"
}Read the object again,
check whether the upload is still needed,
then send it again.This also applies to overlapping changes of the other fields of a file object. It does not detect two requests one after the other: if there is a pause between reading and saving, the last one wins here too.
Normal model and file model compared
| Normal model | File model | |
|---|---|---|
| Version field | none | _version, internal |
| Client sends something along | no | no |
| two overlapping requests | the later one wins | the second one gets 409 |
| read 10 minutes ago, saved now | the later one wins | the later one wins |
The client decides: create or update
There is no endpoint that decides by itself whether to create or update (save, “upsert”). The client decides based on the id:
| Object has an id | Request |
|---|---|
| no | POST /create, then remember the id from the response |
| yes | PUT or PATCH /update/{id} |
If someone clicks “Save” twice before the first response arrives, two objects are created. Disable the button until the response arrives. For singletons, CDMS prevents a second object itself: the second create returns 400 object-already-exists|use-update.
Pitfalls
Where to go next
- Which verb for what: PUT or PATCH? The null trap
- Uploading files: Uploading
- What belongs together in one transaction: One request, one transaction