Skip to content

Graph Transactions

mbroecheler edited this page May 23, 2012 · 27 revisions

A Graph that implements the TransactionalGraph or ThreadedTransactionalGraph interfaces must natively support transaction handling. A transaction describes a coherent and complete unit of work comprised of multiple read and write operations to be executed against the database together or not at all. In addition, transactions are needed to handle conflicts and consistency issues that arise when multiple users interact with the Graph concurrently. The exact ACID and isolation level guarantees extended by a TransactionalGraph or ThreadedTransactionalGraph depend on the specific implementation.

TransactionalGraph and Starting and Stopping Transactions

A TransactionalGraph has the following methods.

public void startTransaction();
public void stopTransaction(Conclusion conclusion);

When a transaction is started, all the subsequent read and write operations occur within this transaction context. When the transaction is successfully stopped, those mutations operations are persisted and visible to other contexts interacting with the graph and all locks are released. If a transaction is failed, then the mutation operations are “rolled back” to when the transaction was started.
TransactionalGraph makes no assumptions about how transactions are implemented by a graph database. Hence, a transaction may fail at any point if a conflict arises that could not be resolved.

To facilitate ease of use, a call to startTransaction() is not strictly required. If a database operation is evaluated and a transaction has not been started, then a transaction is automatically started. However, it is up to the user to stopTransaction(Conclusion conclusion) or else they run the risk of a OutOfMemoryException if too many mutations have occurred and possible dead-locks if locks are held for too long. Finally, note that a TransactionalGraph.shutdown() will automatically successfully commit any open transaction.

Note, that element references created in a transactional context may not be accessed outside the transactional context. Doing so may cause an exception. A transaction marks a complete unit of work and after it is stopped, its state may be discarded. Moreover, concurrently running transaction can render such references out-of-sync. Any references created during the transaction may therefore no longer be alive. Hence, the following code snippet may cause an exception:

Vertex v = graph.addVertex(null);
//More operations inside the transaction
stopTransaction(Conclusion.SUCCESS);
//Other code
v.setProperty("name","Marko");

In such cases, the transactional context should be extended until all operations have been completed. In other words, the stopTransaction(Conclusion.SUCCESS) call should be moved after the v.setProperty("name","Marko"); write operation.
In cases where the element reference needs to be accessed outside its original transactional context, it should be re-instantiated based on the element id. For example:

Vertex v = graph.addVertex(null);
//More operations inside the transaction
stopTransaction(Conclusion.SUCCESS);
//Other code
startTransaction();
v = graph.getVertex(v.getId());
v.setProperty("name","Marko");

ThreadedTransactionalGraph and Multi-Threads for One Transaction

ThreadedTransactionalGraph provides more fine grained control over the transactional context.
While TransactionalGraph binds each transaction to the executing thread, ThreadedTransactionalGraph.startThreadTransaction() returns a TransactionalGraph that represents its own transactional context independent of the executing thread.
Hence, one can have multiple threads operating against a single transaction represented by the returned TransactionalGraph object. This is useful for parallelizing graph algorithms.

A ThreadedTransactionalGraph extends TransactionalGraph with a single method.

public TransactionalGraph startThreadTransaction()