It’s over 9000! Neo4j on WebSockets

it__s_over_9000_

In the last blog post we managed to run Neo4j at Ludicrous Speed over http using Undertow and get to about 8000 requests per second. If we needed more speed we can scale up the server or we can scale out to multiple servers by switching out the GraphDatabaseFactory and using the HighlyAvailableGraphDatabaseFactory class instead in Neo4j Enterprise Edition.

But can we go faster on a single server without new hardware? Well… yes, if we’re willing to drop http and switch to Web Sockets.

WebSocket is a protocol providing full-duplex communications channels over a single TCP connection…allowing for messages to be passed back and forth while keeping the connection open.

Sounds complicated? It’s not, we can add it to our existing Undertow server with 18 more lines of code. No really:

                         .addPath("/websocket", websocket(new WebSocketConnectionCallback() {
                            @Override
                            public void onConnect(WebSocketHttpExchange exchange, WebSocketChannel channel) {
                                channel.getReceiveSetter().set(new AbstractReceiveListener() {
                                    @Override
                                    protected void onFullTextMessage(WebSocketChannel channel, BufferedTextMessage message) {
                                        String data = message.getData();
                                        try {
                                            List<Map<String, AtomicInteger>> results = getCrossReferences(data);
                                            WebSockets.sendBinary(ByteBuffer.wrap(objectMapper.writeValueAsBytes(results)), channel, null);
                                        } catch (IOException e) {
                                            WebSockets.sendText("Error:" + e.toString(), channel, null);
                                        }
                                    }
                                });
                                channel.resumeReceives();
                            }
                        })

We are adding a “/websocket” endpoint to our existing Undertow server that: 1. sets up a channel 2. performs the CrossReference query we’ve seen before when it receives a message and 3. sends the results back. So let’s test this out. I believe Gatling 2.0 has web socket testing integrated, but I’m still using 1.5.3, so we’ll borrow the Gatling Web Sockets Library from Andrew Duffy. To use it, we’ll just compile it and add it to the lib directory of our performance testing project. Let’s create a new web socket test for this that looks very similar to our previous http test:

class TestCrossReferenceWebSocket extends Simulation {

  val testfile = csv("test-data.txt").circular

  val scn = scenario("Cross Reference via WebSocket")
    .exec(websocket("socket").open("ws://localhost:7474/websocket", "socket_open"))
    .during(30) {
      feed(testfile)
      .exec(websocket("socket")
        .sendMessage("""{"cc": "${cc}", "phone": "${phone}", "email": "${email}", "ip": "${ip}" }""",
          "socket_send"))
        .pause(0 milliseconds, 1 milliseconds)
    }
    .exec(websocket("socket").close("socket_close")
  )

  setUp(scn.users(16))
}

Each simulated user will open a connection to the server, spend 30 seconds reading test data from a file, sending requests to the server, and finally it will close the connection once it’s done. Let’s fire it up and oh my…

Screen Shot 2014-03-09 at 3.34.57 PM

Zoom in on that… 28 thousand requests per second… on my laptop. That’s 3.5 times as many requests as last time, and about 22 times what we started out with. Before you even ask, of course you can go faster still if you switch out JSON for a binary serialization format, but I’ll leave that as an exercise for the reader since there are so many of them. Feel free to fork this project on Github and have a go at it.

If you want to try sending Cypher over Web Sockets in MessagePack, you can also take a look at Michael Hunger‘s Cypher Websocker Endpoint project on Github. If ZeroMQ is more your thing, Nigel Small has his ZeroGraph project.

Tagged , , , , , , , ,

4 thoughts on “It’s over 9000! Neo4j on WebSockets

  1. Timmy Storms says:

    Hi Max,

    Thanks for this great post. I’m having trouble running the gatling web socket script though. After I’ve added the gatling-websocket-0.0.9.jar, I’m getting “Exception in thread “main” scala.tools.nsc.MissingRequirementError: object scala not found.”. Any idea what’s causing this? I’m not quite a scala developer. ;-)

  2. Timmy Storms says:

    Nevermind, the jar file was corrupt. Probably af firewall issue…

  3. […] By that I mean that it’s completely customizable. You can add API Extensions, Plugins, Kernel Extensions, your own Cypher Functions, your own modified Kernel, its completely embeddable, so pretty much what-ever-you-want. […]

Leave a comment