TypeDB.Transaction (TypeDB v0.5.1)

Copy Markdown View Source

Explicit transactions.

A transaction is a lightweight handle: an id plus the connection it belongs to. It is a plain struct, not a process, so it can be passed between processes freely — TypeDB tracks the transaction server-side.

Bracketed usage (preferred)

TypeDB.transaction/5 commits on success and rolls back on error or exception, which is what you want almost always:

TypeDB.transaction(conn, "social", :write, fn tx ->
  {:ok, _} = TypeDB.Transaction.query(tx, "insert $p isa person, has name 'Alice';")
  {:ok, _} = TypeDB.Transaction.query(tx, "insert $p isa person, has name 'Bob';")
end)

Manual usage

When the commit point is not lexically scoped:

{:ok, tx} = TypeDB.Transaction.open(conn, "social", :write)
{:ok, _answer} = TypeDB.Transaction.query(tx, "insert $p isa person;")
:ok = TypeDB.Transaction.commit(tx)

Always pair open/4 with commit/1, rollback/1 or close/1. An abandoned transaction holds server-side resources until its transaction_timeout_millis elapses — and a :schema transaction holds the exclusive schema lock for that whole time, blocking every other schema change.

Transaction types

  • :read — read-only, no commit needed. Multiple readers run concurrently.
  • :write — data changes. Concurrent writers are allowed; conflicting commits fail at commit time.
  • :schema — schema changes. Takes an exclusive, database-wide lock.

A query is rejected if it needs more than the transaction grants: a define requires :schema, an insert requires :write or :schema.

Summary

Functions

Asks TypeDB to analyse a query without running it.

Analyses a query, raising on failure.

Closes the transaction, discarding uncommitted writes.

Closes the transaction, raising on failure.

Commits the transaction.

Commits the transaction, raising on failure.

Opens a transaction on database.

Opens a transaction, raising on failure.

Runs a TypeQL query inside the transaction.

Runs a query, raising on failure.

Discards everything written so far, leaving the transaction open.

Discards everything written so far, raising on failure.

Types

t()

@type t() :: %TypeDB.Transaction{
  conn: TypeDB.Connection.t(),
  database: String.t(),
  id: String.t(),
  type: type()
}

type()

@type type() :: :read | :write | :schema

Functions

analyze(tx, query, opts \\ [])

@spec analyze(t(), String.t(), keyword()) :: {:ok, map()} | {:error, TypeDB.Error.t()}

Asks TypeDB to analyse a query without running it.

Returns the decoded pipeline structure: the query's stages, variables, and inferred types. Useful for query tooling and for checking that a query type-checks against the current schema before shipping it.

Not covered by this driver's versioning

The map is TypeDB's, passed through verbatim with string keys, and it is the one return value in this driver that SemVer does not cover. It is a diagnostic surface: the endpoint is not in TypeDB's published HTTP API reference, and its shape tracks the server rather than this package, so a TypeDB upgrade can change it inside a patch release of the driver.

Modelling it as a struct would be a promise the driver cannot keep — every server change would either break the struct or be silently dropped by it. Match defensively, and do not build anything load-bearing on the shape.

analyze!(tx, query, opts \\ [])

@spec analyze!(t(), String.t(), keyword()) :: map()

Analyses a query, raising on failure.

close(tx, opts \\ [])

@spec close(
  t(),
  keyword()
) :: :ok | {:error, TypeDB.Error.t()}

Closes the transaction, discarding uncommitted writes.

Closing is idempotent and never fails on an already-closed transaction — it is safe to call in an after block.

Takes the same :timeout and :deadline options as commit/2.

close!(tx, opts \\ [])

@spec close!(
  t(),
  keyword()
) :: :ok

Closes the transaction, raising on failure.

commit(tx, opts \\ [])

@spec commit(
  t(),
  keyword()
) :: :ok | {:error, TypeDB.Error.t()}

Commits the transaction.

The transaction is finished afterwards, whether or not the commit succeeded — a failed commit does not leave anything to roll back.

Committing a :read transaction is an error; use close/1.

Options

  • :timeout — overrides the connection's :timeout for this call. A commit is usually the most expensive request a transaction makes, since it is where the server does the work, so it is the one most likely to want more time than an ordinary query.
  • :deadline — overrides the connection's :deadline: a wall-clock budget in ms for this call including any retries.

commit!(tx, opts \\ [])

@spec commit!(
  t(),
  keyword()
) :: :ok

Commits the transaction, raising on failure.

open(conn, database, type, opts \\ [])

@spec open(TypeDB.Connection.t(), String.t(), type(), keyword()) ::
  {:ok, t()} | {:error, TypeDB.Error.t()}

Opens a transaction on database.

Options

Transaction options (see TypeDB.Options) plus :timeout and :deadline for the HTTP request itself. Any other key raises ArgumentError.

TypeDB.Transaction.open(conn, "social", :schema,
  schema_lock_acquire_timeout_millis: 30_000
)

open!(conn, database, type, opts \\ [])

@spec open!(TypeDB.Connection.t(), String.t(), type(), keyword()) :: t()

Opens a transaction, raising on failure.

query(tx, query, opts \\ [])

@spec query(t(), String.t(), keyword()) ::
  {:ok, TypeDB.Answer.t()} | {:error, TypeDB.Error.t()}

Runs a TypeQL query inside the transaction.

Nothing is persisted until commit/1.

Options

Query options (see TypeDB.Options) plus:

  • :timeout — overrides the connection timeout for this request. Long analytical reads usually want this. It bounds one attempt; :deadline bounds the call.
  • :deadline — overrides the connection's :deadline: a wall-clock budget in ms for this call including any retries.
  • :given_rows — input rows for the query's given stage. See "Parameterised queries" below.

Examples

TypeDB.Transaction.query(tx, "match $p isa person; select $p;",
  include_instance_types: false,
  answer_count_limit: 1_000
)

Parameterised queries

Never interpolate user input into a query string — TypeQL injection is as real as SQL injection. Use TypeDB's given stage (3.12+) instead: the values travel beside the query rather than inside it, so they can never be parsed as TypeQL.

TypeDB.Transaction.query(tx, """
  given $n: string;
  insert $p isa person, has name == $n;
""", given_rows: [%{"n" => "Alice"}, %{"n" => "Bob"}])

The rest of the pipeline runs once per row, which also makes this the fast way to write many rows: one request and one query compilation instead of N. For 2,000 rows against a local server, bench/given.exs measures 101ms this way, 1541ms for a single request whose query text carries 2,000 insert statements, and 8830ms for 2,000 requests.

Declare every input variable in the given stage, and mark optional columns with ?:

given $name: string, $age: integer?;

A query with a given stage requires rows, and rows require a given stage. Each row is a map of variable name to a plain Elixir term or a concept from an earlier answer; nil leaves an optional column unbound. TypeDB.Given lists the accepted types and explains why the driver encodes them rather than forwarding raw JSON.

TypeDB refuses a request body over 2 MiB, which is a limit on bytes rather than rows and has no server-side flag: measured against 3.12.1, 2047 KiB is accepted and 2048 KiB comes back as 400 HSR2. Batch large loads by payload size — see Recipes, which also explains why a body far over the limit presents as a :transport failure instead.

Raises

A :given_rows value the driver cannot encode raises TypeDB.Error with kind :encode. That happens while the request is being built, so there is no request to return an error for. An option this function does not accept raises ArgumentError, for the same reason and one step earlier. Everything the server rejects comes back as {:error, %TypeDB.Error{}} as usual.

query!(tx, query, opts \\ [])

@spec query!(t(), String.t(), keyword()) :: TypeDB.Answer.t()

Runs a query, raising on failure.

rollback(tx, opts \\ [])

@spec rollback(
  t(),
  keyword()
) :: :ok | {:error, TypeDB.Error.t()}

Discards everything written so far, leaving the transaction open.

The transaction returns to the state it had when opened, so you can retry inside the same transaction rather than reopening one.

Takes the same :timeout and :deadline options as commit/2.

rollback!(tx, opts \\ [])

@spec rollback!(
  t(),
  keyword()
) :: :ok

Discards everything written so far, raising on failure.