I generally wait for Neo4j to execute a Cypher query as the following.
var exeResult = session.ExecuteWriteAsync(async x =>
{
var result = await x.RunAsync(GetQuery());
return await result.ToListAsync();
});
await exeResult;
// or:
// exeResult.Wait();
Few concerns about this approach:
Is this the correct way to wait for a Cypher query execution?
If the query execution fails (e.g., a typo in the Cypher query), the exception is not caught in the try-catch block that encompasses this snippet; instead, the program exits.
If multiple queries are to be executed (e.g., the above block should be repeated for GetQuery(x) where x changes between different queries), the behavior of the above block is hard to predict. Sometimes, it runs to completion; sometimes, the program exists with no error messages while the queries are "partially" executed (i.e., some executed, some errored out). It seems the second query execution starts before the previous one ends, leading to race conditions? or sometimes I get Neo.TransientError.Transaction.DeadlockDetected.
In short, I am unsure if this is the correct way to wait for a query's successful execution.
That seems a good alternative, though I don't have the blocking alternatives. For instance, I only have Driver.AsyncSession and do not have Driver.Session; or I have ExecuteWriteAsync but do not have ExecuteWrite.
I am using Neo4j.Driver version 5.3.0. Should I add a different dependency?
That statement was from the .NET driver doc, so I imagine it is supported. I can try to find out.
In the meantime, I looked at the java driver source code a while back. What I found was the blocking calls where actually using the non-blocking calls, waiting, consuming all the results, and returning the results. Thus, it was doing what you want to do, so I encourage you to look at that library's source code to see how they did it. Maybe you can get some ideas to achieve what you want. Sorry, I am not a C# developer.
var exeResult = session.ExecuteWriteAsync(async x =>
{
var result = await x.RunAsync("BAD CYPHER");
return await result.ToListAsync();
});
try
{
await exeResult;
}
catch(ClientException ex)
{
Console.WriteLine(ex);
}
And get the exception written to the output, so they are being raised, and caught - I guess this might be to do with what you're doing with the exception after you've caught it - i.e. are you logging/doing something with it?
For the multiple queries side of thing - they should be in separate sessions. Unless you're wanting it to all be in the same transaction - in which case you should be using the session.BeginTransactionAsync() methods. My guess is that the unpredictability is due to consumption of the response, and tidying up etc - new session instances should resolve that, and it's how the driver is intended to be used.
Thank you @charlotte.skardon. I am running multiple transactions with the same session like:
using var session = driver.AsyncSession(x => x.WithDefaultAccessMode(AccessMode.Write));
var q1Result = await session.ExecuteWriteAsync(async x =>
{
var result = await x.RunAsync(GetQuery("query1"));
return await result.ToListAsync();
});
var q2Result = await session.ExecuteWriteAsync(async x =>
{
var result = await x.RunAsync(GetQuery("query2"));
return await result.ToListAsync();
});
Are you suggesting I should not be reusing the session as this and instead create a separate session for each query?
what you're doing with the exception after you've caught it
I both log it to a log file and print it to a console. But when an exception is raised, it is neither added to the console nor the logs.
Yes, you should use a new session per transaction.
Sessions are resource cheap as they've already been instantiated - so you don't need to worry about that side of things.
I'm pretty confident that once you've gone down this route, a lot of the missing executions etc will resolve themselves, as I think you're not consuming your session results and that's giving you problems.
Well, there is a good use case for executing multiple queries in one session. This would occur if you need an entire operation to be atomic and you want to use the results of one query for the next query. These have to be done in the same transaction so you can roll back any changes if any issue arises during the sequence of queries.