diff --git a/community/kernel-api/src/main/java/org/neo4j/storageengine/api/txstate/ReadableTransactionState.java b/community/kernel-api/src/main/java/org/neo4j/storageengine/api/txstate/ReadableTransactionState.java index 680643245500a..6cac8090a380d 100644 --- a/community/kernel-api/src/main/java/org/neo4j/storageengine/api/txstate/ReadableTransactionState.java +++ b/community/kernel-api/src/main/java/org/neo4j/storageengine/api/txstate/ReadableTransactionState.java @@ -19,6 +19,7 @@ */ package org.neo4j.storageengine.api.txstate; +import com.sun.istack.internal.Nullable; import org.eclipse.collections.api.set.primitive.MutableLongSet; import org.eclipse.collections.impl.UnmodifiableMap; @@ -110,17 +111,20 @@ public interface ReadableTransactionState // INDEX UPDATES /** - * A readonly view of all index updates for the provided schema + * A readonly view of all index updates for the provided schema. Returns {@code null}, if the index + * updates for this schema have not been initialized. */ + @Nullable UnmodifiableMap getIndexUpdates( SchemaDescriptor schema ); /** * A readonly view of all index updates for the provided schema, in sorted order. The returned - * Map is unmodifiable. + * Map is unmodifiable. Returns {@code null}, if the index updates for this schema have not been initialized. * * Ensure sorted index updates for a given index. This is needed for range query support and * ay involve converting the existing hash map first. */ + @Nullable NavigableMap getSortedIndexUpdates( SchemaDescriptor descriptor ); // OTHER diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/TxState.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/TxState.java index f6be8d293f6ef..bb6f3d35c68d5 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/TxState.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/state/TxState.java @@ -19,6 +19,7 @@ */ package org.neo4j.kernel.impl.api.state; +import com.sun.istack.internal.Nullable; import org.eclipse.collections.api.iterator.LongIterator; import org.eclipse.collections.api.map.primitive.MutableLongObjectMap; import org.eclipse.collections.api.map.primitive.MutableObjectLongMap; @@ -704,6 +705,7 @@ public Long indexCreatedForConstraint( ConstraintDescriptor constraint ) } @Override + @Nullable public UnmodifiableMap getIndexUpdates( SchemaDescriptor schema ) { if ( indexUpdates == null ) @@ -720,6 +722,7 @@ public Long indexCreatedForConstraint( ConstraintDescriptor constraint ) } @Override + @Nullable public NavigableMap getSortedIndexUpdates( SchemaDescriptor descriptor ) { if ( indexUpdates == null ) @@ -750,10 +753,10 @@ public void indexDoUpdateEntry( SchemaDescriptor descriptor, long nodeId, ValueTuple propertiesBefore, ValueTuple propertiesAfter ) { NodeStateImpl nodeState = getOrCreateNodeState( nodeId ); - Map updates = getIndexUpdatesByDescriptor( descriptor ); + Map updates = getOrCreateIndexUpdatesByDescriptor( descriptor ); if ( propertiesBefore != null ) { - MutableLongDiffSets before = getIndexUpdatesForSeek( updates, propertiesBefore ); + MutableLongDiffSets before = getOrCreateIndexUpdatesForSeek( updates, propertiesBefore ); //noinspection ConstantConditions before.remove( nodeId ); if ( before.getRemoved().contains( nodeId ) ) @@ -767,7 +770,7 @@ public void indexDoUpdateEntry( SchemaDescriptor descriptor, long nodeId, } if ( propertiesAfter != null ) { - MutableLongDiffSets after = getIndexUpdatesForSeek( updates, propertiesAfter ); + MutableLongDiffSets after = getOrCreateIndexUpdatesForSeek( updates, propertiesAfter ); //noinspection ConstantConditions after.add( nodeId ); if ( after.getAdded().contains( nodeId ) ) @@ -781,18 +784,12 @@ public void indexDoUpdateEntry( SchemaDescriptor descriptor, long nodeId, } } - /** - * This method does some initialization of empty diffsets. Only call it from updating code. - */ - private MutableLongDiffSets getIndexUpdatesForSeek( Map updates, ValueTuple values ) + private MutableLongDiffSets getOrCreateIndexUpdatesForSeek( Map updates, ValueTuple values ) { return updates.computeIfAbsent( values, value -> new MutableLongDiffSetsImpl() ); } - /** - * This method does some initialization of empty diffsets. Only call it from updating code. - */ - private Map getIndexUpdatesByDescriptor( SchemaDescriptor schema ) + private Map getOrCreateIndexUpdatesByDescriptor( SchemaDescriptor schema ) { if ( indexUpdates == null ) { diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/TxStateTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/TxStateTest.java index cca4c99fd5da8..a084923b2c6a3 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/TxStateTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/TxStateTest.java @@ -22,6 +22,8 @@ import org.apache.commons.lang3.mutable.MutableBoolean; import org.eclipse.collections.api.IntIterable; import org.eclipse.collections.api.set.primitive.LongSet; +import org.eclipse.collections.impl.UnmodifiableMap; +import org.eclipse.collections.impl.factory.primitive.LongSets; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -38,7 +40,9 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.NavigableMap; import java.util.Set; +import java.util.TreeMap; import org.neo4j.function.Predicates; import org.neo4j.helpers.collection.Iterators; @@ -53,8 +57,10 @@ import org.neo4j.kernel.impl.util.collection.CollectionsFactory; import org.neo4j.kernel.impl.util.collection.CollectionsFactorySupplier; import org.neo4j.kernel.impl.util.collection.OffHeapCollectionsFactory; +import org.neo4j.kernel.impl.util.diffsets.MutableLongDiffSetsImpl; import org.neo4j.storageengine.api.StorageProperty; import org.neo4j.storageengine.api.schema.IndexDescriptor; +import org.neo4j.storageengine.api.txstate.LongDiffSets; import org.neo4j.storageengine.api.txstate.ReadableDiffSets; import org.neo4j.storageengine.api.txstate.TxStateVisitor; import org.neo4j.test.rule.RandomRule; @@ -69,6 +75,7 @@ import static org.hamcrest.core.IsEqual.equalTo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -233,6 +240,89 @@ public void shouldMapFromRemovedLabelToNodes() //endregion + //region index updates + + @Test + public void shouldComputeIndexUpdatesOnUninitializedTxState() + { + // WHEN + UnmodifiableMap diffSets = state.getIndexUpdates( indexOn_1_1.schema() ); + + // THEN + assertNull( diffSets ); + } + + @Test + public void shouldComputeSortedIndexUpdatesOnUninitializedTxState() + { + // WHEN + NavigableMap diffSets = state.getSortedIndexUpdates( indexOn_1_1.schema() ); + + // THEN + assertNull( diffSets ); + } + + @Test + public void shouldComputeIndexUpdatesOnEmptyTxState() + { + // GIVEN + addNodesToIndex( indexOn_2_1 ).withDefaultStringProperties( 42L ); + + // WHEN + UnmodifiableMap diffSets = state.getIndexUpdates( indexOn_1_1.schema() ); + + // THEN + assertNull( diffSets ); + } + + @Test + public void shouldComputeSortedIndexUpdatesOnEmptyTxState() + { + // GIVEN + addNodesToIndex( indexOn_2_1 ).withDefaultStringProperties( 42L ); + + // WHEN + NavigableMap diffSets = state.getSortedIndexUpdates( indexOn_1_1.schema() ); + + // THEN + assertNull( diffSets ); + } + + @Test + public void shouldComputeIndexUpdatesOnTxStateWithAddedNodes() + { + // GIVEN + addNodesToIndex( indexOn_1_1 ).withDefaultStringProperties( 42L ); + addNodesToIndex( indexOn_1_1 ).withDefaultStringProperties( 43L ); + addNodesToIndex( indexOn_1_1 ).withDefaultStringProperties( 41L ); + + // WHEN + UnmodifiableMap diffSets = state.getIndexUpdates( indexOn_1_1.schema() ); + + // THEN + assertEquals( addedNodes( 42L ), diffSets.get( ValueTuple.of( Values.stringValue( "value42" ) ) ) ); + assertEquals( addedNodes( 43L ), diffSets.get( ValueTuple.of( Values.stringValue( "value43" ) ) ) ); + assertEquals( addedNodes( 41L ), diffSets.get( ValueTuple.of( Values.stringValue( "value41" ) ) ) ); + } + + @Test + public void shouldComputeSortedIndexUpdatesOnTxStateWithAddedNodes() + { + // GIVEN + addNodesToIndex( indexOn_1_1 ).withDefaultStringProperties( 42L ); + addNodesToIndex( indexOn_1_1 ).withDefaultStringProperties( 43L ); + addNodesToIndex( indexOn_1_1 ).withDefaultStringProperties( 41L ); + + // WHEN + NavigableMap diffSets = state.getSortedIndexUpdates( indexOn_1_1.schema() ); + + TreeMap expected = sortedAddedNodesDiffSets( 42, 41, 43 ); + // THEN + assertEquals( expected, diffSets ); + } + + // endregion + //region index rule tests @Test @@ -752,6 +842,22 @@ public void visitDeletedNode( long id ) //endregion + private LongDiffSets addedNodes( long... added ) + { + return new MutableLongDiffSetsImpl( LongSets.mutable.of( added ), LongSets.mutable.empty(), collectionsFactory ); + } + + private TreeMap sortedAddedNodesDiffSets( long... added ) + { + TreeMap map = new TreeMap<>( ValueTuple.COMPARATOR ); + for ( long node : added ) + { + + map.put( ValueTuple.of( Values.stringValue( "value" + node ) ), addedNodes( node ) ); + } + return map; + } + abstract class VisitationOrder extends TxStateVisitor.Adapter { private final Set visitMethods = new HashSet<>(); @@ -820,12 +926,6 @@ final void visitLate() private interface IndexUpdater { void withDefaultStringProperties( long... nodeIds ); - - void withStringProperties( Collection> nodesWithValues ); - - void withNumberProperties( Collection> nodesWithValues ); - - void withBooleanProperties( Collection> nodesWithValues ); } private IndexUpdater addNodesToIndex( final IndexDescriptor descriptor ) @@ -840,25 +940,7 @@ public void withDefaultStringProperties( long... nodeIds ) { entries.add( of( nodeId, "value" + nodeId ) ); } - withStringProperties( entries ); - } - - @Override - public void withStringProperties( Collection> nodesWithValues ) - { - withProperties( nodesWithValues ); - } - - @Override - public void withNumberProperties( Collection> nodesWithValues ) - { - withProperties( nodesWithValues ); - } - - @Override - public void withBooleanProperties( Collection> nodesWithValues ) - { - withProperties( nodesWithValues ); + withProperties( entries ); } private void withProperties( Collection> nodesWithValues )