Skip to content

Transactions in AGS

This page describes how to manage data consistency and isolation with Aerospike Graph Service (AGS).

Queries

AGS supports different transaction isolation levels for read-only queries and Gremlin mutation queries.

Read-only queries

Read-only queries follow an eventual consistency model. If updates occur concurrently on the same vertex or edge, the query results may temporarily reflect stale or inconsistent data while mutations are in progress.

Gremlin mutation queries

Mutation queries create, update, or delete vertices and edges.

The consistency of mutation queries depends on whether the Aerospike database namespace is configured with strong consistency (SC) mode or available and partition-tolerant (AP) mode.

Mutation queries modify the graph using the Gremlin steps listed below. If a query contains any of the following mutation steps, it is classified and executed as a mutation query. Otherwise, it is treated as a read-only query.

OperationGremlin step
Create edgeaddE()
Create vertexaddV()
Merge edgemergeE()
Merge vertexmergeV()
Update vertex propertyv(<>).property("key", "value")
Update edge propertye(<>).property("key", "value")
Delete vertexv(<>).drop()
Delete edgee(<>).drop()
Delete vertex propertyv(<>).properties("key").drop()
Delete edge propertye(<>).properties("key").drop()

Transactions in strong consistency mode

When the database namespace uses strong consistency (SC) mode:

  • With transactions enabled, all mutation queries are atomic and isolated, guaranteeing no data loss, even during a cluster split
  • With transactions enabled and configured, every mutation traversal runs atomically and in isolation.

AGS protects mutations even during network partitions. The only exception is the drop() step for a supernode vertex, which remains best-effort because of the volume of connected edges.

Transaction options in SC mode

AGS provides two options to support atomicity when SC mode is enabled:

  • Aerospike transactions coordinate atomic execution for each iteration of a mutation step that touches multiple records.
  • TinkerPop transactions let clients define explicit transaction scopes. Start a transaction in client code, run multiple traversals, then commit() or rollback() the scope. TinkerPop transactions are available only in SC mode.

You can enable one or both options after your graph is running on an SC namespace.

Enable Aerospike and TinkerPop transactions

The following requirements and configuration options ensure write consistency across supported mutation operations in SC mode.

Prerequisites

  • Use Aerospike Enterprise Edition 8.0.0 and later on your database cluster.
  • Configure the namespace of your Aerospike database to use strong consistency. SC is required to enable both Aerospike and TinkerPop transactions.
  • Enable SC for the target graph in AGS.

After SC is enabled, you can optionally turn on one or both features in AGS. Enable them independently using the following configuration options.

Aerospike transaction configuration options

Set the following configuration options:

TinkerPop transaction configuration options

Set the following configuration options:

Supported mutation queries in SC mode

All Gremlin mutation steps are fully supported. Common examples include the following:

OperationGremlin step
Create vertexaddV()
Create edgeaddE()
Update vertex propertyV().property("key", "value")
Update edge propertyE().property("key", "value")
Delete vertexV(...).drop()
Delete edgeE(...).drop()
Delete propertyV().properties("key").drop()
Merge vertexmergeV()
Merge edgemergeE()

How to use TinkerPop transactions

TinkerPop transactions require SC mode. They are not available on AP namespaces.

In a TinkerPop transaction, traversal steps must follow these rules:

  • Scans and indexes are not available inside a transaction, so you cannot query for vertices or edges in the transaction scope. Address elements by ID (except when adding new elements). Resolve IDs outside the transaction with g, then use those IDs inside the transaction with gtx. Keep transactions short and focused to minimize potential contention.
  • A single TinkerPop transaction can modify a maximum of 4096 records. Adding or removing an edge touches 3 records (the two endpoint vertices and the edge). With that in mind, removing a vertex removes all of its connected edges. If a vertex is a supernode, it also cannot be removed within a transaction.
  • Once a traversal in the TinkerPop transaction modifies a record, that record is locked for the duration of the transaction and other transactions or traversals cannot update it. Use TinkerPop transactions sparingly in areas of the graph with many concurrent writes.

Add multiple elements in one TinkerPop transaction

The following TinkerPop transaction example creates two vertices and two edges, each with a property, and updates a property on a pre-existing vertex. If any step within the transaction scope fails, none of the transaction changes are applied. If all steps succeed, all transaction changes are applied atomically to the graph.

Add multiple elements inside a transaction (Java)
// Existing GraphTraversalSource
GraphTraversalSource g;
// Begin a transaction scope
GraphTraversalSource gtx = g.tx().begin();
// Get the ID of an existing vertex outside the transaction
Object vertexId1 = g.V().has("foo", "bar").id().next();
try {
// Create two vertices and an edge between them, all inside the transaction
Object vertexId2 = gtx.addV("allOrNothing").property("allOrNothing", true).id().next();
Object vertexId3 = gtx.addV("allOrNothing").property("allOrNothing", true).id().next();
// Create edges and update a property on an existing vertex
gtx.addE("allOrNothing").property("allOrNothing", true)
.from(__.V(vertexId1)).to(__.V(vertexId2)).next();
gtx.addE("allOrNothing").property("allOrNothing", true)
.from(__.V(vertexId2)).to(__.V(vertexId3)).next();
gtx.V(vertexId1).property("allOrNothing", true).next();
// Atomically apply all changes
gtx.tx().commit();
} catch (Exception e) {
// If any transaction step fails, none of the transaction changes are applied
gtx.tx().rollback();
}

Query outside the TinkerPop transaction, then mutate inside the transaction

You cannot run a read query inside a TinkerPop transaction, but you can read IDs outside the transaction with g and then reference those IDs with gtx inside the TinkerPop transaction. The following example shows how.

Query outside transaction, mutate inside transaction (Java)
// Existing GraphTraversalSource
GraphTraversalSource g;
// Begin a transaction scope
GraphTraversalSource gtx = g.tx().begin();
try {
// Create a controller vertex inside the transaction
Object controllerId = gtx.addV("controller").id().next();
// Read candidate device IDs outside the transaction
List<Vertex> devices = g.V().hasLabel("device").has("state", "idle").toList();
// Mutate those devices inside the transaction
for (Vertex device : devices) {
gtx.V(device.id()).property("state", "connected").next();
gtx.addE("connectedTo").from(__.V(device.id())).to(__.V(controllerId)).next();
}
gtx.tx().commit();
} catch (Exception e) {
gtx.tx().rollback();
}

When concurrent writes are possible, use retries (see below) or verify the current value before updating state to connected.

Per transaction timeout for TinkerPop transactions

Set a timeout for a single TinkerPop transaction using the parameter aerospike.graph.tx.timeout. The value is in seconds.

Set per transaction timeout (Java)
final GraphTraversalSource gtx = g.tx().begin().with("aerospike.graph.tx.timeout", 30);

To make this the default for all TinkerPop transactions, set aerospike.graph.tx.timeout in the configuration.

Retry patterns for concurrent write situations

Aerospike and TinkerPop transactions prevent concurrent writes to the same graph elements. Use retry patterns in client code to handle concurrent write situations. The example below demonstrates a retry pattern for a transaction.

Retry pattern for transactions (Java)
for (int i = 0; i < RETRY_COUNT; i++) {
try {
// Write operation that may throw an exception from a concurrent update
g.V(1).drop().iterate();
break;
} catch (final Exception e) {
// Back off exponentially
Thread.sleep(Math.min(Math.pow(10, i), 5000));
}
}

Available and partition-tolerant mode

In available and partition-tolerant (AP) mode, Aerospike and TinkerPop transactions are not available. Some writes may be lost during a cluster split. However, mutations which use the following Gremlin steps are atomic and isolated:

OperationGremlin step
Create vertexaddV()
Update vertex propertyv(<>).property("key", "value")
Update edge propertye(<>).property("key", "value")
Delete vertex propertyv(<>).properties("key").drop()
Delete edge propertye(<>).properties("key").drop()
Merge vertexmergeV()

For all other mutation traversals in AP mode, plan for eventual consistency and retries if conflicts arise.

Feedback

Was this page helpful?

What type of feedback are you giving?

What would you like us to know?

+Capture screenshot

Can we reach out to you?