---
title: "Transactions in AGS"
description: "Learn how to manage data consistency and isolation in AGS using Aerospike and TinkerPop transactions."
---

# Transactions in AGS

> For the complete documentation index see: [llms.txt](https://aerospike.com/docs/llms.txt)
> 
> All documentation pages available in markdown.

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](https://aerospike.com/glossary/eventual-consistency/). 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](https://aerospike.com/docs/graph/3.1.0/develop/query/basics/) depends on whether the Aerospike database namespace is configured with [strong consistency (SC)](https://aerospike.com/docs/database/manage/namespace/consistency/) mode or [available and partition-tolerant (AP)](https://aerospike.com/docs/database/learn/architecture/clustering/consistency-modes) 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.

| Operation | Gremlin step |
| --- | --- |
| Create edge | `addE()` |
| Create vertex | `addV()` |
| Merge edge | `mergeE()` |
| Merge vertex | `mergeV()` |
| Update vertex property | `v(<>).property("key", "value")` |
| Update edge property | `e(<>).property("key", "value")` |
| Delete vertex | `v(<>).drop()` |
| Delete edge | `e(<>).drop()` |
| Delete vertex property | `v(<>).properties("key").drop()` |
| Delete edge property | `e(<>).properties("key").drop()` |

## Transactions in strong consistency mode

When the database namespace uses [strong consistency (SC)](https://aerospike.com/docs/database/manage/namespace/consistency) mode:

-   With transactions [enabled](#enable-aerospike-and-tinkerpop-transactions), all mutation queries are atomic and isolated, guaranteeing no data loss, even during a cluster split
-   With transactions enabled and [configured](#aerospike-transaction-configuration-options), 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](https://aerospike.com/docs/graph/3.1.0/develop/query/supernodes) vertex, which remains best-effort because of the volume of connected edges.

### Transaction options in SC mode

AGS provides two options to support [atomicity](https://aerospike.com/glossary/acid-compliant/) when SC mode is enabled:

-   Aerospike transactions coordinate atomic execution for each iteration of a mutation step that touches multiple records.
-   [TinkerPop transactions](https://tinkerpop.apache.org/docs/3.4.8/reference/#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](https://aerospike.com/products/database/) 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:

-   [aerospike.graph.mrt.enabled](https://aerospike.com/docs/graph/reference/config#aerospikegraphmrtenabled)
    
    Set this value to `true` to enable transactions.
    
-   [aerospike.graph.mrt.timeout](https://aerospike.com/docs/graph/reference/config#aerospikegraphmrttimeout)
    
    Optional timeout in seconds for transactions. By default, this is `0`, which uses the server [mrt-duration](https://aerospike.com/docs/database/reference/config#namespace__mrt-duration) setting. The default [mrt-duration](https://aerospike.com/docs/database/reference/config#namespace__mrt-duration) is 10 seconds.
    

#### TinkerPop transaction configuration options

Set the following configuration options:

-   [aerospike.graph.tx.enabled](https://aerospike.com/docs/graph/reference/config#aerospikegraphtxenabled)
    
    Set this value to `true` to allow transaction scopes from Gremlin client code.
    
-   [aerospike.graph.tx.timeout](https://aerospike.com/docs/graph/reference/config#aerospikegraphtxtimeout)
    
    Optional timeout in seconds for TinkerPop transactions. By default, this is `0`, which uses the server [mrt-duration](https://aerospike.com/docs/database/reference/config#namespace__mrt-duration) setting. The default [mrt-duration](https://aerospike.com/docs/database/reference/config#namespace__mrt-duration) is 10 seconds.
    

#### Supported mutation queries in SC mode

All Gremlin mutation steps are fully supported. Common [examples](https://aerospike.com/docs/graph/3.1.0/develop/query/basics/) include the following:

| Operation | Gremlin step |
| --- | --- |
| Create vertex | `addV()` |
| Create edge | `addE()` |
| Update vertex property | `V().property("key", "value")` |
| Update edge property | `E().property("key", "value")` |
| Delete vertex | `V(...).drop()` |
| Delete edge | `E(...).drop()` |
| Delete property | `V().properties("key").drop()` |
| Merge vertex | `mergeV()` |
| Merge edge | `mergeE()` |

#### 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](https://aerospike.com/docs/graph/3.1.0/develop/query/transactions/#query-outside-the-tinkerpop-transaction-then-mutate-inside-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](https://aerospike.com/docs/graph/3.1.0/develop/query/supernodes), 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)

```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)

```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)

```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](https://aerospike.com/docs/graph/reference/config#aerospikegraphtxtimeout) 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)

```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)](https://aerospike.com/docs/database/learn/architecture/clustering/consistency-modes) 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:

| Operation | Gremlin step |
| --- | --- |
| Create vertex | `addV()` |
| Update vertex property | `v(<>).property("key", "value")` |
| Update edge property | `e(<>).property("key", "value")` |
| Delete vertex property | `v(<>).properties("key").drop()` |
| Delete edge property | `e(<>).properties("key").drop()` |
| Merge vertex | `mergeV()` |

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

## Related documentation

-   [Gremlin query basics](https://aerospike.com/docs/graph/3.1.0/develop/query/basics/)
-   [Strong consistency](https://aerospike.com/docs/database/manage/namespace/consistency)
-   [Consistency modes](https://aerospike.com/docs/database/learn/architecture/clustering/consistency-modes)
-   [Apache TinkerPop: Transactions](https://tinkerpop.apache.org/docs/3.4.8/reference/#transactions)