Arangox.Transaction (Arangox v0.8.0)

Copy Markdown View Source

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.

t()

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

id()

@type id() :: binary()

The server-assigned transaction identifier: a nonempty string of decimal digits.

t()

@type t() :: %Arangox.Transaction{id: id()}

A stream-transaction handle. Treat it as authentication material.

Functions

new(id)

@spec new(id()) :: t()

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]">

valid_id?(id)

@spec valid_id?(term()) :: boolean()

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