Neo4j bolt protocol has very high latency

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()) { // 0.2ms to return from this call var result = session.Run("match(n:User { ID: 1}) return n.ID"); // Uncommenting this makes the whole thing take 60ms // result.ToArray(); } } 

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 .

+6
source share
1 answer

After further digging, I traced the problem with the Neo4j.Driver package on purpose, since the driver for NodeJS did not suffer from the same problem.

Cloning the current source of the package, building it, and linking to the DLL directly instead of the NuGet package completely fixed the problem. To imagine in perspective: the current version, which is on NuGet (1.0.2), takes 62 seconds to make 1000 simple matches with the local host, while the current source does it in 0.3 seconds (even beating the NodeJS driver 10 times )

I'm not quite sure why, but I'm sure this has something to do with the rda.SocketsForPCL dependency of the current package, which seems to be a glue library for sockets to work cross-platform. However, the current source references the System.Net.Sockets package for this.

So, in conclusion, this problem can be solved by reference to the current source assembly and will be completely solved when a new version of the package is released.

+4
source

All Articles