In many use cases, a certain level of uniqueness is desired among entities. You could for instance imagine that only one user with a certain e-mail address may exist in a system. If multiple concurrent threads naively try to create the user, duplicates will be created. There are three main strategies for ensuring uniqueness, and they all work across HA and single-instance deployments.
By using a single thread, no two threads will even try to create a particular entity simultaneously. On HA, an external single-threaded client can perform the operations on the cluster.
By using put-if-absent
functionality,
entity uniqueness can be guaranteed using an index. Here the index acts as the lock and will only lock the smallest part needed to guaranteed uniqueness across threads and transactions.
To get the more high-level get-or-create
functionality make use of UniqueFactory
as seen in the example below.
Example code:
public Node getOrCreateUserWithUniqueFactory( String username, GraphDatabaseService graphDb ) { UniqueFactory<Node> factory = new UniqueFactory.UniqueNodeFactory( graphDb, "users" ) { @Override protected void initialize( Node created, Map<String, Object> properties ) { created.setProperty( "name", properties.get( "name" ) ); } }; return factory.getOrCreate( "name", username ); }
Important | |
---|---|
While this is a working solution, please consider using the preferred Section 13.6.2, “Get or create” instead. |
By using explicit, pessimistic locking, unique creation of entities can be achieved in a multi-threaded environment. It is most commonly done by locking on a single or a set of common nodes.
One might be tempted to use Java synchronization for this, but it is dangerous. By mixing locks in the Neo4j kernel and in the Java runtime, it is easy to produce deadlocks that are not detectable by Neo4j. As long as all locking is done by Neo4j, all deadlocks will be detected and avoided. Also, a solution using manual synchronization doesn’t ensure uniqueness in an HA environment.
Example code:
public Node getOrCreateUserPessimistically( String username, GraphDatabaseService graphDb, Node lockNode ) { Index<Node> usersIndex = graphDb.index().forNodes( "users" ); Node userNode = usersIndex.get( "name", username ).getSingle(); if ( userNode != null ) return userNode; Transaction tx = graphDb.beginTx(); try { tx.acquireWriteLock( lockNode ); userNode = usersIndex.get( "name", username ).getSingle(); if ( userNode == null ) { userNode = graphDb.createNode(); userNode.setProperty( "name", username ); usersIndex.add( userNode, "name", username ); } tx.success(); return userNode; } finally { tx.finish(); } }
Copyright © 2012 Neo Technology