Skip to content

1.2 Driver documentation

Zhen Li edited this page Apr 28, 2017 · 24 revisions

This document servers as a supplementary documents to our official driver documentation. If you have not read the official documentation yet, read that one first.

This document aims to provide an introduction to how to use the 1.2 driver by giving examples with additional explanation of how the driver is internally implemented. This document will NOT cover how to write Cypher or how to set up this driver in Azure or AWS.

This document is still under heavy construction and it is drafted by a terrible English writer. Have fun reading :)

0. Project version requirement

This driver requires .NET core 1.3 or .NET framework 4.6+ support.

1. Using driver with Causal Cluster

The main goal of 1.2 driver is to provide improved user experience with Neo4j Causal Cluster. To achieve this goal, the 1.2 driver provides improved Causal Clustering API. The improved API which

  • automates the passing of causal consistency context (bookmark) within Sessions
  • hides failures that are caused by cluster leader switch by replay transactions using the strategy defined by retry logic
  • prevents driver death when the database service is completely unavailable

When reading this section, we assume that you already have some basic knowledge of what is a Neo4j Causal Cluster, and what is Leader/Core/Read-replica member in a Casual Cluster.

1.1 Creating a driver

var driver = GraphDatabase.Driver( "bolt+routing://localhost:7687" );

A driver is thread-safe. It is recommended to use the driver in a singleton pattern. The driver maintains a connection pool to the servers that are currently known to the driver. The pool manages connections that could be claimed by sessions and transactions on demands. There is no maximum limitation on how many connections that could be created by the connection pool. While there is a config setting Config.MaxIdleSessionPoolSize (Badly named, it should be MaxIdleConnectionPoolSize) that an application user could modify to decide how many connections with each server that could be buffered in the pool. Setting this config to 0 will turn off pooling of connections with all servers.

1.1.1 Initial Uri

"bolt+routing://localhost:7687"

When creating the driver, an initial Uri is provided to the driver. In the initial Uri, scheme bolt+routing should be used for a driver with built-in routing to a Neo4j Causal Cluster.

The initial Uri directs the driver to a server in the cluster where the driver could inquire a routing table. A routing table carries routing information to cluster members and the roles of the cluster members. Using the routing information returned in the routing table, a connection pool for pooling connections to the cluster is then generated. Note: The server that could accept the inquiration of routing table are only the Core members (a.k.a. routers).

The roles of cluster members in a routing table could be routers (where a routing table could be inquired), readers (where read statements could be executed) and writers (where write statements could be executed). After the first routing table is received, the driver will start to use the newly returned routers for updating routing tables.

While if the driver ever runs out of routers (the driver failed to contact any of the router in a routing table) due to some errors inside the cluster (such as restart of the cluster), the driver will fall back to contract the server indicted by the initial Uri for a new routing table to recover from no router problem.

Note: this driver currently dose NOT support resolving DNS to IP addresses at connection pool level.

1.1.2 Authentication token and customized config

var driver = GraphDatabase.Driver( "bolt+routing://localhost:7687", myAuthToken, 
    new Config{EncryptionLevel= EncryptionLevel.None} );

If the server have authentication enabled (dbms.security.auth_enabled=true), then the driver should also authenticate itself to the server with the correct Neo4j login username and password.

Driver accepts a config where application users could modify the driver according to their application requirement. Read the API doc of Config class for more information.

1.2 Disposing driver disposes all connections

driver.Dispose();

When disposing a driver, all connections (both idle connections in the connection pool and connections used by on going sessions and transactions) will be closed. After the driver disposed, no more connections could be created.

1.3 Creating a session

using(var session = driver.Session(defaultAccessMode, bookmark))
{
    session.Run(cypherStatement);
}

In 1.2 driver, session holds a defaultAccessMode attribute which are used by session.Run and session.BeginTransaction to decide which server (readers or writers) to execute cypher statements.

While despite of the defaultAccessMode that is given to the session, the method session.WriteTransaction will always try to run a transaction on a write server. Similarly, the method session.ReadTransaction will always try to run on a read server. The example code bellow shows a session with default Read access mode could run a write statement in session.WriteTransaction:

using(var session = driver.Session(AccessMode.Read))
{
    session.WriteTransaction((tx)=>tx.Run(cypherStatement));
}

Note: A session is not thread-safe. Do NOT assign one session to two concurrent threads.

1.4 Causal consistency within a session

string bookmark = null;
using(var session = driver.Session())
{
    session.WriteTransaction(tx=>tx.Run("CREATE (n:Node)"));
    var result = session.ReadTransaction(tx=>tx.Run("MATCH (n) RETURN count(n)"));
    assertDatabaseNotEmpty(result);
    bookmark = session.LastBookmark;
}

using(var session = driver.Session(bookmark: bookmark))
{
    var result = session.ReadTransaction(tx=>tx.Run("MATCH (n) RETURN count(n)"));
    assertDatabaseNotEmpty(result);
}

When creating a session, the driver.Session method also accepts a parameter called bookmark. A bookmark is a reference to a previous transaction. In a Causal Cluster, when starting a new transaction, we might want to make sure that the server hosting the new transaction is at least as up-to-date as the transaction referenced by a bookmark.

When running multiple transactions inside a session, a new transaction is guaranteed to see the transactions that have been committed in the same session (a.k.a. Causal Consistency). To guarantee the order between sessions, we pass the bookmark between sessions. The latest bookmark of a session could be obtained via field called session.LastBookmark.

Only successful explicit transactions in a session could update the LastBookmark inside a session, which suggests:

  • a failing transaction will not change LastBookmark inside a session,
  • statement run in sesison.Run method has nothing to do with LastBookmark.

1.5 Run transaction with built-in retries

1.2 driver introduces two new methods ReadTransaction and WriteTransaction. They take Action<ITransaction> or Func<ITransaction, T> as input where a transaction tx is exposed for running cypher statements. The statements run inside the exposed transaction will be retried using our built-in retry logic if some specific recoverable errors happens (See more in RetryLogic).

When using retries, make sure that your statements inside retries are idempotent to avoid commit multiple times due to errors that might happen during commit, such as loss of Internet connection.

1.6 session.Run and session.BeginTransaction

These are the two methods that come with driver since 1.0. However after reading previous sections, it is not hard to notice that these two methods have limited uses on a driver for Neo4j Causal Cluster.

A statement executed by a session.Run method:

  • does not participate in Causal Consistency in a session,
  • cannot be retried using driver built-in retry logic when cluster instability error happens.

A statement run by a transaction obtained via session.BeginTransaction method:

  • cannot be replayed when cluster instability error happens.

While session.Run still provides a quick way to run a statement and session.BeginTransaction gives access to a transaction for application users to build their own error handling logic.

1.7 Consuming result

The result is lazied pulled at the time when they are iterated. If the result of previously executed statement in a session is not iterated, but the session is used for a new statement execution, then the result of the previous statement will be buffered.

2. Using driver with a stand alone single instance

Checkout official driver documentation