Transactions in Neptune openCypher - Amazon Neptune
Services or capabilities described in Amazon Web Services documentation might vary by Region. To see the differences applicable to the China Regions, see Getting Started with Amazon Web Services in China.

Transactions in Neptune openCypher

The openCypher implementation in Amazon Neptune uses the transaction semantics defined by Neptune However, isolation levels provided by the Bolt driver have some specific implications for Bolt transaction semantics, as described in the sections below.

Read-only Bolt transaction queries

There are various ways that read-only queries can be processed, with different transaction models and isolation levels, as follows:

Implicit read-only transaction queries

Here is an example of a read-only implicit transaction:

public void executeReadImplicitTransaction() { // end point final String END_POINT = "(End Point URL)"; // read query final String READ_QUERY = "MATCH (n) RETURN n limit 10"; // create the driver final Driver driver = GraphDatabase.driver(END_POINT, AuthTokens.none(), Config.builder().withEncryption() .withTrustStrategy(TrustStrategy.trustSystemCertificates()) .build()); // create the session config SessionConfig sessionConfig = SessionConfig.builder() .withFetchSize(1000) .withDefaultAccessMode(AccessMode.READ) .build(); // run the query as access mode read driver.session(sessionConfig).readTransaction(new TransactionWork<String>() { final StringBuilder resultCollector = new StringBuilder(); @Override public String execute(final Transaction tx) { // execute the query Result queryResult = tx.run(READ_QUERY); // Read the result for (Record record : queryResult.list()) { for (String key : record.keys()) { resultCollector.append(key) .append(":") .append(record.get(key).asNode().toString()); } } return resultCollector.toString(); } } ); // close the driver. driver.close(); }

Because read-replicas only accept read-only queries, all queries against read-replicas execute as read-implicit transactions regardless of the access mode set in the session configuration. Neptune evaluates read-implicit transactions as read-only queries under SNAPSHOT isolation semantics.

In case of failure, read-implicit transactions are retried by default.

Autocommit read-only transaction queries

Here is an example of a read-only autocommit transaction:

public void executeAutoCommitTransaction() { // end point final String END_POINT = "(End Point URL)"; // read query final String READ_QUERY = "MATCH (n) RETURN n limit 10"; // Create the session config. final SessionConfig sessionConfig = SessionConfig .builder() .withFetchSize(1000) .withDefaultAccessMode(AccessMode.READ) .build(); // create the driver final Driver driver = GraphDatabase.driver(END_POINT, AuthTokens.none(), Config.builder() .withEncryption() .withTrustStrategy(TrustStrategy.trustSystemCertificates()) .build()); // result collector final StringBuilder resultCollector = new StringBuilder(); // create a session final Session session = driver.session(sessionConfig); // run the query final Result queryResult = session.run(READ_QUERY); for (final Record record : queryResult.list()) { for (String key : record.keys()) { resultCollector.append(key) .append(":") .append(record.get(key).asNode().toString()); } } // close the session session.close(); // close the driver driver.close(); }

If the access mode is set to READ in the session configuration, Neptune evaluates autocommit transaction queries as read-only queries under SNAPSHOT isolation semantics. Note that read-replicas only accept read-only queries.

If you don't pass in a session configuration, autocommit queries are processed by default with mutation query isolation, so it is important to pass in a session configuration that explicitly sets the access mode to READ.

In case of failure, read-only autocommit queries are not re-tried.

Explicit read-only transaction queries

Here is an example of an explicit read-only transaction:

public void executeReadExplicitTransaction() { // end point final String END_POINT = "(End Point URL)"; // read query final String READ_QUERY = "MATCH (n) RETURN n limit 10"; // Create the session config. final SessionConfig sessionConfig = SessionConfig .builder() .withFetchSize(1000) .withDefaultAccessMode(AccessMode.READ) .build(); // create the driver final Driver driver = GraphDatabase.driver(END_POINT, AuthTokens.none(), Config.builder() .withEncryption() .withTrustStrategy(TrustStrategy.trustSystemCertificates()) .build()); // result collector final StringBuilder resultCollector = new StringBuilder(); // create a session final Session session = driver.session(sessionConfig); // begin transaction final Transaction tx = session.beginTransaction(); // run the query on transaction final List<Record> list = tx.run(READ_QUERY).list(); // read the result for (final Record record : list) { for (String key : record.keys()) { resultCollector .append(key) .append(":") .append(record.get(key).asNode().toString()); } } // commit the transaction and for rollback we can use beginTransaction.rollback(); tx.commit(); // close the driver driver.close(); }

If the access mode is set to READ in the session configuration, Neptune evaluates explicit read-only transactions as read-only queries under SNAPSHOT isolation semantics. Note that read-replicas only accept read-only queries.

If you don't pass in a session configuration, explicit read-only transactions are processed by default with mutation query isolation, so it is important to pass in a session configuration that explicitly sets the access mode to READ.

In case of failure, read-only explicit queries are retried by default.

Mutation Bolt transaction queries

As with read-only queries, there are various ways that mutation queries can be processed, with different transaction models and isolation levels, as follows:

Implicit mutation transaction queries

Here is an example of an implicit mutation transaction:

public void executeWriteImplicitTransaction() { // end point final String END_POINT = "(End Point URL)"; // create node with label as label and properties. final String WRITE_QUERY = "CREATE (n:label {name : 'foo'})"; // Read the vertex created with label as label. final String READ_QUERY = "MATCH (n:label) RETURN n"; // create the driver final Driver driver = GraphDatabase.driver(END_POINT, AuthTokens.none(), Config.builder() .withEncryption() .withTrustStrategy(TrustStrategy.trustSystemCertificates()) .build()); // create the session config SessionConfig sessionConfig = SessionConfig .builder() .withFetchSize(1000) .withDefaultAccessMode(AccessMode.WRITE) .build(); final StringBuilder resultCollector = new StringBuilder(); // run the query as access mode write driver.session(sessionConfig).writeTransaction(new TransactionWork<String>() { @Override public String execute(final Transaction tx) { // execute the write query and consume the result. tx.run(WRITE_QUERY).consume(); // read the vertex written in the same transaction final List<Record> list = tx.run(READ_QUERY).list(); // read the result for (final Record record : list) { for (String key : record.keys()) { resultCollector .append(key) .append(":") .append(record.get(key).asNode().toString()); } } return resultCollector.toString(); } }); // at the end, the transaction is automatically committed. // close the driver. driver.close(); }

Reads made as part of mutation queries are executed under READ COMMITTED isolation with the usual guarantees for Neptune mutation transactions.

Whether or not you specifically pass in a session configuration, the transaction is always treated as a write transaction.

For conflicts, see Conflict Resolution Using Lock-Wait Timeouts.

Autocommit mutation transaction queries

Mutation autocommit queries inherit the same behavior as mutation implicit transactions.

If you do not pass in a session configuration, the transaction is treated as a write transaction by default.

In case of failure, mutation autocommit queries are not automatically retried.

Explicit mutation transaction queries

Here is an example of an explicit mutation transaction:

public void executeWriteExplicitTransaction() { // end point final String END_POINT = "(End Point URL)"; // create node with label as label and properties. final String WRITE_QUERY = "CREATE (n:label {name : 'foo'})"; // Read the vertex created with label as label. final String READ_QUERY = "MATCH (n:label) RETURN n"; // create the driver final Driver driver = GraphDatabase.driver(END_POINT, AuthTokens.none(), Config.builder() .withEncryption() .withTrustStrategy(TrustStrategy.trustSystemCertificates()) .build()); // create the session config SessionConfig sessionConfig = SessionConfig .builder() .withFetchSize(1000) .withDefaultAccessMode(AccessMode.WRITE) .build(); final StringBuilder resultCollector = new StringBuilder(); final Session session = driver.session(sessionConfig); // run the query as access mode write final Transaction tx = driver.session(sessionConfig).beginTransaction(); // execute the write query and consume the result. tx.run(WRITE_QUERY).consume(); // read the result from the previous write query in a same transaction. final List<Record> list = tx.run(READ_QUERY).list(); // read the result for (final Record record : list) { for (String key : record.keys()) { resultCollector .append(key) .append(":") .append(record.get(key).asNode().toString()); } } // commit the transaction and for rollback we can use tx.rollback(); tx.commit(); // close the session session.close(); // close the driver. driver.close(); }

Explicit mutation queries inherit the same behavior as implicit mutation transactions.

If you do not pass in a session configuration, the transaction is treated as a write transaction by default.

For conflicts, see Conflict Resolution Using Lock-Wait Timeouts.