CodamAIDocs
Topicdone

One request, one transaction

Everything in a request succeeds or nothing does. This page explains when a commit and when a rollback happens, and that there is no bracket spanning two requests.

Variants
success → commit before the responseerror anywhere → rollbackconflict at commit → 409two requests → two transactionshooks inside the transaction, side effects not

What this is about

A transaction is a bracket around changes in the database: either all changes inside it become permanent, or none do. Making them permanent is called a commit, discarding them is called a rollback.

In CDMS this bracket is exactly one request. Everything a request writes belongs together: the object itself, all nested children, all objects deleted along, and everything hooks write through CDMS.

The timeline of a request

sequenceDiagram
    participant C as Client
    participant D as CDMS
    participant H as Hook
    participant DB as Database
    C->>D: PUT /company/update/c4…
    D->>DB: first database action opens the transaction
    D->>H: before hooks
    D->>DB: change, write children
    D->>H: after hooks
    D->>DB: flush: SQL is executed, not yet committed
    D->>DB: read back for the response
    D->>DB: COMMIT
    D-->>C: 200 with the object

Two points in time need to be kept apart:

  • flush: CDMS sends the collected SQL statements to the database. The database checks its rules at this point, for example unique values. Other requests do not see the change yet.
  • commit: the database makes the change permanent and visible to everyone. This happens right before CDMS writes the response.

So when a success response reaches the client, the change is already saved. A request right after it sees it.

Success, error, conflict

How a request ends

When: All steps succeed.

  1. 1
    CDMS
    processes the request: check, write, hooks, read back
  2. 2
    CDMS→Database
    commit, immediately before the response body is written
  3. 3
    CDMS→Client
    2xx

Result: The change is permanent.

When: Any step fails: missing role, validation, hook, database rule.

  1. 1
    CDMS
    a step throws an error
  2. 2
    CDMS→Database
    marks the whole request as failed and rolls back all open transactions
  3. 3
    CDMS→Client
    error response, e.g. 400, 403, 404, 422

Result: Nothing of this request is saved, not even the parts that were already written before the error.

When: All steps succeed, but another request changed the same file model at the same time. Only file models detect concurrent changes.

  1. 1
    CDMS→Database
    commit fails because of the concurrent change
  2. 2
    CDMS→Client
    409, the request is rolled back

Result: Because the commit comes before the response, the client learns about it. See Concurrent changes.

Decision table

What remains of a request
All steps successful?Commit successful?Result
yesyes2xx, everything saved
yesno409 for a concurrent change of a file model, otherwise a server error; nothing saved
no–error response; nothing saved

Two cases have their own pages:

What lies inside the bracket

Whatin the same transaction?
the object of the requestyes
children created, changed or deleted through nestingyes
unlinked objects (reference cleared)yes
writes of a hook through CDMS to models of the same levelyes
the read-back for the responseyes, in STRICT mode
file contents in the file storageno, but they only take effect after the commit and are discarded on an error
emails, HTTP calls, messages sent by a hookno

Two requests, two transactions

  1. 1
    Client→CDMS
    POST /customer/create → 200, the customer is saved
  2. 2
    Client→CDMS
    POST /order/create with the new customer → 422
  3. 3
    CDMS
    rolls back only the second request
    Result: The customer stays, even though their order is missing.

There is no bracket across several requests. If two objects may only come into existence together, write them in one request, nested through the relation. See The four cases in nested writing.

Pitfalls

What comes next

Sources in the code and the knowledge base
  • commons-persistence – DatabaseRequestContext (getEntityManager, markRollbackOnly, commitThreadTransactions, closeEntityManager)
  • CDMS/cdms-rest-api – RequestTransactionCommitter (beforeBodyWrite, postHandle, settle), RequestCommitConfig (extendHandlerExceptionResolvers), RequestFailureMarker (resolveException), CdmsExceptionMapper (markRollbackOnly)
  • CDMS/cdms-system-layer – AbstractSystemLayer (createObject, updateObject, patchObject, deleteObject: flush, rollback)
  • CDMS/cdms-persistence-database – docs/adr/ADR-009-request-scoped-transaction-bracket.md
  • documentation/30-daten-und-persistenz/02-transaktionen.md
Search