neo4j-php / neo4j-php-client

Php client and driver for neo4j database
https://neo4j.com/developer/php/
MIT License
163 stars 40 forks source link

Do we need to create connection for every execution? #71

Closed milanchheda closed 3 years ago

milanchheda commented 3 years ago

Apologies its more of question and not bug πŸ™

We have a Laravel application and we are using the mentioned package to interact with our neo4j. Thank you for the excellent package.

Do we need to create connection for every execution? Basically, create(), withBuilder() can this be extracted or something so that we don't do this for every transaction /query?

Here is the code I m talking about:

$client = ClientBuilder::create()
    ->withFormatter(new BasicFormatter())
    ->withDriver('neo4j', 'neo4j+s://bolt.db.xxxxxxxxx.xx:7687', Authenticate::basic('neo4j', 'xxxxxxxxxxxxxx'))
    ->build();

Additional info: PHP: 8.0.6 Laravel: 8.51.0 "laudis/neo4j-php-client": "^2.0",

transistive commented 3 years ago

Hello @milanchheda,

Thank you for posting this issue! Naturally, it is possible to do so! This library is Object-Oriented, so in theory you can do whatever you want with the instance.

For example: you can bind the driver instance to your container and inject it in your controllers for easy usage!

https://laravel.com/docs/8.x/container

PS: We are working on a library for laravel! Stay tuned :+1:

fbiville commented 3 years ago

Drivers are usually singletons and can be shared across different parts of your application. I think it applies as well to its enclosing Client companion.

Sessions, on the other hand, are not meant to be shared and should be relatively short-lived (as short as the unit of work they execute requires).

milanchheda commented 3 years ago

Thank you for the response.

We have a frontend application that hits the laravel api endpoint. The laravel api connects to neo4j, fires the query and returns the response. Every time the api is hit, it creates a new connection. Can you suggest how I can have optimize here and not have the need to create connection every time?

Also, laravel library would be amazing. Thank you for the awesome work. πŸ‘πŸΌπŸ™

transistive commented 3 years ago

Hello @milanchheda,

A normal PHP session always runs single threaded and does not have any form of persistence between requests. Because of this keeping connections open is impossible.

There are PHP solutions like swoole that allow for this, but I haven't used them yet.

You can also use https://laravel.com/docs/8.x/octane which runs on top of swoole but beware, the driver isn't thread safe yet.

transistive commented 3 years ago

Also, I wouldn't worry too much about this. This also happens when connecting to any database like sql or redis. Connections open in a few milliseconds. If you are having issues with performance because of the driver, I would love to know

milanchheda commented 3 years ago

Hey @transistive ,

So I have this query which upon execution in neo4j shows Cypher version: CYPHER 4.2, planner: COST, runtime: INTERPRETED. xxxxx total db hits in 116 ms.:

PROFILE 
MATCH 
p=(n1:TOK)-[:NXT]->(n2:TOK)-[:NXT]->(n3:TOK)-[:NXT]->(n4:TOK)-[:NXT]->(n5:TOK)-[:NXT]->(n6:TOK)-[:NXT]->(n7:TOK {wordl: 'conflicts'})-[:NXT]->(n8:TOK {wordl: 'of'})-[:NXT]->(n9:TOK {wordl: 'interest'})-[:NXT]->(n10:TOK)-[:NXT]->(n11:TOK)-[:NXT]->(n12:TOK)-[:NXT]->(n13:TOK)-[:NXT]->(n14:TOK)-[:NXT]->(n15:TOK)
RETURN
n1.wordl as Word1,n2.wordl as Word2,n3.wordl as Word3,n4.wordl as Word4,n5.wordl as Word5,n6.wordl as Word6,n7.wordl as Word7,n8.wordl as Word8,n9.wordl as Word9,n10.wordl as Word10,n11.wordl as Word11,n12.wordl as Word12,n13.wordl as Word13,n14.wordl as Word14,n15.wordl as Word15,count(*) as Count
ORDER BY 
Count DESC;

But when I send this query using run it takes almost 20 seconds.

$time_start = microtime(true);

$results = $client->run('PROFILE MATCH 
                    p=' . $this->patternString . '
                RETURN
                        ' . $this->returnString . '
                    ORDER BY 
                        Count DESC;
                ');

$time_end = microtime(true);

//dividing with 60 will give the execution time in minutes other wise seconds
$execution_time = ($time_end - $time_start) / 60;

I simply added microtime(true) just before and after the above code to calculate the time run takes.

Would you know why it takes that long for run to execute the Cypher query? Is there something I need to change in my query? Let me know if you need any other details. Its very surprising that a query takes 100ms takes almost 20 seconds when executed through code.

Thanks

transistive commented 3 years ago

Thank you for your response. There is another issue with the same problem. I have identified some bottlenecks. Please give me until the end of the weekend to fix these.

milanchheda commented 3 years ago

Sure @transistive. Much appreciated. πŸ™πŸΌ

transistive commented 3 years ago

Closed due to inactivity and a considerable increase in performance because of referenced PRs.