Public Service Announcement: Neo4j Drivers 1.2 Release


We are happy to announce that all our officially supported Bolt drivers are now available as versions 1.2. With this release, we massively improved the way you write code to work with a cluster, introducing reusable “transaction functions” and built-in retry functionality.

For some new capabilities we added new APIs. Here you can find detailed documentation and the driver repositories.

New Capabilities in all Neo4j Drivers


Drivers now handle cluster server failures and role changes automatically, allowing the application to treat the cluster as a single black box providing read and write service. This simplifies the programming model massively. You don’t have to care about cluster state or retrying operations after its change.

    • A Bolt+routing URI represents a network address
    • Automatic DNS “Round Robin” resolution can yield multiple hosts → addresses
    • A load balancer (e.g., AWS ELB) can route to multiple hosts → addresses
    • These are the routing bootstrap addresses: they should be configured to be probable core servers
    • Read Replicas cannot provide routing tables
    • When the driver is initialized, it goes to one of the bootstrap addresses to get a routing table

Neo4j drivers requests routing table
Neo4j drivers cluster returns routing table
Neo4j drivers routes client request
Neo4j drivers refreshes routing table


The Neo4j driver will switch traffic to an appropriate read or write connection depending on the transaction access mode. The read/write transaction access mode is a familiar SQL/ODBC/JDBC pattern of use.

We added new methods Session.read_transaction and Session.write_transaction to allow the execution of reusable units of work. You simply pass in a transaction function to the method. To allow re-execution of failed operations, duration for retries is configurable via max_retry_time in the Neo4j driver configuration (the default is 30s).

Here is an example on how you would use this capability:

Python Example


from neo4j.v1 import GraphDatabase


driver = GraphDatabase.driver("bolt+routing://server:7687",
                              auth=("neo4j", "password"))


def add_friends(tx, name, friend_name):
    tx.run("MERGE (p:Person {name: $name}) "
           "MERGE (f:Person {name: $friend_name}) "
           "MERGE (p)-[:KNOWS]-(f)",
           name=name, friend_name=friend_name)


def print_friends(tx, name):
    for record in tx.run(
          "MATCH (a:Person)-[:KNOWS]->(friend) WHERE a.name = $name "
          "RETURN friend.name ORDER BY friend.name", name=name):
        print(record["friend.name"])


with driver.session() as session:
    session.write_transaction(
      lambda tx:
        tx.run("create constraint on (p:Person) assert p.name is unique"))
    session.write_transaction(add_friends, "Arthur", "Guinevere")
    session.write_transaction(add_friends, "Arthur", "Lancelot")
    session.write_transaction(add_friends, "Arthur", "Merlin")
    session.read_transaction(print_friends, "Arthur")

Java Example


You can find the full code in this example project.

public class Person{
    private final static String COUNT_PEOPLE =
         ("MATCH (a:Person) RETURN count(a)");

    // callback method
    public static long count(Transaction tx){
        StatementResult result = tx.run(COUNT_PEOPLE);
        return result.single().get(0).asLong();
    }
    ...
}


public class SocialNetwork{
    public long countUsers() {
        try (Session session = driver.session()){
            return session.readTransaction(Person::count);
        }
    }

    public long addUser(Person user) {
        System.out.println(format("Adding user %s", user));
        try (Session session = driver.session(){
            return session.writeTransaction(user::save);
        }
    }
}

We decoupled the Session from a single underlying connection; a Session can now be defined as a causally linked sequence of transactional units of work.

You don’t need to manage bookmarks for causal consistency manually any longer. Bookmarks are now automatically passed between transactions within a routing session. This makes causal consistency the default interaction mode with the database cluster.

Auto-commit transactions (Session.run) will now run partially synchronous to the network (RUN and PULL_ALL will be sent to the server, the RUN response will be immediately received); this allows exceptions to be raised at a more logical point in the application

Updates in Some of the Neo4j Drivers


The Python language driver now includes a compiled C module included for improved performance on supported platforms. Please let us know if this works for you.

If the provided hostname resolves to multiple IP addresses most of the drivers (except .NET) can handle this now.

As always, we’d love your feedback, so please try out the new Neo4j driver releases and raise feature or bug requests on the driver repositories. Please let us also know what you think about the new APIs and if there are ways to improve them.

If you need quick help, please join neo4j.com/Discord and ask in the #drivers or the appropriate #neo4j-<language> channel. Otherwise you can also ask on Stack Overflow. Please tag your Stack Overflow questions with [neo4j-<language>-driver]

Enjoy the new Neo4j drivers,

Nigel Small, for the Neo4j Drivers Team