Run non-blocking asynchronous queries
The examples in Query the database and Run your own transactions use the driver in its synchronous form. This means that, when running a query against the database, your application waits for the server to retrieve all the results and transmit them to the driver. This is not a problem for most use cases, but for queries that have a long processing time or a large result set, asynchronous handling may speed up your application.
Asynchronous managed transactions
You run an asynchronous transaction through an AsyncSession
.
The flow is similar to that of regular transactions, except that async transaction functions return a CompletionStage
object (which may be further converted into CompletableFuture
).
package demo;
import java.util.Map;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import org.neo4j.driver.async.AsyncSession;
import org.neo4j.driver.AuthTokens;
import org.neo4j.driver.Driver;
import org.neo4j.driver.GraphDatabase;
import org.neo4j.driver.summary.ResultSummary;
import org.neo4j.driver.SessionConfig;
public class App {
public static void main(String... args) throws ExecutionException, InterruptedException {
final String dbUri = "<URI for Neo4j database>";
final String dbUser = "<Username>";
final String dbPassword = "<Password>";
try (var driver = GraphDatabase.driver(dbUri, AuthTokens.basic(dbUser, dbPassword))) { (1)
driver.verifyConnectivity();
var summary = printAllPeople(driver);
// Block as long as necessary (for demonstration purposes)
System.out.println(summary.get()); (8)
}
}
public static CompletableFuture<ResultSummary> printAllPeople(Driver driver) {
var session = driver.session(AsyncSession.class, SessionConfig.builder().withDatabase("neo4j").build()); (2)
var query = """
UNWIND ['Alice', 'Bob', 'Sofia', 'Charles'] AS name
MERGE (p:Person {name: name}) RETURN p.name
""";
var summary = session.executeWriteAsync(tx -> tx.runAsync(query) (3)
.thenCompose(resCursor -> resCursor.forEachAsync(record -> { (4)
System.out.println(record.get(0).asString());
})))
.whenComplete((result, error) -> { (5)
session.closeAsync();
})
.toCompletableFuture(); (6)
return summary; (7)
}
}
1 | Driver creation is the same in the synchronous and asynchronous versions. |
2 | An asynchronous session is created by providing AsyncSession.class as first parameter to Driver.session() , which returns an AsyncSession object.
Note that async sessions may not be opened as resources with try statements, as the driver can’t know when it is safe to close them. |
3 | As for regular transactions, .executeWriteAsync() (and executeReadAsync() ) take a transaction function callback.
Inside the transaction function, run queries with .runAsync() .
Each query run returns a CompletionStage . |
4 | Optionally use methods on CompletionStage to process the result in the asynchronous runner.
The query’s result set is available as an AsyncResultCursor , which implements a similar set of methods for processing the result to those of synchronous transactions (see Transactions → Process query results).Inner object types are the same as the synchronous case (i.e. Record , ResultSummary ). |
5 | Optionally run operations once the query has completed, such as closing the driver session. |
6 | CompletableFuture is a convenient type to return back to the caller. |
7 | Contrary to synchronous transactions, .executeWriteAsync() and executeReadAsync() return the result summary only.
Result processing and handling must be done inside the asynchronous runner. |
8 | .get() waits as long as necessary for the future to complete, and then returns its result. |
The methods .executeReadAsync() and .executeWriteAsync() have replaced .readTransactionAsync() and .writeTransactionAsync() , which are deprecated in version 5.x and will be removed in version 6.0.
|
Glossary
- LTS
-
A Long Term Support release is one guaranteed to be supported for a number of years. Neo4j 4.4 is LTS, and Neo4j 5 will also have an LTS version.
- Aura
-
Aura is Neo4j’s fully managed cloud service. It comes with both free and paid plans.
- Cypher
-
Cypher is Neo4j’s graph query language that lets you retrieve data from the database. It is like SQL, but for graphs.
- APOC
-
Awesome Procedures On Cypher (APOC) is a library of (many) functions that can not be easily expressed in Cypher itself.
- Bolt
-
Bolt is the protocol used for interaction between Neo4j instances and drivers. It listens on port 7687 by default.
- ACID
-
Atomicity, Consistency, Isolation, Durability (ACID) are properties guaranteeing that database transactions are processed reliably. An ACID-compliant DBMS ensures that the data in the database remains accurate and consistent despite failures.
- eventual consistency
-
A database is eventually consistent if it provides the guarantee that all cluster members will, at some point in time, store the latest version of the data.
- causal consistency
-
A database is causally consistent if read and write queries are seen by every member of the cluster in the same order. This is stronger than eventual consistency.
- NULL
-
The null marker is not a type but a placeholder for absence of value. For more information, see Cypher → Working with
null
. - transaction
-
A transaction is a unit of work that is either committed in its entirety or rolled back on failure. An example is a bank transfer: it involves multiple steps, but they must all succeed or be reverted, to avoid money being subtracted from one account but not added to the other.
- backpressure
-
Backpressure is a force opposing the flow of data. It ensures that the client is not being overwhelmed by data faster than it can handle.
- transaction function
-
A transaction function is a callback executed by an
executeRead
orexecuteWrite
call. The driver automatically re-executes the callback in case of server failure. - Driver
-
A
Driver
object holds the details required to establish connections with a Neo4j database.