TypeDB.Transaction (TypeDB v0.2.0)

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.

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.

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.

Raises

A :given_rows value the driver cannot encode raises TypeDB.Error with kind :config. That happens while the request is being built, so there is no request to return an error for. 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.