4
votes

I have the following 2 existing nodes in my graph.

A Customer node identified by a unique customer number. A Product node identified by a unique ISBN identifier.

I want to create an association between one Customer node and one Product node. But I want to represent this association as a new node called a License node which will have one link to the Customer node and one link to the Product node.

This License node will have a new internal identifier generated as a random GUID.

My logic in my application which creates the new License node and links them to the other 2 nodes is executed in one transaction.

if (Product NOT already associated with the License for that Customer) create a new License node with a new random GUID create a relationship from the new License Node to the Product Node create a relationship from the Customer Node to the new License Node

However multiple requests can arrive at the same time with the same ISBN and customer number. When this happens I am sometimes getting duplicate License nodes created for the same Customer and Product nodes. The transaction in spring data neo4j does not seem to prevent this from happening.

Example of correctly added License

Example of License added twice

How can I ensure that only one License node will get created between the Customer node and the Product node?

1
How looks like the Cypher query you are using to execute the logic?Bruno Peres
Thanks Bruno, I am using Java 1.8 and Spring Data Neo4j 4.2.0 and the cypher gets generated by the framework. The transaction is also defined as an annotation. I will try out the suggestions made by frant.hartm in his answer below.Donal Hurley

1 Answers

0
votes

The transaction in spring data neo4j does not seem to prevent this from happening.

Neo4j has read commited isolation level for transactions. To prevent this you would need serializable.

To achieve what you need you could:

  • lock Product and Customer node before doing the Product NOT already associated with... check. You could use a query like this to do that (within the same transaction):

    MATCH (n:Product) WHERE ID(n) = {id} REMOVE n._lock
    

    and similar for Customer.

  • add a special key to License which is a concatenation of Product and Customer ids - then create a unique constraint on that.