NoSQL Zone is brought to you in partnership with:

Max De Marzi, is a seasoned web developer. He started building websites in 1996 and has worked with Ruby on Rails since 2006. The web forced Max to wear many hats and master a wide range of technologies. He can be a system admin, database developer, graphic designer, back-end engineer and data scientist in the course of one afternoon. Max is a graph database enthusiast. He built the Neography Ruby Gem, a rest api wrapper to the Neo4j Graph Database. He is addicted to learning new things, loves a challenge and finding pragmatic solutions. Max is very easy to work with, focuses under pressure and has the patience of a rock. Max is a DZone MVB and is not an employee of DZone and has posted 59 posts at DZone. You can read more from them at their website. View Full User Profile

It’s Over 9000! Neo4j on WebSockets

03.18.2014
| 2478 views |
  • submit to reddit

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.


Published at DZone with permission of Max De Marzi, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)