Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Changed transaction taking around unique indexes

It will open a transaction as late as possible, after checking if
the entity exists in an index.
  • Loading branch information...
commit d2c5ff700abd0cd4d050975e53e0e07dd1f3b3d6 1 parent 6c827d5
Andres Taylor authored
20 kernel/src/main/java/org/neo4j/graphdb/index/UniqueFactory.java
@@ -191,11 +191,11 @@ protected void delete( Relationship relationship )
191 191 */
192 192 public final T getOrCreate( String key, Object value )
193 193 {
194   - Transaction tx = graphDatabase().beginTx();
195   - try
  194 + T result = index.get( key, value ).getSingle();
  195 + if ( result == null )
196 196 {
197   - T result = index.get( key, value ).getSingle();
198   - if ( result == null )
  197 + Transaction tx = graphDatabase().beginTx();
  198 + try
199 199 {
200 200 Map<String, Object> properties = Collections.singletonMap( key, value );
201 201 T created = create( properties );
@@ -209,14 +209,14 @@ public final T getOrCreate( String key, Object value )
209 209 {
210 210 delete( created );
211 211 }
  212 + tx.success();
  213 + }
  214 + finally
  215 + {
  216 + tx.finish();
212 217 }
213   - tx.success();
214   - return result;
215   - }
216   - finally
217   - {
218   - tx.finish();
219 218 }
  219 + return result;
220 220 }
221 221
222 222 /**
128 kernel/src/test/java/org/neo4j/graphdb/index/UniqueFactoryTest.java
... ... @@ -0,0 +1,128 @@
  1 +package org.neo4j.graphdb.index;
  2 +
  3 +import static org.junit.Assert.assertEquals;
  4 +import static org.junit.Assert.assertSame;
  5 +import static org.junit.Assert.assertTrue;
  6 +import static org.junit.Assert.fail;
  7 +import static org.mockito.Mockito.mock;
  8 +import static org.mockito.Mockito.times;
  9 +import static org.mockito.Mockito.verify;
  10 +import static org.mockito.Mockito.when;
  11 +
  12 +import java.util.Collections;
  13 +import java.util.Map;
  14 +import java.util.concurrent.atomic.AtomicBoolean;
  15 +
  16 +import org.junit.Test;
  17 +import org.neo4j.graphdb.GraphDatabaseService;
  18 +import org.neo4j.graphdb.Node;
  19 +import org.neo4j.graphdb.Transaction;
  20 +
  21 +public class UniqueFactoryTest
  22 +{
  23 + @Test
  24 + public void shouldUseConcurrentlyCreatedNode()
  25 + {
  26 + // given
  27 + GraphDatabaseService graphdb = mock( GraphDatabaseService.class );
  28 + @SuppressWarnings( "unchecked" )
  29 + Index<Node> index = mock( Index.class );
  30 + Transaction tx = mock( Transaction.class );
  31 + when( graphdb.beginTx() ).thenReturn( tx );
  32 + when( index.getGraphDatabase() ).thenReturn( graphdb );
  33 + @SuppressWarnings( "unchecked" )
  34 + IndexHits<Node> getHits = mock( IndexHits.class );
  35 + when( index.get( "key1", "value1" ) ).thenReturn( getHits );
  36 + Node createdNode = mock( Node.class );
  37 + when( graphdb.createNode() ).thenReturn( createdNode );
  38 + Node concurrentNode = mock( Node.class );
  39 + when( index.putIfAbsent( createdNode, "key1", "value1" ) ).thenReturn( concurrentNode );
  40 + UniqueFactory.UniqueNodeFactory unique = new UniqueFactory.UniqueNodeFactory( index )
  41 + {
  42 + @Override
  43 + protected void initialize( Node created, Map<String, Object> properties )
  44 + {
  45 + fail( "we did not create the node, so it should not be initialized" );
  46 + }
  47 + };
  48 +
  49 + // when
  50 + Node node = unique.getOrCreate( "key1", "value1" );
  51 +
  52 + // then
  53 + assertSame(node, concurrentNode);
  54 + verify( index ).get( "key1", "value1" );
  55 + verify( index ).putIfAbsent( createdNode, "key1", "value1" );
  56 + verify( graphdb, times( 1 ) ).createNode();
  57 + verify( tx ).success();
  58 + }
  59 +
  60 + @Test
  61 + public void shouldCreateNodeAndIndexItIfMissing()
  62 + {
  63 + // given
  64 + GraphDatabaseService graphdb = mock( GraphDatabaseService.class );
  65 + @SuppressWarnings( "unchecked" )
  66 + Index<Node> index = mock( Index.class );
  67 + Transaction tx = mock( Transaction.class );
  68 + when( graphdb.beginTx() ).thenReturn( tx );
  69 + when( index.getGraphDatabase() ).thenReturn( graphdb );
  70 + @SuppressWarnings( "unchecked" )
  71 + IndexHits<Node> getHits = mock( IndexHits.class );
  72 +
  73 + when( index.get( "key1", "value1" ) ).thenReturn( getHits );
  74 + Node indexedNode = mock( Node.class );
  75 + when( graphdb.createNode() ).thenReturn( indexedNode );
  76 + final AtomicBoolean initializeCalled = new AtomicBoolean( false );
  77 + UniqueFactory.UniqueNodeFactory unique = new UniqueFactory.UniqueNodeFactory( index )
  78 + {
  79 + @Override
  80 + protected void initialize( Node created, Map<String, Object> properties )
  81 + {
  82 + initializeCalled.set( true );
  83 + assertEquals( Collections.singletonMap( "key1", "value1" ), properties );
  84 + }
  85 + };
  86 +
  87 + // when
  88 + Node node = unique.getOrCreate( "key1", "value1" );
  89 +
  90 + // then
  91 + assertSame(node, indexedNode);
  92 + verify( index ).get( "key1", "value1" );
  93 + verify( index ).putIfAbsent( indexedNode, "key1", "value1" );
  94 + verify( graphdb, times( 1 ) ).createNode();
  95 + verify( tx ).success();
  96 + assertTrue( "Node not initialized", initializeCalled.get() );
  97 + }
  98 +
  99 + @Test
  100 + public void shouldNotTouchTransactionsIfAlreadyInIndex()
  101 + {
  102 + GraphDatabaseService graphdb = mock( GraphDatabaseService.class );
  103 + @SuppressWarnings( "unchecked" )
  104 + Index<Node> index = mock( Index.class );
  105 + when( index.getGraphDatabase() ).thenReturn( graphdb );
  106 + @SuppressWarnings( "unchecked" )
  107 + IndexHits<Node> getHits = mock( IndexHits.class );
  108 + when( index.get( "key1", "value1" ) ).thenReturn( getHits );
  109 + Node indexedNode = mock( Node.class );
  110 + when( getHits.getSingle() ).thenReturn( indexedNode );
  111 +
  112 + UniqueFactory.UniqueNodeFactory unique = new UniqueFactory.UniqueNodeFactory( index )
  113 + {
  114 + @Override
  115 + protected void initialize( Node created, Map<String, Object> properties )
  116 + {
  117 + fail( "we did not create the node, so it should not be initialized" );
  118 + }
  119 + };
  120 +
  121 + // when
  122 + Node node = unique.getOrCreate( "key1", "value1" );
  123 +
  124 + // then
  125 + assertSame( node, indexedNode );
  126 + verify( index ).get( "key1", "value1" );
  127 + }
  128 +}

0 comments on commit d2c5ff7

Please sign in to comment.
Something went wrong with that request. Please try again.