diff --git a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v2_3/TransactionBoundQueryContext.scala b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v2_3/TransactionBoundQueryContext.scala index 1b18c783f0d51..9c387638e68b8 100644 --- a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v2_3/TransactionBoundQueryContext.scala +++ b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v2_3/TransactionBoundQueryContext.scala @@ -353,7 +353,7 @@ final class TransactionBoundQueryContext(tc: TransactionalContextWrapper, val re JavaConversionSupport.mapToScalaENFXSafe(scan(DefaultIndexReference.general(index.labelId, index.propertyId)))(nodeOps.getByIdIfExists) override def lockingExactUniqueIndexSearch(index: SchemaTypes.IndexDescriptor, value: Any): Option[Node] = { - val nodeId: Long = tc.dataRead.nodeUniqueIndexSeek(DefaultIndexReference.general(index.labelId, index.propertyId), + val nodeId: Long = tc.dataRead.lockingNodeUniqueIndexSeek(DefaultIndexReference.general(index.labelId, index.propertyId), IndexQuery.exact(index.propertyId, Values.of(value))) if (StatementConstants.NO_SUCH_NODE == nodeId) None else Some(nodeOps.getById(nodeId)) } diff --git a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_1/TransactionBoundQueryContext.scala b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_1/TransactionBoundQueryContext.scala index 445ce83c807c8..0d633ca53ecc9 100644 --- a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_1/TransactionBoundQueryContext.scala +++ b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_1/TransactionBoundQueryContext.scala @@ -360,7 +360,7 @@ final class TransactionBoundQueryContext(txContext: TransactionalContextWrapper, override def lockingUniqueIndexSeek(index: IndexDescriptor, value: Any): Option[Node] = { indexSearchMonitor.lockingUniqueIndexSeek(index, value) - val nodeId = reads().nodeUniqueIndexSeek(DefaultIndexReference.general(index.labelId, index.propertyId), IndexQuery.exact(index.propertyId, value)) + val nodeId = reads().lockingNodeUniqueIndexSeek(DefaultIndexReference.general(index.labelId, index.propertyId), IndexQuery.exact(index.propertyId, value)) if (StatementConstants.NO_SUCH_NODE == nodeId) None else Some(nodeOps.getById(nodeId)) } diff --git a/community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/TransactionBoundQueryContext.scala b/community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/TransactionBoundQueryContext.scala index fc460604f3801..0b76dcab1a80d 100644 --- a/community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/TransactionBoundQueryContext.scala +++ b/community/cypher/interpreted-runtime/src/main/scala/org/neo4j/cypher/internal/runtime/interpreted/TransactionBoundQueryContext.scala @@ -301,7 +301,7 @@ sealed class TransactionBoundQueryContext(val transactionalContext: Transactiona override def lockingUniqueIndexSeek(indexReference: IndexReference, queries: Seq[IndexQuery.ExactPredicate]): Option[NodeValue] = { indexSearchMonitor.lockingUniqueIndexSeek(indexReference, queries) val index = DefaultIndexReference.general(indexReference.label(), indexReference.properties():_*) - val nodeId = reads().nodeUniqueIndexSeek(index, queries:_*) + val nodeId = reads().lockingNodeUniqueIndexSeek(index, queries:_*) if (StatementConstants.NO_SUCH_NODE == nodeId) None else Some(nodeOps.getById(nodeId)) } diff --git a/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/Read.java b/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/Read.java index 85a90bd253db7..561d335f9f08f 100644 --- a/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/Read.java +++ b/community/kernel-api/src/main/java/org/neo4j/internal/kernel/api/Read.java @@ -30,6 +30,8 @@ public interface Read int ANY_RELATIONSHIP_TYPE = -1; /** + * Seek all nodes matching the provided index query in an index. + * * @param index {@link IndexReference} referencing index to query. * @param cursor the cursor to use for consuming the results. * @param indexOrder requested {@link IndexOrder} of result. Must be among the capabilities of @@ -45,13 +47,17 @@ void nodeIndexSeek( IndexReference index, NodeValueIndexCursor cursor, IndexOrde * Note that this is a very special method and should be use with caution. It has special locking semantics in * order to facilitate unique creation of nodes. If a node is found; a shared lock for the index entry will be * held whereas if no node is found we will hold onto an exclusive lock until the close of the transaction. - * @param index {@link IndexReference} referencing index to query. - * {@link IndexReference referenced index}, or {@link IndexOrder#NONE}. + * + * @param index {@link IndexReference} referencing index to query. + * {@link IndexReference referenced index}, or {@link IndexOrder#NONE}. * @param predicates Combination of {@link IndexQuery.ExactPredicate index queries} to run against referenced index. */ - long nodeUniqueIndexSeek( IndexReference index, IndexQuery.ExactPredicate... predicates ) + long lockingNodeUniqueIndexSeek( IndexReference index, IndexQuery.ExactPredicate... predicates ) throws KernelException; + /** + * Scan all values in an index. + * * @param index {@link IndexReference} referencing index to query. * @param cursor the cursor to use for consuming the results. * @param indexOrder requested {@link IndexOrder} of result. Must be among the capabilities of @@ -75,6 +81,11 @@ long nodeUniqueIndexSeek( IndexReference index, IndexQuery.ExactPredicate... pre Scan nodeLabelScan( int label ); + /** + * Return all nodes in the graph. + * + * @param cursor Cursor to initialize for scanning. + */ void allNodesScan( NodeCursor cursor ); Scan allNodesScan(); diff --git a/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/helpers/StubRead.java b/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/helpers/StubRead.java index b231f9c15ff85..688c8b3bfef26 100644 --- a/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/helpers/StubRead.java +++ b/community/kernel-api/src/test/java/org/neo4j/internal/kernel/api/helpers/StubRead.java @@ -43,7 +43,7 @@ public void nodeIndexSeek( IndexReference index, NodeValueIndexCursor cursor, In } @Override - public long nodeUniqueIndexSeek( IndexReference index, + public long lockingNodeUniqueIndexSeek( IndexReference index, IndexQuery.ExactPredicate... predicates ) throws KernelException { throw new UnsupportedOperationException(); diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/AllStoreHolder.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/AllStoreHolder.java index 69f7780cc9c45..5dfe2d2927049 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/AllStoreHolder.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/AllStoreHolder.java @@ -246,12 +246,13 @@ long graphPropertiesReference() } @Override - IndexReader indexReader( IndexReference index ) throws IndexNotFoundKernelException + IndexReader indexReader( IndexReference index, boolean fresh ) throws IndexNotFoundKernelException { SchemaIndexDescriptor schemaIndexDescriptor = index.isUnique() ? SchemaIndexDescriptorFactory.uniqueForLabel( index.label(), index.properties() ) : SchemaIndexDescriptorFactory.forLabel( index.label(), index.properties() ); - return statement.getIndexReader( schemaIndexDescriptor ); + return fresh ? statement.getFreshIndexReader( schemaIndexDescriptor ) : + statement.getIndexReader( schemaIndexDescriptor ); } @Override diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultNodeValueIndexCursor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultNodeValueIndexCursor.java index 2d52ef06cafdb..5c6cccc174e33 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultNodeValueIndexCursor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultNodeValueIndexCursor.java @@ -24,6 +24,7 @@ import org.neo4j.collection.primitive.PrimitiveLongCollections; import org.neo4j.collection.primitive.PrimitiveLongIterator; import org.neo4j.collection.primitive.PrimitiveLongSet; +import org.neo4j.graphdb.Resource; import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.internal.kernel.api.NodeCursor; import org.neo4j.internal.kernel.api.NodeValueIndexCursor; @@ -47,6 +48,7 @@ final class DefaultNodeValueIndexCursor extends IndexCursor implements NodeValueIndexCursor, NodeValueClient { private Read read; + private Resource resource; private long node; private IndexQuery[] query; private Value[] values; @@ -142,9 +144,10 @@ public boolean next() } } - public void setRead( Read read ) + public void setRead( Read read, Resource resource ) { this.read = read; + this.resource = resource; } @Override @@ -196,7 +199,18 @@ public void close() this.added = emptyIterator(); this.removed = PrimitiveLongCollections.emptySet(); - pool.accept( this ); + try + { + if ( resource != null ) + { + resource.close(); + resource = null; + } + } + finally + { + pool.accept( this ); + } } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Operations.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Operations.java index 2f76576a243f4..c662e97b3ec6b 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Operations.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Operations.java @@ -32,7 +32,6 @@ import org.neo4j.internal.kernel.api.CursorFactory; import org.neo4j.internal.kernel.api.ExplicitIndexRead; import org.neo4j.internal.kernel.api.ExplicitIndexWrite; -import org.neo4j.internal.kernel.api.IndexOrder; import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.internal.kernel.api.IndexReference; import org.neo4j.internal.kernel.api.Locks; @@ -394,8 +393,8 @@ private void validateNoExistingNodeWithExactValues( IndexBackedConstraintDescrip indexEntryResourceId( labelId, propertyValues ) ); - allStoreHolder.nodeIndexSeek( allStoreHolder.indexGetCapability( schemaIndexDescriptor ), valueCursor, - IndexOrder.NONE, propertyValues ); + allStoreHolder.nodeIndexSeekWithFreshIndexReader( + allStoreHolder.indexGetCapability( schemaIndexDescriptor ), valueCursor, propertyValues ); if ( valueCursor.next() && valueCursor.nodeReference() != modifiedNode ) { throw new UniquePropertyValueValidationException( constraint, VALIDATION, diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Read.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Read.java index b7369d31b54f9..5cde928dd17bb 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Read.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Read.java @@ -109,9 +109,17 @@ public final void nodeIndexSeek( return; } - ((DefaultNodeValueIndexCursor) cursor).setRead( this ); - IndexProgressor.NodeValueClient target = (DefaultNodeValueIndexCursor) cursor; - IndexReader reader = indexReader( index ); + DefaultNodeValueIndexCursor cursorImpl = (DefaultNodeValueIndexCursor) cursor; + IndexReader reader = indexReader( index, false ); + cursorImpl.setRead( this, null ); + IndexProgressor.NodeValueClient target = withFullValuePrecision( cursorImpl, query, reader ); + reader.query( target, indexOrder, query ); + } + + private IndexProgressor.NodeValueClient withFullValuePrecision( DefaultNodeValueIndexCursor cursor, + IndexQuery[] query, IndexReader reader ) + { + IndexProgressor.NodeValueClient target = cursor; if ( !reader.hasFullValuePrecision( query ) ) { IndexQuery[] filters = new IndexQuery[query.length]; @@ -148,10 +156,11 @@ public final void nodeIndexSeek( cursors.allocatePropertyCursor(), this, filters ); } } - reader.query( target, indexOrder, query ); + return target; } - public final long nodeUniqueIndexSeek( + @Override + public final long lockingNodeUniqueIndexSeek( IndexReference index, IndexQuery.ExactPredicate... predicates ) throws IndexNotApplicableKernelException, IndexNotFoundKernelException, IndexBrokenKernelException @@ -167,14 +176,14 @@ public final long nodeUniqueIndexSeek( //First try to find node under a shared lock //if not found upgrade to exclusive and try again locks.acquireShared( lockTracer, INDEX_ENTRY, indexEntryId ); - try ( NodeValueIndexCursor cursor = cursors.allocateNodeValueIndexCursor() ) + try ( DefaultNodeValueIndexCursor cursor = cursors.allocateNodeValueIndexCursor() ) { - nodeIndexSeek( index, cursor, IndexOrder.NONE, predicates ); + nodeIndexSeekWithFreshIndexReader( index, cursor, predicates ); if ( !cursor.next() ) { locks.releaseShared( INDEX_ENTRY, indexEntryId ); locks.acquireExclusive( lockTracer, INDEX_ENTRY, indexEntryId ); - nodeIndexSeek( index, cursor, IndexOrder.NONE, predicates ); + nodeIndexSeekWithFreshIndexReader( index, cursor, predicates ); if ( cursor.next() ) // we found it under the exclusive lock { // downgrade to a shared lock @@ -187,6 +196,17 @@ public final long nodeUniqueIndexSeek( } } + void nodeIndexSeekWithFreshIndexReader( + IndexReference index, + DefaultNodeValueIndexCursor cursor, + IndexQuery.ExactPredicate... query ) throws IndexNotFoundKernelException, IndexNotApplicableKernelException + { + IndexReader reader = indexReader( index, true ); + cursor.setRead( this, reader ); + IndexProgressor.NodeValueClient target = withFullValuePrecision( cursor, query, reader ); + reader.query( target, IndexOrder.NONE, query ); + } + @Override public final void nodeIndexScan( IndexReference index, @@ -202,8 +222,8 @@ public final void nodeIndexScan( // for a scan, we simply query for existence of the first property, which covers all entries in an index int firstProperty = index.properties()[0]; - ((DefaultNodeValueIndexCursor) cursor).setRead( this ); - indexReader( index ).query( (DefaultNodeValueIndexCursor) cursor, indexOrder, IndexQuery.exists( firstProperty ) ); + ((DefaultNodeValueIndexCursor) cursor).setRead( this, null ); + indexReader( index, false ).query( (DefaultNodeValueIndexCursor) cursor, indexOrder, IndexQuery.exists( firstProperty ) ); } private boolean hasForbiddenProperties( IndexReference index ) @@ -541,7 +561,7 @@ public final void futureRelationshipPropertyReferenceRead( long reference ) ktx.assertOpen(); } - abstract IndexReader indexReader( IndexReference index ) throws IndexNotFoundKernelException; + abstract IndexReader indexReader( IndexReference index, boolean fresh ) throws IndexNotFoundKernelException; abstract LabelScanReader labelScanReader(); diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/integrationtest/NodeGetUniqueFromIndexSeekIT.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/integrationtest/NodeGetUniqueFromIndexSeekIT.java index a7a1e1bea9f55..a0cc025fa85de 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/integrationtest/NodeGetUniqueFromIndexSeekIT.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/integrationtest/NodeGetUniqueFromIndexSeekIT.java @@ -83,7 +83,7 @@ public void shouldFindMatchingNode() throws Exception // when looking for it Read read = newTransaction().dataRead(); int propertyId = index.properties()[0]; - long foundId = read.nodeUniqueIndexSeek( index, exact( propertyId, value ) ); + long foundId = read.lockingNodeUniqueIndexSeek( index, exact( propertyId, value ) ); commit(); // then @@ -100,7 +100,7 @@ public void shouldNotFindNonMatchingNode() throws Exception // when looking for it Transaction transaction = newTransaction(); - long foundId = transaction.dataRead().nodeUniqueIndexSeek( index, exact( propertyId1, value ) ); + long foundId = transaction.dataRead().lockingNodeUniqueIndexSeek( index, exact( propertyId1, value ) ); commit(); // then @@ -118,7 +118,7 @@ public void shouldCompositeFindMatchingNode() throws Exception // when looking for it Transaction transaction = newTransaction(); - long foundId = transaction.dataRead().nodeUniqueIndexSeek( index, + long foundId = transaction.dataRead().lockingNodeUniqueIndexSeek( index, exact( propertyId1, value1 ), exact( propertyId2, value2 ) ); commit(); @@ -138,7 +138,7 @@ public void shouldNotCompositeFindNonMatchingNode() throws Exception // when looking for it Transaction transaction = newTransaction(); - long foundId = transaction.dataRead().nodeUniqueIndexSeek( index, + long foundId = transaction.dataRead().lockingNodeUniqueIndexSeek( index, exact( propertyId1, value1 ), exact( propertyId2, value2 ) ); commit(); @@ -182,7 +182,7 @@ public void shouldBlockUniqueIndexSeekFromCompetingTransaction() throws Exceptio latch.waitForAllToStart(); try ( Transaction tx = session.beginTransaction() ) { - tx.dataRead().nodeUniqueIndexSeek( index, exact( propertyId1, value ) ); + tx.dataRead().lockingNodeUniqueIndexSeek( index, exact( propertyId1, value ) ); tx.success(); } catch ( KernelException e ) diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/integrationtest/UniquenessConstraintValidationIT.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/integrationtest/UniquenessConstraintValidationIT.java index ebb58538d1bfd..b93a2ee074f37 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/integrationtest/UniquenessConstraintValidationIT.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/integrationtest/UniquenessConstraintValidationIT.java @@ -308,7 +308,7 @@ public void unrelatedNodesWithSamePropertyShouldNotInterfereWithUniquenessCheck( createLabeledNode( transaction, "Item", "id", 2 ); // then I should find the original node - assertThat( transaction.dataRead().nodeUniqueIndexSeek( idx, exact( propId, Values.of( 1 ) ) ), + assertThat( transaction.dataRead().lockingNodeUniqueIndexSeek( idx, exact( propId, Values.of( 1 ) ) ), equalTo( ourNode ) ); commit(); } @@ -336,7 +336,7 @@ public void addingUniqueNodeWithUnrelatedValueShouldNotAffectLookup() throws Exc createLabeledNode( transaction, "Person", "id", 2 ); // then I should find the original node - assertThat( transaction.dataRead().nodeUniqueIndexSeek( idx, exact( propId, Values.of( 1 ) ) ), + assertThat( transaction.dataRead().lockingNodeUniqueIndexSeek( idx, exact( propId, Values.of( 1 ) ) ), equalTo( ourNode ) ); commit(); } diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/newapi/MockStore.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/newapi/MockStore.java index 2d83d9cc2bcd9..7d4c77dfb6f53 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/newapi/MockStore.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/newapi/MockStore.java @@ -101,7 +101,7 @@ long graphPropertiesReference() } @Override - IndexReader indexReader( org.neo4j.internal.kernel.api.IndexReference index ) + IndexReader indexReader( IndexReference index, boolean fresh ) { throw new UnsupportedOperationException( "not implemented" ); }