neo4j / neo4j-javascript-driver

Neo4j Bolt driver for JavaScript
https://neo4j.com/docs/javascript-manual/current/
Apache License 2.0
839 stars 148 forks source link

Driver Performance #1185

Open Sydney-o9 opened 4 months ago

Sydney-o9 commented 4 months ago

We are Neo4j cloud users and are measuring performance using Sentry.

Our backend application simply leverages the neo4j-javascript-driver to make calls to our Neo4j cloud db.

It takes almost 750ms for the driver to initialise and fulfil the request

Neo4j

I'm looking for ways to improve this, ideally would like to know how long it takes for:

Overall, I found the driver to be slow, but I have no proof, I want to measure this.

Can you give us ways to assess the performance of the driver in our application?

Sydney-o9 commented 3 months ago

Hi Team, if you could please give some indication here - that'd be appreciated

bigmontz commented 3 months ago

Hi @Sydney-o9,

the internal timings in the driver are not exposed, but you can troubleshooting by check:

import neo4j from 'neo4j-driver'

const mydbname = "the database name, usually 'neo4j'"
const driver = neo4j.driver(...)

// Check the timing for this request
// This one should just bootstrap the driver and do a handshake with the database
await driver.getServerInfo({ database: mydbname }) 

// Measure the time for running the query
const { records, summary } = await driver.executeQuery(query, params, { database: mydbname })

You an also check in the summary, the time the database get to consume the result (https://neo4j.com/docs/api/javascript-driver/current/class/lib6/result-summary.js~ResultSummary.html#instance-member-resultConsumedAfter), this can be useful for checking if the issue are in the query.

bigmontz commented 3 months ago

Could you inform also the NodeJS version, Driver version, Database version and some snippet to see what you're doing?

Overdrivr commented 2 months ago

Hello, We're facing similar performance issues :

All timings are captured around this snipped of code

export class Neo4jHelper {
  private driver: Driver

  constructor() {
      this.driver = neo4j.driver(
        neo4jAccount?.uri,
        neo4j.auth.basic(neo4jAccount?.user, neo4jAccount?.password),
        { disableLosslessIntegers: true },
      )
  }

  public async read(query: string, config?: TransactionConfig) {
    const session = this.driver.session()
    try {
      return await session.executeRead(tx => tx.run(query, config))
    } finally {
      await session.close()
    }
  }
}

A few examples that caught our attention, they are reproducible and don't appear to be some random variation. Almost all of our call exhibit the same patterns.

Example 1

The call to read took 700ms, to return the label of a single node. Consuming the records themselves takes 0 time units (0 ms).

image image

Example 2

A slightly more complex query, that took approximately 2.4s to read() and returns about 2k records (with 10 fields each)

image

The query itself took 260 ms for records to be consumed, which leaves an overhead of 2.2s in the driver itself.

image

Questions

bigmontz commented 2 months ago

The time measures reported in the resultConsumedAfter and resultAvailableAfter are calculated without taking in consideration any networking.

The driver itself take some work to bootstrap, so if you are creating a driver for each request, you will might have issues. Setting the database name in the session creation is highly recommend in the environments with high latency, since when the database name is not set, the driver will take an extra round trip to resolve the database name. In Aura, the database name is neo4j. So, you might create the session like:

const session = this.driver.session({ database: 'neo4j' })

The first request might get a bit more time because the driver will fetch the cluster formation before trigger the first query, but the next requests should be faster.