I am using Neo4j for a project using the official Neo4j driver for .NET found here:
https://www.nuget.org/packages/Neo4j.Driver
This driver uses the bolt protocol, based on my assumption that a specialized binary protocol will be more efficient than the HTTP API. But from the very beginning of the project, I noticed relatively high delays from Neo4j even for very simple operations. For example, such a match as the following: 30-60 ms, when UserID is an indexed field, and the database is otherwise completely empty:
match(n:User { UserID: 1 }) return n.UserID
This behavior occurs both on my local machine (near zero network load) and in our production environment. I started researching this today and found that the query returns quickly, but it takes a long time to actually stream the results. For example, the query below takes 0.2 ms before the call is returned to the local host, but then calls ToArray() on result (buffering records, which in this case is the only integer field) increases the time by 60 ms .
using (var driver = GraphDatabase.Driver($"bolt://localhost:7687", AuthTokens.Basic("neo4j", "1"))) { using (var session = driver.Session()) {
Then I tried a community sponsored Neo4jClient package that works over HTTP:
https://github.com/Readify/Neo4jClient
With the same request, the total time is reduced to 0.5 ms:
var client = new GraphClient(new Uri("http://localhost:7474/db/data"), "neo4j", "1"); client.Connect(); client.Cypher.Match("(n:User { ID: 1})").Return<int>("n.ID").Results.ToArray();
Running a more formal test yields the following results: a huge difference between the official bolt-driven driver and the HTTP-based Neo4jClient.
Host Process Environment Information: BenchmarkDotNet.Core=v0.9.9.0 OS=Microsoft Windows NT 6.2.9200.0 Processor=Intel(R) Core(TM) i7-4770 CPU 3.40GHz, ProcessorCount=8 Frequency=3312642 ticks, Resolution=301.8739 ns, Timer=TSC CLR=MS.NET 4.0.30319.42000, Arch=32-bit RELEASE GC=Concurrent Workstation JitModules=clrjit-v4.6.1586.0 Type=Neo4jBenchmarks Mode=Throughput Platform=X64 Jit=RyuJit Method | Median | StdDev | Scaled | Scaled-SD | ------------- |--------------- |------------ |------- |---------- | Neo4jClient | 382.5675 us | 3.3771 us | 1.00 | 0.00 | Neo4jSession | 61,299.9382 us | 690.1626 us | 160.02 | 2.24 |
Thus, the HTTP client is 160 times faster when network overhead is negligible.
I also ran a test in our production environment, and although the difference was not that big, the HTTP method was still 6 times faster (and my network connection to production was pretty slow).
Full test code:
public class Neo4jBenchmarks { private readonly IDriver _driver; private readonly GraphClient _client; public Neo4jBenchmarks() { _driver = GraphDatabase.Driver("bolt://localhost:7687", AuthTokens.Basic("neo4j", "1")); _client = new GraphClient(new Uri("http://localhost:7474/db/data"), "neo4j", "1"); _client.Connect(); } [Benchmark(Baseline = true)] public void Neo4jClient() { _client.Cypher.Match("(n:User { ID: 1})").Return<int>("n.ID").Results.ToArray(); } [Benchmark] public void Neo4jSession() { using (var session = _driver.Session()) { session.Run("match(n:User { ID: 1}) return n.ID").ToArray(); } } }
Neo4j CE 3.0.4 is running on my machine and in production (it is currently a community), although I run it on Windows 10, and production is a Linux machine. We have not modified any settings, as far as I know, but I doubt that this can explain the difference in performance by 160 times.
I also tried reusing the session object (which I think is a very bad idea because it is not thread safe) because creating a session involves creating a transaction to see if this has changed, but it was not noticeable.
I would like to use Neo4jClient, but we really need the ability to execute arbitrary string requests, while Neo4jClient relies heavily on the free API and, although it offers low-level string mode, it is deprecated and is actively discouraged in the documentation .