Gremlin transactions in Neptune - Amazon Neptune

Gremlin transactions in Neptune

There are several contexts within which Gremlin transactions are executed. When working with Gremlin it is important to understand the context you are working within and what its implications are:

  • Script-based   –   Requests are made using text-based Gremlin strings, like this:

    • Using the Java driver and Client.submit(string).

    • Using the Gremlin console and :remote connect.

    • Using the HTTP API.

  • Bytecode-based   –   Requests are made using serialized Gremlin bytecode typical of Gremlin Language Variants(GLV).

    For example, using the Java driver, g = traversal().withRemote(...).

For either of the above contexts, there is the additional context of the request being sent as sessionless or as bound to a session.

Note

Gremlin transactions must always either be committed or rolled back, so that server-side resources can be released. In the event of an error during the transaction, it is important to retry the entire transaction and not just the particular request that failed.

Sessionless requests

When sessionless, a request is equivalent to a single transaction.

For scripts, the implication is that one or more Gremlin statements sent in a single request will commit or rollback as a single transaction. For example:

Cluster cluster = Cluster.open(); Client client = cluster.connect(); // sessionless // 3 vertex additions in one request/transaction: client.submit("g.addV();g.addV();g.addV()").all().get();

For bytecode, a sessionless request is made for each traversal spawned and executed from g:

GraphTraversalSource g = traversal().withRemote(...); // 3 vertex additions in three individual requests/transactions: g.addV().iterate(); g.addV().iterate(); g.addV().iterate(); // 3 vertex additions in one single request/transaction: g.addV().addV().addV().iterate();

Requests bound to a session

When bound to a session, multiple requests can be applied within the context of a single transaction.

For scripts, the implication is that there is no need to concatenate together all of the graph operations into a single embedded string value:

Cluster cluster = Cluster.open(); Client client = cluster.connect(sessionName); // session try { // 3 vertex additions in one request/transaction: client.submit("g.addV();g.addV();g.addV()").all().get(); } finally { client.close(); } try { // 3 vertex additions in three requests, but one transaction: client.submit("g.addV()").all().get(); // starts a new transaction with the same sessionName client.submit("g.addV()").all().get(); client.submit("g.addV()").all().get(); } finally { client.close(); }

For script-based sessions, closing the client with client.close() commits the transaction. There is no explicit rollback command available in script-based sessions. To force a rollback, you can cause the transaction to fail by issuing a query such as g.inject(0).fail('rollback') before closing the client.

Note

A query like g.inject(0).fail('rollback'), used to intentionally throw an error to force a rollback, produces an exception on the client. Catch and discard the resulting exception before closing the client.

For bytecode, the transaction can be explicitly controlled and the session managed transparently. Gremlin Language Variants (GLV) support Gremlin's tx() syntax to commit() or rollback() a transaction as follows:

GraphTraversalSource g = traversal().withRemote(conn); Transaction tx = g.tx(); // Spawn a GraphTraversalSource from the Transaction. // Traversals spawned from gtx are executed within a single transaction. GraphTraversalSource gtx = tx.begin(); try { gtx.addV('person').iterate(); gtx.addV('software').iterate(); tx.commit(); } finally { if (tx.isOpen()) { tx.rollback(); } }

Although the example above is written in Java, you can also use this tx() syntax in other languages. For language-specific transaction syntax, see the Transactions section of the Apache TinkerPop documentation for Java, Python, Javascript, .NET, and Go.

Warning

Sessionless read-only queries are executed under SNAPSHOT isolation, but read-only queries run within an explicit transaction are executed under SERIALIZABLE isolation. The read-only queries executed under SERIALIZABLE isolation incur higher overhead and can block or get blocked by concurrent writes, unlike those run under SNAPSHOT isolation.

Timeout behavior for bytecode commit and rollback

When you use bytecode-based transactions with the tx() syntax, the commit() and rollback() operations are not subject to query timeout settings. Neither the global neptune_query_timeout parameter nor per-query timeout values set through evaluationTimeout apply to these operations. On the server, commit() and rollback() run without a time limit until they complete or encounter an error.

On the client side, the Gremlin driver's tx.commit() and tx.rollback() calls will not complete until the server responds. Depending on the language, this might manifest as a blocking call or an unresolved async operation. No driver provides a built-in timeout setting that bounds these calls. Consult the API documentation for your specific Gremlin Language Variant for details on concurrency behavior around these transaction features.

Important

If a commit() or rollback() call takes longer than expected, it might be blocked by lock contention from a concurrent transaction. For more information about lock conflicts, see Conflict Resolution Using Lock-Wait Timeouts.

If you need to bound the time your application waits for a commit() or rollback(), you can use your language's concurrency features to apply a client-side timeout. If the client-side timeout fires, the server continues processing the operation. The server-side operation holds a worker thread until it completes. After a client-side timeout, close the connection and create a new one rather than reusing the existing connection, because the transaction state is indeterminate.

Server-side transaction cleanup

If a client disconnects or abandons a transaction without committing or rolling back, Neptune has server-side mechanisms that eventually clean up the orphaned transaction:

  • Session timeout   –   Bytecode-based sessions that remain idle for longer than the maximum session lifetime (10 minutes) are closed, and any open transaction is rolled back.

  • Connection idle timeout   –   Neptune closes WebSocket connections that are idle for approximately 20 minutes. When the connection closes, the server rolls back any open transaction associated with that connection.

These cleanup mechanisms are safety nets. We recommend that you always explicitly commit or roll back transactions when you are finished with them.