diff --git a/community/common/src/main/java/org/neo4j/function/Predicates.java b/community/common/src/main/java/org/neo4j/function/Predicates.java index aa850f02c054e..8eed9d9a7ad7b 100644 --- a/community/common/src/main/java/org/neo4j/function/Predicates.java +++ b/community/common/src/main/java/org/neo4j/function/Predicates.java @@ -271,4 +271,19 @@ public static Predicate in( final Iterable allowed ) } public static IntPredicate ALWAYS_TRUE_INT = v -> true; + + public static IntPredicate any( int[] values ) + { + return v -> + { + for ( int value : values ) + { + if ( v == value ) + { + return true; + } + } + return false; + }; + } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/NodeRelationshipCursor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/NodeRelationshipCursor.java index 889beddf26f40..91495c8d03582 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/NodeRelationshipCursor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/NodeRelationshipCursor.java @@ -20,6 +20,7 @@ package org.neo4j.kernel.impl.api.store; import java.util.function.Consumer; +import java.util.function.IntPredicate; import org.neo4j.collection.primitive.PrimitiveLongIterator; import org.neo4j.io.pagecache.PageCursor; @@ -33,6 +34,8 @@ import org.neo4j.storageengine.api.Direction; import org.neo4j.storageengine.api.txstate.ReadableTransactionState; +import static org.neo4j.function.Predicates.ALWAYS_TRUE_INT; +import static org.neo4j.function.Predicates.any; import static org.neo4j.kernel.impl.store.record.Record.NO_NEXT_RELATIONSHIP; import static org.neo4j.kernel.impl.store.record.Record.NULL_REFERENCE; import static org.neo4j.kernel.impl.store.record.RecordLoad.FORCE; @@ -53,7 +56,7 @@ public class NodeRelationshipCursor extends AbstractIteratorRelationshipCursor private long relationshipId; private long fromNodeId; private Direction direction; - private int[] allowedTypes; + private IntPredicate allowedTypes; private int groupChainIndex; private boolean end; @@ -73,7 +76,7 @@ public NodeRelationshipCursor init( boolean isDense, long firstRelId, long fromN ReadableTransactionState state ) { PrimitiveLongIterator addedNodeRelationships = addedNodeRelationships( fromNodeId, direction, null, state ); - return init( isDense, firstRelId, fromNodeId, direction, null, state, addedNodeRelationships ); + return init( isDense, firstRelId, fromNodeId, direction, ALWAYS_TRUE_INT, state, addedNodeRelationships ); } public NodeRelationshipCursor init( boolean isDense, long firstRelId, long fromNodeId, Direction direction, @@ -81,11 +84,11 @@ public NodeRelationshipCursor init( boolean isDense, long firstRelId, long fromN { PrimitiveLongIterator addedNodeRelationships = addedNodeRelationships( fromNodeId, direction, allowedTypes, state ); - return init( isDense, firstRelId, fromNodeId, direction, allowedTypes, state, addedNodeRelationships ); + return init( isDense, firstRelId, fromNodeId, direction, any( allowedTypes ), state, addedNodeRelationships ); } private NodeRelationshipCursor init( boolean isDense, long firstRelId, long fromNodeId, Direction direction, - int[] allowedTypes, ReadableTransactionState state, PrimitiveLongIterator addedNodeRelationships ) + IntPredicate allowedTypes, ReadableTransactionState state, PrimitiveLongIterator addedNodeRelationships ) { internalInitTxState( state, addedNodeRelationships ); this.isDense = isDense; @@ -111,6 +114,11 @@ private NodeRelationshipCursor init( boolean isDense, long firstRelId, long from private PrimitiveLongIterator addedNodeRelationships( long fromNodeId, Direction direction, int[] allowedTypes, ReadableTransactionState state ) { + if ( state == null ) + { + return null; + } + return allowedTypes == null ? state.getNodeState( fromNodeId ).getAddedRelationships( direction ) : state.getNodeState( fromNodeId ).getAddedRelationships( direction, allowedTypes ); } @@ -129,17 +137,13 @@ protected boolean doFetchNext() // to chase a used record down the line. try { + // Direction check if ( record.inUse() ) { - // direction is checked while reading the group chain, no need to check it here again - if ( !isDense ) + if ( direction != Direction.BOTH ) { - // Direction check switch ( direction ) { - case BOTH: - break; - case INCOMING: { if ( record.getSecondNode() != fromNodeId ) @@ -163,11 +167,12 @@ protected boolean doFetchNext() } } - // Type check, for dense nodes it is checked while traversing the group records - if ( isDense || checkType( record.getType() ) ) + // Type check + if ( !allowedTypes.test( record.getType() ) ) { - return true; + continue; } + return true; } } finally @@ -202,23 +207,6 @@ else if ( record.getSecondNode() == fromNodeId ) return false; } - private boolean checkType( int type ) - { - if ( allowedTypes == null ) - { - return true; - } - - for ( int allowedType : allowedTypes ) - { - if ( type == allowedType ) - { - return true; - } - } - return false; - } - @Override public void close() { @@ -232,7 +220,7 @@ private long nextChainStart() { // We check inUse flag here since we can actually follow pointers in unused records // to guard for and overcome concurrent deletes in the relationship group chain - if ( groupRecord.inUse() && checkType( groupRecord.getType() ) ) + if ( groupRecord.inUse() && allowedTypes.test( groupRecord.getType() ) ) { // Go to the next chain (direction) within this group while ( groupChainIndex < GROUP_CHAINS.length ) diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/SinglePropertyCursor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/SinglePropertyCursor.java index 45d278170e72f..fb5a62bd86619 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/SinglePropertyCursor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/SinglePropertyCursor.java @@ -55,13 +55,13 @@ protected boolean loadNextFromDisk() } // go to disk if the state does not contain the keyId we are looking for - return !state.hasChanges() || state.getAddedProperty( propertyKeyId ) == null; + return state.getAddedProperty( propertyKeyId ) == null; } @Override protected DefinedProperty nextAdded() { - return fetched ? null : (DefinedProperty) state.getAddedProperty( propertyKeyId ); + return !fetched ? (DefinedProperty) state.getAddedProperty( propertyKeyId ) : null; } @Override diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/store/counts/CountsTrackerTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/store/counts/CountsTrackerTest.java index d725005cfb0ed..19285096ba2b7 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/store/counts/CountsTrackerTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/store/counts/CountsTrackerTest.java @@ -27,7 +27,6 @@ import java.time.Clock; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; -import java.util.concurrent.TimeoutException; import java.util.function.Predicate; import org.neo4j.function.IOFunction; @@ -375,31 +374,16 @@ public void shouldNotEndUpInBrokenStateAfterRotationFailure() throws Exception { tx.incrementNodeCount( labelId, 1 ); // now at 2 } - - while ( true ) + clock.forward( Config.empty().get( GraphDatabaseSettings.counts_store_rotation_timeout ) * 2, MILLISECONDS ); + try { - /* - * There is a race between forwarding the clock and the thread calling rotate, in case the call to forward - * the clock happens before the thread calls rotate, then this test hangs. In order to solve this issue, - * a timeout is set on the future and if the timeout is trigger the clock is forwarded again and we try - * again. - */ - clock.forward( Config.empty().get( GraphDatabaseSettings.counts_store_rotation_timeout ) * 2, MILLISECONDS ); - try - { - rotation.get( 100, MILLISECONDS ); - fail( "Should've failed rotation due to timeout" ); - } - catch ( TimeoutException e ) - { - // ignore try again - } - catch ( ExecutionException e ) - { - // good - assertTrue( e.getCause() instanceof RotationTimeoutException ); - break; - } + rotation.get(); + fail( "Should've failed rotation due to timeout" ); + } + catch ( ExecutionException e ) + { + // good + assertTrue( e.getCause() instanceof RotationTimeoutException ); } // THEN