A handle to an ArangoDB stream transaction, carried as a value.
Arangox.begin_transaction/2 returns one. The handle form's functions
(Arangox.commit_transaction/3, Arangox.abort_transaction/3,
Arangox.transaction_status/3) and the :transaction per-request option
accept only this struct, never a bare identifier binary.
Identity travels in the request, never in the connection
Where Arangox.transaction/3 binds a transaction to one checked-out
connection for the span of a closure, a handle carries the transaction's
identity as a value. A request made with transaction: handle sends the
identifier in its own x-arango-trx-id header, for that request only —
connection state is never written, so a checked-in connection cannot carry
one caller's transaction to the next.
Any pooled connection to the same deployment can therefore serve any request of the transaction. Verified against an ArangoDB 3.12 cluster (3.12.4, three coordinators): a transaction begun through one coordinator accepted writes, served isolated reads, reported status, and committed or aborted through the other coordinators — the identifier encodes the issuing coordinator and foreign coordinators forward to it. That is the measured scope: any connection, any coordinator, one deployment. A handle means nothing to a different deployment, and this states measured behaviour rather than an unconditional promise about future server versions.
The identifier is a bearer capability
Any code that can reach the deployment with the pool's credentials and holds the identifier can read and write inside the transaction, commit it, or abort it. Server-assigned identifiers are sequential, so an identifier is also a hint about its neighbours. Treat the handle like authentication material: do not let it cross a trust boundary, and keep it out of URLs, logs and telemetry.
The driver treats it that way itself: the identifier is redacted from
inspect/1 of this struct, from the headers of the request struct that
Arangox.request/6 echoes back, and from every error message the driver
constructs. (The server's error messages may name the identifier — they
are returned verbatim in Arangox.Error's :message, as documented there.)
Identifier shape
A stream-transaction identifier on the wire is a nonempty string of decimal
digits ("3298558923352"). The driver validates that shape before an
identifier reaches a header or a request path, so a malformed value —
anything empty, non-digit, or carrying control characters — is rejected
locally and never goes near the wire. Wrap an identifier obtained outside
this driver with new/1, which applies the same validation.
A transaction lives in the database it was begun in: pass the same
:database option to the requests, commit, abort and status calls that you
passed to Arangox.begin_transaction/2.
Summary
Types
The server-assigned transaction identifier: a nonempty string of decimal digits.
A stream-transaction handle. Treat it as authentication material.
Functions
Wraps a transaction identifier obtained outside this driver in a handle.
Whether id has the shape of a stream-transaction identifier: a nonempty
binary of decimal digits.
Types
Functions
Wraps a transaction identifier obtained outside this driver in a handle.
Arangox.begin_transaction/2 is the normal way to obtain a handle; this
exists for identifiers that arrive from elsewhere (another process, another
driver, an HTTP header). The identifier must be a nonempty string of decimal
digits — the only shape the server issues — or an ArgumentError is raised.
The error never echoes the value, which may be a live transaction identifier.
iex> Arangox.Transaction.new("3298558923352")
#Arangox.Transaction<id: "[redacted]">
Whether id has the shape of a stream-transaction identifier: a nonempty
binary of decimal digits.
iex> Arangox.Transaction.valid_id?("3298558923352")
true
iex> Arangox.Transaction.valid_id?("3298\n5589")
false