Reputation: 58494
I am running the below MERGE
query against my Neo4j server from a client application in 10 parallel threads, the newFoo
and id
parameters are the same on all 10 runs:
MERGE (foo:Foo { id: {id} })
ON MATCH
SET foo = {newFoo}
After running this, I run the below query to expect 1
but I instead get 10
:
match (f:Foo)
return count(f)
I thought that MERGE
runs in an atomic transaction but apparently not. Am I doing something wrong here?
Below is the code that I used to reproduce the issue:
public static async Task RunInParallel()
{
var client = new GraphClient(new Uri("http://localhost:7474/db/data"), "neo4j", "1234567890")
{
JsonContractResolver = new CamelCasePropertyNamesContractResolver()
};
client.Connect();
var foo = new Foo
{
Id = "1",
Name = "Foo",
Slug = "foo-bar-foo"
};
List<Task> tasks = new List<Task>();
for (int i = 0; i < 10; i++)
{
var task = client.Cypher
.Merge("(foo:Foo { id: {id} })")
.OnMatch()
.Set("foo = {newFoo}")
.WithParams(new
{
Id = foo.Id,
NewFoo = foo
})
.ExecuteWithoutResultsAsync();
tasks.Add(task);
}
await Task.WhenAll(tasks.ToArray());
}
Upvotes: 4
Views: 1200
Reputation: 8556
MERGE
(by itself) does not guarantee uniqueness. When using MERGE
(on a unique property) you should always create a uniqueness constraint for the specified property:
CREATE CONSTRAINT ON (f:Foo) ASSERT f.id IS UNIQUE
This will ensure you aren't creating any duplicates.
Edit
MERGE
without a uniqueness constraint is not thread safe. Adding a uniqueness constraint ensures an index lock is held before writing, making the operation thread safe.
Upvotes: 8