From 26c903ca8fecff88c53b3a061040c49ed7da4e94 Mon Sep 17 00:00:00 2001 From: Davide Grohmann Date: Mon, 27 Jul 2015 17:34:39 +0200 Subject: [PATCH] Load entity data on write after having acquired an exclusive lock The problem was that multiple threads modifying the same entity could corrupt the data. As an example consider the following case with 2 threads: - both threads load the same node record from disk (having no properties) - first thread grabs the lock first and add a property "a" with value 42 - second thread waiting on the lock - first thread release the lock - second thread add a property "a" with vale 33 to the same node since the second thread thinks there are no properties on the node it adds a new property instead modifying the one already there - there are duplicated properties in the property chain Similar problems happened for writing properties on relationships and adding same labels on nodes. --- .../ConstraintEnforcingEntityOperations.java | 97 +++-- .../impl/api/GuardingStatementOperations.java | 60 +-- .../impl/api/LockingStatementOperations.java | 94 ++--- .../kernel/impl/api/OperationsFacade.java | 133 ++----- .../api/StateHandlingStatementOperations.java | 352 ++++++++++-------- .../api/operations/EntityReadOperations.java | 5 + .../api/operations/EntityWriteOperations.java | 26 +- .../impl/transaction/state/RecordChanges.java | 18 + .../impl/util/collection/ArrayCollection.java | 8 + .../test/java/org/neo4j/kernel/TestGuard.java | 2 +- .../api/LockingStatementOperationsTest.java | 41 +- .../state/IndexQueryTransactionStateTest.java | 117 ++++-- .../api/state/LabelTransactionStateTest.java | 187 +++------- .../StateHandlingStatementOperationsTest.java | 25 +- .../kernel/impl/core/TestRelationship.java | 7 +- .../java/ConcurrentChangesOnEntitiesTest.java | 250 +++++++++++++ 16 files changed, 812 insertions(+), 610 deletions(-) create mode 100644 community/neo4j/src/test/java/ConcurrentChangesOnEntitiesTest.java diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/ConstraintEnforcingEntityOperations.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/ConstraintEnforcingEntityOperations.java index 447685cff4ce4..9f2a5755c6aaa 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/ConstraintEnforcingEntityOperations.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/ConstraintEnforcingEntityOperations.java @@ -36,7 +36,6 @@ import org.neo4j.kernel.api.constraints.UniquenessConstraint; import org.neo4j.kernel.api.cursor.LabelItem; import org.neo4j.kernel.api.cursor.NodeItem; -import org.neo4j.kernel.api.cursor.PropertyItem; import org.neo4j.kernel.api.cursor.RelationshipItem; import org.neo4j.kernel.api.exceptions.EntityNotFoundException; import org.neo4j.kernel.api.exceptions.index.IndexNotFoundKernelException; @@ -90,8 +89,8 @@ public ConstraintEnforcingEntityOperations( } @Override - public boolean nodeAddLabel( KernelStatement state, NodeItem node, int labelId ) - throws ConstraintValidationKernelException + public boolean nodeAddLabel( KernelStatement state, long nodeId, int labelId ) + throws ConstraintValidationKernelException, EntityNotFoundException { Iterator allConstraints = schemaReadOperations.constraintsGetForLabel( state, labelId ); Iterator constraints = uniquePropertyConstraints( allConstraints ); @@ -99,39 +98,50 @@ public boolean nodeAddLabel( KernelStatement state, NodeItem node, int labelId ) { PropertyConstraint constraint = constraints.next(); int propertyKeyId = constraint.propertyKey(); - Object propertyValue = node.getProperty( propertyKeyId ); - if ( propertyValue != null ) + try ( Cursor cursor = nodeCursorById( state, nodeId ) ) { - validateNoExistingNodeWithLabelAndProperty( state, labelId, propertyKeyId, propertyValue, node.id() ); + NodeItem node = cursor.get(); + Object propertyValue = node.getProperty( propertyKeyId ); + if ( propertyValue != null ) + { + validateNoExistingNodeWithLabelAndProperty( state, labelId, propertyKeyId, propertyValue, node.id() ); + } } } - return entityWriteOperations.nodeAddLabel( state, node, labelId ); + return entityWriteOperations.nodeAddLabel( state, nodeId, labelId ); } @Override - public Property nodeSetProperty( KernelStatement state, NodeItem node, DefinedProperty property ) - throws ConstraintValidationKernelException + public Property nodeSetProperty( KernelStatement state, long nodeId, DefinedProperty property ) + throws ConstraintValidationKernelException, EntityNotFoundException { - try ( Cursor labels = node.labels() ) + try ( Cursor cursor = nodeCursorById( state, nodeId ) ) { - while ( labels.next() ) + + NodeItem node = cursor.get(); + + try ( Cursor labels = node.labels() ) { - int labelId = labels.get().getAsInt(); - int propertyKeyId = property.propertyKeyId(); - Iterator constraintIterator = - uniquePropertyConstraints( - schemaReadOperations.constraintsGetForLabelAndPropertyKey( state, labelId, - propertyKeyId ) ); - if ( constraintIterator.hasNext() ) + while ( labels.next() ) { - validateNoExistingNodeWithLabelAndProperty( state, labelId, property.propertyKeyId(), - property.value(), node.id() ); + int labelId = labels.get().getAsInt(); + int propertyKeyId = property.propertyKeyId(); + Iterator constraintIterator = + uniquePropertyConstraints( + schemaReadOperations.constraintsGetForLabelAndPropertyKey( state, labelId, + propertyKeyId ) ); + if ( constraintIterator.hasNext() ) + { + validateNoExistingNodeWithLabelAndProperty( + state, labelId, property.propertyKeyId(), property.value(), node.id() ); + } } } + } - return entityWriteOperations.nodeSetProperty( state, node, property ); + return entityWriteOperations.nodeSetProperty( state, nodeId, property ); } private void validateNoExistingNodeWithLabelAndProperty( KernelStatement state, int labelId, @@ -177,39 +187,39 @@ private Iterator uniquePropertyConstraints( Iterator void relationshipVisit( KernelStatement sta entityReadOperations.relationshipVisit( statement, relId, visitor ); } + @Override + public Cursor nodeCursorById( KernelStatement statement, long nodeId ) throws EntityNotFoundException + { + return entityReadOperations.nodeCursorById( statement, nodeId ); + } + @Override public Cursor nodeCursor( KernelStatement statement, long nodeId ) { @@ -385,6 +402,12 @@ public Cursor nodeCursor( TxStateHolder txStateHolder, StoreStatement return entityReadOperations.nodeCursor( txStateHolder, statement, nodeId ); } + @Override + public Cursor relationshipCursorById( KernelStatement statement, long relId ) throws EntityNotFoundException + { + return entityReadOperations.relationshipCursorById( statement, relId ); + } + @Override public Cursor relationshipCursor( KernelStatement statement, long relId ) { diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/GuardingStatementOperations.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/GuardingStatementOperations.java index 6392cf2422a3e..6afee0d0999b1 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/GuardingStatementOperations.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/GuardingStatementOperations.java @@ -59,8 +59,8 @@ public GuardingStatementOperations( @Override public long relationshipCreate( KernelStatement statement, int relationshipTypeId, - NodeItem startNodeId, - NodeItem endNodeId ) + long startNodeId, + long endNodeId ) throws EntityNotFoundException { guard.check(); @@ -75,49 +75,49 @@ public long nodeCreate( KernelStatement statement ) } @Override - public void nodeDelete( KernelStatement state, NodeItem node ) + public void nodeDelete( KernelStatement state, long nodeId ) throws EntityNotFoundException { guard.check(); - entityWriteDelegate.nodeDelete( state, node ); + entityWriteDelegate.nodeDelete( state, nodeId ); } @Override - public void relationshipDelete( KernelStatement state, RelationshipItem relationship ) + public void relationshipDelete( KernelStatement state, long relationshipId ) throws EntityNotFoundException { guard.check(); - entityWriteDelegate.relationshipDelete( state, relationship ); + entityWriteDelegate.relationshipDelete( state, relationshipId ); } @Override - public boolean nodeAddLabel( KernelStatement state, NodeItem node, int labelId ) - throws ConstraintValidationKernelException + public boolean nodeAddLabel( KernelStatement state, long nodeId, int labelId ) + throws ConstraintValidationKernelException, EntityNotFoundException { guard.check(); - return entityWriteDelegate.nodeAddLabel( state, node, labelId ); + return entityWriteDelegate.nodeAddLabel( state, nodeId, labelId ); } @Override - public boolean nodeRemoveLabel( KernelStatement state, NodeItem node, int labelId ) + public boolean nodeRemoveLabel( KernelStatement state, long nodeId, int labelId ) throws EntityNotFoundException { guard.check(); - return entityWriteDelegate.nodeRemoveLabel( state, node, labelId ); + return entityWriteDelegate.nodeRemoveLabel( state, nodeId, labelId ); } @Override - public Property nodeSetProperty( KernelStatement state, NodeItem node, DefinedProperty property ) - throws ConstraintValidationKernelException + public Property nodeSetProperty( KernelStatement state, long nodeId, DefinedProperty property ) + throws ConstraintValidationKernelException, EntityNotFoundException { guard.check(); - return entityWriteDelegate.nodeSetProperty( state, node, property ); + return entityWriteDelegate.nodeSetProperty( state, nodeId, property ); } @Override public Property relationshipSetProperty( KernelStatement state, - RelationshipItem relationship, - DefinedProperty property ) + long relationshipId, + DefinedProperty property ) throws EntityNotFoundException { guard.check(); - return entityWriteDelegate.relationshipSetProperty( state, relationship, property ); + return entityWriteDelegate.relationshipSetProperty( state, relationshipId, property ); } @Override @@ -128,19 +128,20 @@ public Property graphSetProperty( KernelStatement state, DefinedProperty propert } @Override - public Property nodeRemoveProperty( KernelStatement state, NodeItem node, int propertyKeyId ) + public Property nodeRemoveProperty( KernelStatement state, long nodeId, int propertyKeyId ) + throws EntityNotFoundException { guard.check(); - return entityWriteDelegate.nodeRemoveProperty( state, node, propertyKeyId ); + return entityWriteDelegate.nodeRemoveProperty( state, nodeId, propertyKeyId ); } @Override public Property relationshipRemoveProperty( KernelStatement state, - RelationshipItem relationship, - int propertyKeyId ) + long relationshipId, + int propertyKeyId ) throws EntityNotFoundException { guard.check(); - return entityWriteDelegate.relationshipRemoveProperty( state, relationship, propertyKeyId ); + return entityWriteDelegate.relationshipRemoveProperty( state, relationshipId, propertyKeyId ); } @Override @@ -259,6 +260,13 @@ public void relationshipVisit( KernelStatement sta entityReadDelegate.relationshipVisit( statement, relId, visitor ); } + @Override + public Cursor nodeCursorById( KernelStatement statement, long nodeId ) throws EntityNotFoundException + { + guard.check(); + return entityReadDelegate.nodeCursorById( statement, nodeId ); + } + @Override public Cursor nodeCursor( KernelStatement statement, long nodeId ) { @@ -273,6 +281,14 @@ public Cursor nodeCursor( TxStateHolder txStateHolder, StoreStatement return entityReadDelegate.nodeCursor( txStateHolder, statement, nodeId ); } + @Override + public Cursor relationshipCursorById( KernelStatement statement, long relId ) + throws EntityNotFoundException + { + guard.check(); + return entityReadDelegate.relationshipCursorById( statement, relId ); + } + @Override public Cursor relationshipCursor( KernelStatement statement, long relId ) { diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/LockingStatementOperations.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/LockingStatementOperations.java index 466b05aa4bdc4..a766745888907 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/LockingStatementOperations.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/LockingStatementOperations.java @@ -29,8 +29,6 @@ import org.neo4j.kernel.api.constraints.PropertyConstraint; import org.neo4j.kernel.api.constraints.RelationshipPropertyConstraint; import org.neo4j.kernel.api.constraints.UniquenessConstraint; -import org.neo4j.kernel.api.cursor.NodeItem; -import org.neo4j.kernel.api.cursor.RelationshipItem; import org.neo4j.kernel.api.exceptions.EntityNotFoundException; import org.neo4j.kernel.api.exceptions.index.IndexNotFoundKernelException; import org.neo4j.kernel.api.exceptions.schema.AlreadyConstrainedException; @@ -84,8 +82,8 @@ public LockingStatementOperations( } @Override - public boolean nodeAddLabel( KernelStatement state, NodeItem node, int labelId ) - throws ConstraintValidationKernelException + public boolean nodeAddLabel( KernelStatement state, long nodeId, int labelId ) + throws ConstraintValidationKernelException, EntityNotFoundException { // TODO (BBC, 22/11/13): // In order to enforce constraints we need to check whether this change violates constraints; we therefore need @@ -100,18 +98,18 @@ public boolean nodeAddLabel( KernelStatement state, NodeItem node, int labelId ) // by ConstraintEnforcingEntityOperations included the full cake, with locking included. state.locks().acquireShared( ResourceTypes.SCHEMA, schemaResource() ); - state.locks().acquireExclusive( ResourceTypes.NODE, node.id() ); + state.locks().acquireExclusive( ResourceTypes.NODE, nodeId ); state.assertOpen(); - return entityWriteDelegate.nodeAddLabel( state, node, labelId ); + return entityWriteDelegate.nodeAddLabel( state, nodeId, labelId ); } @Override - public boolean nodeRemoveLabel( KernelStatement state, NodeItem node, int labelId ) + public boolean nodeRemoveLabel( KernelStatement state, long nodeId, int labelId ) throws EntityNotFoundException { - state.locks().acquireExclusive( ResourceTypes.NODE, node.id() ); + state.locks().acquireExclusive( ResourceTypes.NODE, nodeId ); state.assertOpen(); - return entityWriteDelegate.nodeRemoveLabel( state, node, labelId ); + return entityWriteDelegate.nodeRemoveLabel( state, nodeId, labelId ); } @Override @@ -248,11 +246,11 @@ public Iterator uniqueIndexesGetAll( KernelStatement state ) } @Override - public void nodeDelete( KernelStatement state, NodeItem node ) + public void nodeDelete( KernelStatement state, long nodeId ) throws EntityNotFoundException { - state.locks().acquireExclusive( ResourceTypes.NODE, node.id() ); + state.locks().acquireExclusive( ResourceTypes.NODE, nodeId ); state.assertOpen(); - entityWriteDelegate.nodeDelete( state, node ); + entityWriteDelegate.nodeDelete( state, nodeId ); } @Override @@ -264,16 +262,13 @@ public long nodeCreate( KernelStatement statement ) @Override public long relationshipCreate( KernelStatement state, int relationshipTypeId, - NodeItem startNode, - NodeItem endNode ) + long startNodeId, + long endNodeId ) throws EntityNotFoundException { state.locks().acquireShared( ResourceTypes.SCHEMA, schemaResource() ); // Order the locks to lower the risk of deadlocks with other threads adding rels concurrently - long startNodeId = startNode.id(); - long endNodeId = endNode.id(); - if ( startNodeId < endNodeId ) { state.locks().acquireExclusive( ResourceTypes.NODE, startNodeId ); @@ -285,33 +280,25 @@ public long relationshipCreate( KernelStatement state, state.locks().acquireExclusive( ResourceTypes.NODE, startNodeId ); } state.assertOpen(); - return entityWriteDelegate.relationshipCreate( state, relationshipTypeId, startNode, endNode ); + return entityWriteDelegate.relationshipCreate( state, relationshipTypeId, startNodeId, endNodeId ); } @Override - public void relationshipDelete( final KernelStatement state, RelationshipItem relationship ) + public void relationshipDelete( final KernelStatement state, long relationshipId ) throws EntityNotFoundException { - try - { - entityReadDelegate.relationshipVisit( state, relationship.id(), - new RelationshipVisitor() + entityReadDelegate.relationshipVisit( state, relationshipId, + new RelationshipVisitor() + { + @Override + public void visit( long relId, int type, long startNode, long endNode ) { - @Override - public void visit( long relId, int type, long startNode, long endNode ) - { - state.locks().acquireExclusive( ResourceTypes.NODE, startNode ); - state.locks().acquireExclusive( ResourceTypes.NODE, endNode ); - } - } ); - } - catch ( EntityNotFoundException e ) - { - throw new IllegalStateException( - "Unable to delete relationship[" + relationship.id() + "] since it is already deleted." ); - } - state.locks().acquireExclusive( ResourceTypes.RELATIONSHIP, relationship.id() ); + state.locks().acquireExclusive( ResourceTypes.NODE, startNode ); + state.locks().acquireExclusive( ResourceTypes.NODE, endNode ); + } + } ); + state.locks().acquireExclusive( ResourceTypes.RELATIONSHIP, relationshipId ); state.assertOpen(); - entityWriteDelegate.relationshipDelete( state, relationship ); + entityWriteDelegate.relationshipDelete( state, relationshipId ); } @Override @@ -405,8 +392,8 @@ public void constraintDrop( KernelStatement state, RelationshipPropertyConstrain } @Override - public Property nodeSetProperty( KernelStatement state, NodeItem node, DefinedProperty property ) - throws ConstraintValidationKernelException + public Property nodeSetProperty( KernelStatement state, long nodeId, DefinedProperty property ) + throws ConstraintValidationKernelException, EntityNotFoundException { // TODO (BBC, 22/11/13): // In order to enforce constraints we need to check whether this change violates constraints; we therefore need @@ -421,37 +408,38 @@ public Property nodeSetProperty( KernelStatement state, NodeItem node, DefinedPr // by ConstraintEnforcingEntityOperations included the full cake, with locking included. state.locks().acquireShared( ResourceTypes.SCHEMA, schemaResource() ); - state.locks().acquireExclusive( ResourceTypes.NODE, node.id() ); + state.locks().acquireExclusive( ResourceTypes.NODE, nodeId ); state.assertOpen(); - return entityWriteDelegate.nodeSetProperty( state, node, property ); + return entityWriteDelegate.nodeSetProperty( state, nodeId, property ); } @Override - public Property nodeRemoveProperty( KernelStatement state, NodeItem node, int propertyKeyId ) + public Property nodeRemoveProperty( KernelStatement state, long nodeId, int propertyKeyId ) + throws EntityNotFoundException { - state.locks().acquireExclusive( ResourceTypes.NODE, node.id() ); + state.locks().acquireExclusive( ResourceTypes.NODE, nodeId ); state.assertOpen(); - return entityWriteDelegate.nodeRemoveProperty( state, node, propertyKeyId ); + return entityWriteDelegate.nodeRemoveProperty( state, nodeId, propertyKeyId ); } @Override public Property relationshipSetProperty( KernelStatement state, - RelationshipItem relationship, - DefinedProperty property ) + long relationshipId, + DefinedProperty property ) throws EntityNotFoundException { - state.locks().acquireExclusive( ResourceTypes.RELATIONSHIP, relationship.id() ); + state.locks().acquireExclusive( ResourceTypes.RELATIONSHIP, relationshipId ); state.assertOpen(); - return entityWriteDelegate.relationshipSetProperty( state, relationship, property ); + return entityWriteDelegate.relationshipSetProperty( state, relationshipId, property ); } @Override public Property relationshipRemoveProperty( KernelStatement state, - RelationshipItem relationship, - int propertyKeyId ) + long relationshipId, + int propertyKeyId ) throws EntityNotFoundException { - state.locks().acquireExclusive( ResourceTypes.RELATIONSHIP, relationship.id() ); + state.locks().acquireExclusive( ResourceTypes.RELATIONSHIP, relationshipId ); state.assertOpen(); - return entityWriteDelegate.relationshipRemoveProperty( state, relationship, propertyKeyId ); + return entityWriteDelegate.relationshipRemoveProperty( state, relationshipId, propertyKeyId ); } @Override diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/OperationsFacade.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/OperationsFacade.java index 9673d7a6b5852..417a02fbe0637 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/OperationsFacade.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/OperationsFacade.java @@ -29,7 +29,6 @@ import org.neo4j.function.Function; import org.neo4j.graphdb.Direction; import org.neo4j.kernel.api.DataWriteOperations; -import org.neo4j.kernel.api.EntityType; import org.neo4j.kernel.api.LegacyIndexHits; import org.neo4j.kernel.api.ReadOperations; import org.neo4j.kernel.api.SchemaWriteOperations; @@ -259,7 +258,7 @@ public boolean nodeHasLabel( long nodeId, int labelId ) throws EntityNotFoundExc return false; } - try ( Cursor node = getNodeCursor( nodeId ) ) + try ( Cursor node = dataRead().nodeCursorById( statement, nodeId ) ) { return node.get().hasLabel( labelId ); } @@ -269,7 +268,7 @@ public boolean nodeHasLabel( long nodeId, int labelId ) throws EntityNotFoundExc public PrimitiveIntIterator nodeGetLabels( long nodeId ) throws EntityNotFoundException { statement.assertOpen(); - try ( Cursor node = getNodeCursor( nodeId ) ) + try ( Cursor node = dataRead().nodeCursorById( statement, nodeId ) ) { return node.get().getLabels(); } @@ -283,7 +282,7 @@ public boolean nodeHasProperty( long nodeId, int propertyKeyId ) throws EntityNo { return false; } - try ( Cursor node = getNodeCursor( nodeId ) ) + try ( Cursor node = dataRead().nodeCursorById( statement, nodeId ) ) { return node.get().hasProperty( propertyKeyId ); } @@ -297,7 +296,7 @@ public Object nodeGetProperty( long nodeId, int propertyKeyId ) throws EntityNot { return null; } - try ( Cursor node = getNodeCursor( nodeId ) ) + try ( Cursor node = dataRead().nodeCursorById( statement, nodeId ) ) { return node.get().getProperty( propertyKeyId ); } @@ -308,7 +307,7 @@ public RelationshipIterator nodeGetRelationships( long nodeId, Direction directi throws EntityNotFoundException { statement.assertOpen(); - try ( Cursor node = getNodeCursor( nodeId ) ) + try ( Cursor node = dataRead().nodeCursorById( statement, nodeId ) ) { return node.get().getRelationships( direction, relTypes ); } @@ -319,7 +318,7 @@ public RelationshipIterator nodeGetRelationships( long nodeId, Direction directi throws EntityNotFoundException { statement.assertOpen(); - try ( Cursor node = getNodeCursor( nodeId ) ) + try ( Cursor node = dataRead().nodeCursorById( statement, nodeId ) ) { return node.get().getRelationships( direction ); } @@ -329,7 +328,7 @@ public RelationshipIterator nodeGetRelationships( long nodeId, Direction directi public int nodeGetDegree( long nodeId, Direction direction, int relType ) throws EntityNotFoundException { statement.assertOpen(); - try ( Cursor node = getNodeCursor( nodeId ) ) + try ( Cursor node = dataRead().nodeCursorById( statement, nodeId ) ) { return node.get().degree( direction, relType ); } @@ -339,7 +338,7 @@ public int nodeGetDegree( long nodeId, Direction direction, int relType ) throws public int nodeGetDegree( long nodeId, Direction direction ) throws EntityNotFoundException { statement.assertOpen(); - try ( Cursor node = getNodeCursor( nodeId ) ) + try ( Cursor node = dataRead().nodeCursorById( statement, nodeId ) ) { return node.get().degree( direction ); } @@ -349,7 +348,7 @@ public int nodeGetDegree( long nodeId, Direction direction ) throws EntityNotFou public PrimitiveIntIterator nodeGetRelationshipTypes( long nodeId ) throws EntityNotFoundException { statement.assertOpen(); - try ( Cursor node = getNodeCursor( nodeId ) ) + try ( Cursor node = dataRead().nodeCursorById( statement, nodeId ) ) { return node.get().getRelationshipTypes(); } @@ -363,7 +362,7 @@ public boolean relationshipHasProperty( long relationshipId, int propertyKeyId ) { return false; } - try ( Cursor relationship = getRelationshipCursor( relationshipId ) ) + try ( Cursor relationship = dataRead().relationshipCursorById( statement, relationshipId ) ) { return relationship.get().hasProperty( propertyKeyId ); } @@ -377,7 +376,7 @@ public Object relationshipGetProperty( long relationshipId, int propertyKeyId ) { return null; } - try ( Cursor relationship = getRelationshipCursor( relationshipId ) ) + try ( Cursor relationship = dataRead().relationshipCursorById( statement, relationshipId ) ) { return relationship.get().getProperty( propertyKeyId ); } @@ -409,7 +408,7 @@ public Object graphGetProperty( int propertyKeyId ) public PrimitiveIntIterator nodeGetPropertyKeys( long nodeId ) throws EntityNotFoundException { statement.assertOpen(); - try ( Cursor node = getNodeCursor( nodeId ) ) + try ( Cursor node = dataRead().nodeCursorById( statement, nodeId ) ) { return node.get().getPropertyKeys(); } @@ -419,7 +418,7 @@ public PrimitiveIntIterator nodeGetPropertyKeys( long nodeId ) throws EntityNotF public PrimitiveIntIterator relationshipGetPropertyKeys( long relationshipId ) throws EntityNotFoundException { statement.assertOpen(); - try ( Cursor relationship = getRelationshipCursor( relationshipId ) ) + try ( Cursor relationship = dataRead().relationshipCursorById( statement, relationshipId ) ) { return relationship.get().getPropertyKeys(); } @@ -542,7 +541,7 @@ public IndexDescriptor indexesGetForLabelAndPropertyKey( int labelId, int proper IndexDescriptor descriptor = schemaRead().indexesGetForLabelAndPropertyKey( statement, labelId, propertyKeyId ); if ( descriptor == null ) { - throw new IndexSchemaRuleNotFoundException( labelId, propertyKeyId); + throw new IndexSchemaRuleNotFoundException( labelId, propertyKeyId ); } return descriptor; } @@ -586,7 +585,7 @@ public IndexDescriptor uniqueIndexGetForLabelAndPropertyKey( int labelId, int pr if ( null == result ) { - throw new IndexSchemaRuleNotFoundException( labelId, propertyKeyId, true); + throw new IndexSchemaRuleNotFoundException( labelId, propertyKeyId, true ); } return result; @@ -791,7 +790,7 @@ public void relationshipTypeCreateForName( String relationshipTypeName, // @Override - public V schemaStateGetOrCreate( K key, Function creator ) + public V schemaStateGetOrCreate( K key, Function creator ) { return schemaState().schemaStateGetOrCreate( statement, key, creator ); } @@ -816,11 +815,7 @@ public long nodeCreate() public void nodeDelete( long nodeId ) throws EntityNotFoundException { statement.assertOpen(); - - try ( Cursor node = getNodeCursor( nodeId ) ) - { - dataWrite().nodeDelete( statement, node.get() ); - } + dataWrite().nodeDelete( statement, nodeId ); } @Override @@ -828,26 +823,14 @@ public long relationshipCreate( int relationshipTypeId, long startNodeId, long e throws RelationshipTypeIdNotFoundKernelException, EntityNotFoundException { statement.assertOpen(); - - try ( Cursor startNode = getNodeCursor( startNodeId ) ) - { - try ( Cursor endNode = getNodeCursor( endNodeId ) ) - { - return dataWrite().relationshipCreate( statement, relationshipTypeId, startNode.get(), endNode.get() ); - } - } - + return dataWrite().relationshipCreate( statement, relationshipTypeId, startNodeId, endNodeId ); } @Override public void relationshipDelete( long relationshipId ) throws EntityNotFoundException { statement.assertOpen(); - - try ( Cursor relationship = getRelationshipCursor( relationshipId ) ) - { - dataWrite().relationshipDelete( statement, relationship.get() ); - } + dataWrite().relationshipDelete( statement, relationshipId ); } @Override @@ -855,22 +838,14 @@ public boolean nodeAddLabel( long nodeId, int labelId ) throws EntityNotFoundException, ConstraintValidationKernelException { statement.assertOpen(); - - try ( Cursor node = getNodeCursor( nodeId ) ) - { - return dataWrite().nodeAddLabel( statement, node.get(), labelId ); - } + return dataWrite().nodeAddLabel( statement, nodeId, labelId ); } @Override public boolean nodeRemoveLabel( long nodeId, int labelId ) throws EntityNotFoundException { statement.assertOpen(); - - try ( Cursor node = getNodeCursor( nodeId ) ) - { - return dataWrite().nodeRemoveLabel( statement, node.get(), labelId ); - } + return dataWrite().nodeRemoveLabel( statement, nodeId, labelId ); } @Override @@ -878,11 +853,7 @@ public Property nodeSetProperty( long nodeId, DefinedProperty property ) throws EntityNotFoundException, ConstraintValidationKernelException { statement.assertOpen(); - - try ( Cursor node = getNodeCursor( nodeId ) ) - { - return dataWrite().nodeSetProperty( statement, node.get(), property ); - } + return dataWrite().nodeSetProperty( statement, nodeId, property ); } @Override @@ -890,11 +861,7 @@ public Property relationshipSetProperty( long relationshipId, DefinedProperty pr throws EntityNotFoundException { statement.assertOpen(); - - try ( Cursor relationship = getRelationshipCursor( relationshipId ) ) - { - return dataWrite().relationshipSetProperty( statement, relationship.get(), property ); - } + return dataWrite().relationshipSetProperty( statement, relationshipId, property ); } @Override @@ -908,22 +875,14 @@ public Property graphSetProperty( DefinedProperty property ) public Property nodeRemoveProperty( long nodeId, int propertyKeyId ) throws EntityNotFoundException { statement.assertOpen(); - - try ( Cursor node = getNodeCursor( nodeId ) ) - { - return dataWrite().nodeRemoveProperty( statement, node.get(), propertyKeyId ); - } + return dataWrite().nodeRemoveProperty( statement, nodeId, propertyKeyId ); } @Override public Property relationshipRemoveProperty( long relationshipId, int propertyKeyId ) throws EntityNotFoundException { statement.assertOpen(); - - try ( Cursor relationship = getRelationshipCursor( relationshipId ) ) - { - return dataWrite().relationshipRemoveProperty( statement, relationship.get(), propertyKeyId ); - } + return dataWrite().relationshipRemoveProperty( statement, relationshipId, propertyKeyId ); } @Override @@ -1080,14 +1039,14 @@ public LegacyIndexHits relationshipLegacyIndexQuery( String indexName, Object qu } @Override - public void nodeLegacyIndexCreateLazily( String indexName, Map customConfig ) + public void nodeLegacyIndexCreateLazily( String indexName, Map customConfig ) { statement.assertOpen(); legacyIndexWrite().nodeLegacyIndexCreateLazily( statement, indexName, customConfig ); } @Override - public void nodeLegacyIndexCreate( String indexName, Map customConfig ) + public void nodeLegacyIndexCreate( String indexName, Map customConfig ) { statement.assertOpen(); @@ -1095,14 +1054,14 @@ public void nodeLegacyIndexCreate( String indexName, Map customC } @Override - public void relationshipLegacyIndexCreateLazily( String indexName, Map customConfig ) + public void relationshipLegacyIndexCreateLazily( String indexName, Map customConfig ) { statement.assertOpen(); legacyIndexWrite().relationshipLegacyIndexCreateLazily( statement, indexName, customConfig ); } @Override - public void relationshipLegacyIndexCreate( String indexName, Map customConfig ) + public void relationshipLegacyIndexCreate( String indexName, Map customConfig ) { statement.assertOpen(); @@ -1187,7 +1146,7 @@ public void relationshipLegacyIndexDrop( String indexName ) throws LegacyIndexNo } @Override - public Map nodeLegacyIndexGetConfiguration( String indexName ) + public Map nodeLegacyIndexGetConfiguration( String indexName ) throws LegacyIndexNotFoundKernelException { statement.assertOpen(); @@ -1195,7 +1154,7 @@ public Map nodeLegacyIndexGetConfiguration( String indexName ) } @Override - public Map relationshipLegacyIndexGetConfiguration( String indexName ) + public Map relationshipLegacyIndexGetConfiguration( String indexName ) throws LegacyIndexNotFoundKernelException { statement.assertOpen(); @@ -1266,34 +1225,4 @@ public long countsForRelationship( int startLabelId, int typeId, int endLabelId } // - - private Cursor getNodeCursor( long nodeId ) throws EntityNotFoundException - { - Cursor node = dataRead().nodeCursor( statement, nodeId ); - - if ( !node.next() ) - { - node.close(); - throw new EntityNotFoundException( EntityType.NODE, nodeId ); - } - else - { - return node; - } - } - - private Cursor getRelationshipCursor( long relationshipId ) throws EntityNotFoundException - { - Cursor relationship = dataRead().relationshipCursor( statement, relationshipId ); - - if ( !relationship.next() ) - { - relationship.close(); - throw new EntityNotFoundException( EntityType.RELATIONSHIP, relationshipId ); - } - else - { - return relationship; - } - } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/StateHandlingStatementOperations.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/StateHandlingStatementOperations.java index 0694db66946f3..1dc210fa4bcff 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/StateHandlingStatementOperations.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/StateHandlingStatementOperations.java @@ -39,7 +39,6 @@ import org.neo4j.kernel.api.constraints.PropertyConstraint; import org.neo4j.kernel.api.constraints.RelationshipPropertyConstraint; import org.neo4j.kernel.api.constraints.UniquenessConstraint; -import org.neo4j.kernel.api.cursor.EntityItem; import org.neo4j.kernel.api.cursor.LabelItem; import org.neo4j.kernel.api.cursor.NodeItem; import org.neo4j.kernel.api.cursor.PropertyItem; @@ -120,6 +119,22 @@ public StateHandlingStatementOperations( } // + + @Override + public Cursor nodeCursorById( KernelStatement statement, long nodeId ) throws EntityNotFoundException + { + Cursor node = nodeCursor( statement, nodeId ); + if ( !node.next() ) + { + node.close(); + throw new EntityNotFoundException( EntityType.NODE, nodeId ); + } + else + { + return node; + } + } + @Override public Cursor nodeCursor( KernelStatement statement, long nodeId ) { @@ -142,6 +157,22 @@ public Cursor nodeCursor( TxStateHolder txStateHolder, StoreStatement return cursor; } + @Override + public Cursor relationshipCursorById( KernelStatement statement, long relationshipId ) + throws EntityNotFoundException + { + Cursor relationship = relationshipCursor( statement, relationshipId ); + if ( !relationship.next() ) + { + relationship.close(); + throw new EntityNotFoundException( EntityType.RELATIONSHIP, relationshipId ); + } + else + { + return relationship; + } + } + @Override public Cursor relationshipCursor( KernelStatement statement, long relationshipId ) { @@ -286,40 +317,55 @@ public long nodeCreate( KernelStatement state ) } @Override - public void nodeDelete( KernelStatement state, NodeItem node ) + public void nodeDelete( KernelStatement state, long nodeId ) throws EntityNotFoundException { - legacyPropertyTrackers.nodeDelete( node.id() ); - state.txState().nodeDoDelete( node.id() ); + legacyPropertyTrackers.nodeDelete( nodeId ); + try ( Cursor cursor = nodeCursorById( state, nodeId ) ) + { + state.txState().nodeDoDelete( cursor.get().id() ); + } } @Override public long relationshipCreate( KernelStatement state, int relationshipTypeId, - NodeItem startNode, - NodeItem endNode ) + long startNodeId, + long endNodeId ) throws EntityNotFoundException { - long id = storeLayer.reserveRelationship(); - state.txState().relationshipDoCreate( id, relationshipTypeId, startNode.id(), endNode.id() ); - return id; + try ( Cursor startNode = nodeCursorById( state, startNodeId ) ) + { + try ( Cursor endNode = nodeCursorById( state, endNodeId ) ) + { + long id = storeLayer.reserveRelationship(); + state.txState().relationshipDoCreate( id, relationshipTypeId, startNode.get().id(), endNode.get().id() ); + return id; + } + } } @Override - public void relationshipDelete( final KernelStatement state, RelationshipItem relationship ) + public void relationshipDelete( final KernelStatement state, long relationshipId ) throws EntityNotFoundException { - // NOTE: We implicitly delegate to neoStoreTransaction via txState.legacyState here. This is because that - // call returns modified properties, which node manager uses to update legacy tx state. This will be cleaned up - // once we've removed legacy tx state. - legacyPropertyTrackers.relationshipDelete( relationship.id() ); - final TransactionState txState = state.txState(); - if ( txState.relationshipIsAddedInThisTx( relationship.id() ) ) + try ( Cursor cursor = relationshipCursorById( state, relationshipId ) ) { - txState.relationshipDoDeleteAddedInThisTx( relationship.id() ); - } - else - { - txState.relationshipDoDelete( relationship.id(), relationship.type(), relationship.startNode(), - relationship.endNode() ); + RelationshipItem relationship = cursor.get(); + + // NOTE: We implicitly delegate to neoStoreTransaction via txState.legacyState here. This is because that + // call returns modified properties, which node manager uses to update legacy tx state. This will be cleaned up + + // once we've removed legacy tx state. + legacyPropertyTrackers.relationshipDelete( relationship.id() ); + final TransactionState txState = state.txState(); + if ( txState.relationshipIsAddedInThisTx( relationship.id() ) ) + { + txState.relationshipDoDeleteAddedInThisTx( relationship.id() ); + } + else + { + txState.relationshipDoDelete( relationship.id(), relationship.type(), relationship.startNode(), + relationship.endNode() ); + } } } @@ -337,64 +383,72 @@ public RelationshipIterator relationshipsGetAll( KernelStatement state ) } @Override - public boolean nodeAddLabel( KernelStatement state, NodeItem node, int labelId ) + public boolean nodeAddLabel( KernelStatement state, long nodeId, int labelId ) throws EntityNotFoundException { - try ( Cursor labels = node.label( labelId ) ) + try ( Cursor cursor = nodeCursorById( state, nodeId ) ) { - if ( labels.next() ) + NodeItem node = cursor.get(); + try ( Cursor labels = node.label( labelId ) ) { - // Label is already in state or in store, no-op - return false; + if ( labels.next() ) + { + // Label is already in state or in store, no-op + return false; + } } - } - state.txState().nodeDoAddLabel( labelId, node.id() ); + state.txState().nodeDoAddLabel( labelId, node.id() ); - try ( Cursor properties = node.properties() ) - { - while ( properties.next() ) + try ( Cursor properties = node.properties() ) { - PropertyItem propertyItem = properties.get(); - IndexDescriptor descriptor = indexesGetForLabelAndPropertyKey( state, labelId, - propertyItem.propertyKeyId() ); - if ( descriptor != null ) + while ( properties.next() ) { - DefinedProperty after = Property.property( propertyItem.propertyKeyId(), - propertyItem.value() ); + PropertyItem propertyItem = properties.get(); + IndexDescriptor descriptor = indexesGetForLabelAndPropertyKey( state, labelId, + propertyItem.propertyKeyId() ); + if ( descriptor != null ) + { + DefinedProperty after = Property.property( propertyItem.propertyKeyId(), + propertyItem.value() ); - state.txState().indexDoUpdateProperty( descriptor, node.id(), null, after ); + state.txState().indexDoUpdateProperty( descriptor, node.id(), null, after ); + } } - } - return true; + return true; + } } } @Override - public boolean nodeRemoveLabel( KernelStatement state, NodeItem node, int labelId ) + public boolean nodeRemoveLabel( KernelStatement state, long nodeId, int labelId ) throws EntityNotFoundException { - try ( Cursor labels = node.label( labelId ) ) + try ( Cursor cursor = nodeCursorById( state, nodeId ) ) { - if ( !labels.next() ) + NodeItem node = cursor.get(); + try ( Cursor labels = node.label( labelId ) ) { - // Label does not exist in state or in store, no-op - return false; + if ( !labels.next() ) + { + // Label does not exist in state or in store, no-op + return false; + } } - } - state.txState().nodeDoRemoveLabel( labelId, node.id() ); + state.txState().nodeDoRemoveLabel( labelId, node.id() ); - try ( Cursor properties = node.properties() ) - { - while ( properties.next() ) + try ( Cursor properties = node.properties() ) { - PropertyItem propItem = properties.get(); - DefinedProperty property = Property.property( propItem.propertyKeyId(), propItem.value() ); - indexUpdateProperty( state, node.id(), labelId, property.propertyKeyId(), property, null ); + while ( properties.next() ) + { + PropertyItem propItem = properties.get(); + DefinedProperty property = Property.property( propItem.propertyKeyId(), propItem.value() ); + indexUpdateProperty( state, node.id(), labelId, property.propertyKeyId(), property, null ); + } } - } - return true; + return true; + } } @Override @@ -817,59 +871,68 @@ private PrimitiveLongIterator filterIndexStateChangesForRangeSeekByPrefix( Kerne } @Override - public Property nodeSetProperty( KernelStatement state, NodeItem node, DefinedProperty property ) + public Property nodeSetProperty( KernelStatement state, long nodeId, DefinedProperty property ) + throws EntityNotFoundException { - Property existingProperty; - try ( Cursor properties = node.property( property.propertyKeyId() ) ) + try ( Cursor cursor = nodeCursorById( state, nodeId ) ) { - if ( !properties.next() ) + NodeItem node = cursor.get(); + Property existingProperty; + try ( Cursor properties = node.property( property.propertyKeyId() ) ) { - legacyPropertyTrackers.nodeAddStoreProperty( node.id(), property ); - existingProperty = Property.noProperty( property.propertyKeyId(), EntityType.NODE, node.id() ); - } - else - { - existingProperty = Property.property( properties.get().propertyKeyId(), properties.get().value() ); - legacyPropertyTrackers.nodeChangeStoreProperty( node.id(), (DefinedProperty) existingProperty, - property ); + if ( !properties.next() ) + { + legacyPropertyTrackers.nodeAddStoreProperty( node.id(), property ); + existingProperty = Property.noProperty( property.propertyKeyId(), EntityType.NODE, node.id() ); + } + else + { + existingProperty = Property.property( properties.get().propertyKeyId(), properties.get().value() ); + legacyPropertyTrackers.nodeChangeStoreProperty( node.id(), (DefinedProperty) existingProperty, + property ); + } } - } - state.txState().nodeDoReplaceProperty( node.id(), existingProperty, property ); + state.txState().nodeDoReplaceProperty( node.id(), existingProperty, property ); - PrimitiveIntCollection labelIds = getLabels( node ); + PrimitiveIntCollection labelIds = getLabels( node ); - indexesUpdateProperty( state, node.id(), labelIds, property.propertyKeyId(), - existingProperty instanceof DefinedProperty ? (DefinedProperty) existingProperty : null, - property ); + indexesUpdateProperty( state, node.id(), labelIds, property.propertyKeyId(), + existingProperty instanceof DefinedProperty ? (DefinedProperty) existingProperty : null, + property ); - return existingProperty; + return existingProperty; + } } @Override public Property relationshipSetProperty( KernelStatement state, - RelationshipItem relationship, - DefinedProperty property ) + long relationshipId, + DefinedProperty property ) throws EntityNotFoundException { - Property existingProperty; - try ( Cursor properties = relationship.property( property.propertyKeyId() ) ) + try ( Cursor cursor = relationshipCursorById( state, relationshipId ) ) { - if ( !properties.next() ) + RelationshipItem relationship = cursor.get(); + Property existingProperty; + try ( Cursor properties = relationship.property( property.propertyKeyId() ) ) { - legacyPropertyTrackers.relationshipAddStoreProperty( relationship.id(), property ); - existingProperty = Property.noProperty( property.propertyKeyId(), EntityType.RELATIONSHIP, - relationship.id() ); - } - else - { - existingProperty = Property.property( properties.get().propertyKeyId(), properties.get().value() ); - legacyPropertyTrackers.relationshipChangeStoreProperty( relationship.id(), - (DefinedProperty) existingProperty, property ); + if ( !properties.next() ) + { + legacyPropertyTrackers.relationshipAddStoreProperty( relationship.id(), property ); + existingProperty = Property.noProperty( property.propertyKeyId(), EntityType.RELATIONSHIP, + relationship.id() ); + } + else + { + existingProperty = Property.property( properties.get().propertyKeyId(), properties.get().value() ); + legacyPropertyTrackers.relationshipChangeStoreProperty( relationship.id(), + (DefinedProperty) existingProperty, property ); + } } - } - state.txState().relationshipDoReplaceProperty( relationship.id(), existingProperty, property ); - return existingProperty; + state.txState().relationshipDoReplaceProperty( relationship.id(), existingProperty, property ); + return existingProperty; + } } @Override @@ -884,54 +947,62 @@ public Property graphSetProperty( KernelStatement state, DefinedProperty propert } @Override - public Property nodeRemoveProperty( KernelStatement state, NodeItem node, int propertyKeyId ) + public Property nodeRemoveProperty( KernelStatement state, long nodeId, int propertyKeyId ) + throws EntityNotFoundException { - PrimitiveIntCollection labelIds = getLabels( node ); - - Property existingProperty; - try ( Cursor properties = node.property( propertyKeyId ) ) + try ( Cursor cursor = nodeCursorById( state, nodeId ) ) { - if ( !properties.next() ) + NodeItem node = cursor.get(); + PrimitiveIntCollection labelIds = getLabels( node ); + Property existingProperty; + try ( Cursor properties = node.property( propertyKeyId ) ) { - existingProperty = Property.noProperty( propertyKeyId, EntityType.NODE, node.id() ); - } - else - { - existingProperty = Property.property( properties.get().propertyKeyId(), properties.get().value() ); + if ( !properties.next() ) + { + existingProperty = Property.noProperty( propertyKeyId, EntityType.NODE, node.id() ); + } + else + { + existingProperty = Property.property( properties.get().propertyKeyId(), properties.get().value() ); - legacyPropertyTrackers.nodeRemoveStoreProperty( node.id(), (DefinedProperty) existingProperty ); - state.txState().nodeDoRemoveProperty( node.id(), (DefinedProperty) existingProperty ); + legacyPropertyTrackers.nodeRemoveStoreProperty( node.id(), (DefinedProperty) existingProperty ); + state.txState().nodeDoRemoveProperty( node.id(), (DefinedProperty) existingProperty ); - indexesUpdateProperty( state, node.id(), labelIds, propertyKeyId, - (DefinedProperty) existingProperty, null ); + indexesUpdateProperty( state, node.id(), labelIds, propertyKeyId, + (DefinedProperty) existingProperty, null ); + } } + return existingProperty; } - return existingProperty; } @Override public Property relationshipRemoveProperty( KernelStatement state, - RelationshipItem relationship, - int propertyKeyId ) + long relationshipId, + int propertyKeyId ) throws EntityNotFoundException { - Property existingProperty; - try ( Cursor properties = relationship.property( propertyKeyId ) ) + try ( Cursor cursor = relationshipCursorById( state, relationshipId ) ) { - if ( !properties.next() ) + RelationshipItem relationship = cursor.get(); + Property existingProperty; + try ( Cursor properties = relationship.property( propertyKeyId ) ) { - existingProperty = Property.noProperty( propertyKeyId, EntityType.RELATIONSHIP, relationship.id() ); - } - else - { - existingProperty = Property.property( properties.get().propertyKeyId(), properties.get().value() ); + if ( !properties.next() ) + { + existingProperty = Property.noProperty( propertyKeyId, EntityType.RELATIONSHIP, relationship.id() ); + } + else + { + existingProperty = Property.property( properties.get().propertyKeyId(), properties.get().value() ); - legacyPropertyTrackers.relationshipRemoveStoreProperty( relationship.id(), - (DefinedProperty) existingProperty ); - state.txState().relationshipDoRemoveProperty( relationship.id(), - (DefinedProperty) existingProperty ); + legacyPropertyTrackers.relationshipRemoveStoreProperty( relationship.id(), + (DefinedProperty) existingProperty ); + state.txState().relationshipDoRemoveProperty( relationship.id(), + (DefinedProperty) existingProperty ); + } } + return existingProperty; } - return existingProperty; } @Override @@ -1486,41 +1557,6 @@ public String[] relationshipLegacyIndexesGetAll( KernelStatement statement ) } // - private PrimitiveIntIterator getPropertyKeys( EntityItem entity ) - { - PrimitiveIntStack keys = new PrimitiveIntStack(); - try ( Cursor properties = entity.properties() ) - { - while ( properties.next() ) - { - keys.push( properties.get().propertyKeyId() ); - } - } - - return keys.iterator(); - } - - private boolean hasProperty( EntityItem entity, int propertyKeyId ) - { - try ( Cursor cursor = entity.property( propertyKeyId ) ) - { - return cursor.next(); - } - } - - private Object getProperty( EntityItem entity, int propertyKeyId ) - { - try ( Cursor cursor = entity.property( propertyKeyId ) ) - { - if ( cursor.next() ) - { - return cursor.get().value(); - } - } - - return null; - } - private PrimitiveIntCollection getLabels( NodeItem node ) { PrimitiveIntStack labelIds = new PrimitiveIntStack(); diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/operations/EntityReadOperations.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/operations/EntityReadOperations.java index 6aff37bb8b01a..f8044633c2d0c 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/operations/EntityReadOperations.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/operations/EntityReadOperations.java @@ -118,10 +118,15 @@ long nodeGetFromUniqueIndexSeek( KernelStatement state, IndexDescriptor index, O void relationshipVisit( KernelStatement statement, long relId, RelationshipVisitor visitor ) throws EntityNotFoundException, EXCEPTION; + Cursor nodeCursorById( KernelStatement statement, long nodeId ) throws EntityNotFoundException; + Cursor nodeCursor( KernelStatement statement, long nodeId ); Cursor nodeCursor( TxStateHolder txStateHolder, StoreStatement statement, long nodeId ); + Cursor relationshipCursorById( KernelStatement statement, long relId ) + throws EntityNotFoundException; + Cursor relationshipCursor( KernelStatement statement, long relId ); Cursor relationshipCursor( TxStateHolder txStateHolder, StoreStatement statement, long relId ); diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/operations/EntityWriteOperations.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/operations/EntityWriteOperations.java index b06fa5efba46a..fccb6be8dad86 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/operations/EntityWriteOperations.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/operations/EntityWriteOperations.java @@ -19,8 +19,6 @@ */ package org.neo4j.kernel.impl.api.operations; -import org.neo4j.kernel.api.cursor.NodeItem; -import org.neo4j.kernel.api.cursor.RelationshipItem; import org.neo4j.kernel.api.exceptions.EntityNotFoundException; import org.neo4j.kernel.api.exceptions.schema.ConstraintValidationKernelException; import org.neo4j.kernel.api.properties.DefinedProperty; @@ -33,14 +31,14 @@ public interface EntityWriteOperations long relationshipCreate( KernelStatement statement, int relationshipTypeId, - NodeItem startNodeId, - NodeItem endNodeId ) throws EntityNotFoundException; + long startNodeId, + long endNodeId ) throws EntityNotFoundException; long nodeCreate( KernelStatement statement ); - void nodeDelete( KernelStatement state, NodeItem node ); + void nodeDelete( KernelStatement state, long nodeId ) throws EntityNotFoundException; - void relationshipDelete( KernelStatement state, RelationshipItem relationship ); + void relationshipDelete( KernelStatement state, long relationshipId ) throws EntityNotFoundException; /** * Labels a node with the label corresponding to the given label id. @@ -49,8 +47,8 @@ long relationshipCreate( KernelStatement statement, * or {@link * KeyReadOperations#labelGetForName(org.neo4j.kernel.api.Statement, String)}. */ - boolean nodeAddLabel( KernelStatement state, NodeItem node, int labelId ) - throws ConstraintValidationKernelException; + boolean nodeAddLabel( KernelStatement state, long nodeId, int labelId ) + throws ConstraintValidationKernelException, EntityNotFoundException; /** * Removes a label with the corresponding id from a node. @@ -59,12 +57,12 @@ boolean nodeAddLabel( KernelStatement state, NodeItem node, int labelId ) * or {@link * KeyReadOperations#labelGetForName(org.neo4j.kernel.api.Statement, String)}. */ - boolean nodeRemoveLabel( KernelStatement state, NodeItem node, int labelId ); + boolean nodeRemoveLabel( KernelStatement state, long nodeId, int labelId ) throws EntityNotFoundException; - Property nodeSetProperty( KernelStatement state, NodeItem node, DefinedProperty property ) - throws ConstraintValidationKernelException; + Property nodeSetProperty( KernelStatement state, long nodeId, DefinedProperty property ) + throws ConstraintValidationKernelException, EntityNotFoundException; - Property relationshipSetProperty( KernelStatement state, RelationshipItem relationship, DefinedProperty property ); + Property relationshipSetProperty( KernelStatement state, long relationshipId, DefinedProperty property ) throws EntityNotFoundException; Property graphSetProperty( KernelStatement state, DefinedProperty property ); @@ -72,9 +70,9 @@ Property nodeSetProperty( KernelStatement state, NodeItem node, DefinedProperty * Remove a node's property given the node's id and the property key id and return the value to which * it was set or null if it was not set on the node */ - Property nodeRemoveProperty( KernelStatement state, NodeItem node, int propertyKeyId ); + Property nodeRemoveProperty( KernelStatement state, long nodeId, int propertyKeyId ) throws EntityNotFoundException; - Property relationshipRemoveProperty( KernelStatement state, RelationshipItem relationship, int propertyKeyId ); + Property relationshipRemoveProperty( KernelStatement state, long relationshipId, int propertyKeyId ) throws EntityNotFoundException; Property graphRemoveProperty( KernelStatement state, int propertyKeyId ); } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/state/RecordChanges.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/state/RecordChanges.java index 5c1f387845e47..5729df7f9793b 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/state/RecordChanges.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/transaction/state/RecordChanges.java @@ -52,6 +52,14 @@ public RecordChanges( Loader loader, boolean manageBefore this.changeCounter = new LocalIntCounter( globalCounter ); } + @Override + public String toString() + { + return "RecordChanges{" + + "recordChanges=" + recordChanges + + '}'; + } + @Override public RecordProxy getIfLoaded( KEY key ) { @@ -157,6 +165,16 @@ public RecordChange(Map> allChanges, I this.additionalData = additionalData; } + @Override + public String toString() + { + return "RecordChange{" + + "record=" + record + + "key=" + key + + "created=" + created + + '}'; + } + @Override public KEY getKey() { diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/util/collection/ArrayCollection.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/util/collection/ArrayCollection.java index bccaf7215da5e..c69860d05aa0a 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/util/collection/ArrayCollection.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/util/collection/ArrayCollection.java @@ -211,4 +211,12 @@ public void clear() } size = 0; } + + @Override + public String toString() + { + return "ArrayCollection{" + + "array=" + Arrays.toString( array ) + + '}'; + } } diff --git a/community/kernel/src/test/java/org/neo4j/kernel/TestGuard.java b/community/kernel/src/test/java/org/neo4j/kernel/TestGuard.java index 37d982dbaee1f..0ab6334974c5b 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/TestGuard.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/TestGuard.java @@ -96,7 +96,7 @@ public void testGuardOnDifferentGraphOps() getGuard( db ).startOperationsCount( MAX_VALUE ); n0.createRelationshipTo( n1, withName( "REL" ) ); Guard.OperationsCount ops3 = getGuard( db ).stop(); - assertEquals( 3, ops3.getOpsCount() ); + assertEquals( 1, ops3.getOpsCount() ); getGuard( db ).startOperationsCount( MAX_VALUE ); for ( Path position : Traversal.description().breadthFirst().relationships( withName( "REL" ) ). diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/LockingStatementOperationsTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/LockingStatementOperationsTest.java index 1a6e04f09fded..6afa525dc05e5 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/LockingStatementOperationsTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/LockingStatementOperationsTest.java @@ -19,17 +19,16 @@ */ package org.neo4j.kernel.impl.api; -import java.util.Collections; -import java.util.Iterator; - import org.junit.Test; import org.mockito.InOrder; +import java.util.Collections; +import java.util.Iterator; + import org.neo4j.function.Function; import org.neo4j.kernel.api.constraints.NodePropertyConstraint; import org.neo4j.kernel.api.constraints.PropertyConstraint; import org.neo4j.kernel.api.constraints.UniquenessConstraint; -import org.neo4j.kernel.api.cursor.NodeItem; import org.neo4j.kernel.api.exceptions.EntityNotFoundException; import org.neo4j.kernel.api.index.IndexDescriptor; import org.neo4j.kernel.api.properties.DefinedProperty; @@ -39,7 +38,6 @@ import org.neo4j.kernel.impl.api.operations.SchemaReadOperations; import org.neo4j.kernel.impl.api.operations.SchemaStateOperations; import org.neo4j.kernel.impl.api.operations.SchemaWriteOperations; -import org.neo4j.kernel.impl.api.state.StubCursors; import org.neo4j.kernel.impl.locking.Locks; import org.neo4j.kernel.impl.locking.ResourceTypes; @@ -47,9 +45,7 @@ import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; - import static org.neo4j.function.Functions.constant; -import static org.neo4j.kernel.impl.api.state.StubCursors.asNode; import static org.neo4j.kernel.impl.locking.ResourceTypes.schemaResource; public class LockingStatementOperationsTest @@ -85,38 +81,34 @@ public LockingStatementOperationsTest() public void shouldAcquireEntityWriteLockCreatingRelationship() throws Exception { // when - NodeItem node1 = StubCursors.asNode( 2 ); - NodeItem node2 = StubCursors.asNode( 3 ); - lockingOps.relationshipCreate( state, 1, node1, node2 ); + lockingOps.relationshipCreate( state, 1, 2, 3 ); // then order.verify( locks ).acquireExclusive( ResourceTypes.NODE, 2 ); order.verify( locks ).acquireExclusive( ResourceTypes.NODE, 3 ); - order.verify( entityWriteOps ).relationshipCreate( state, 1, node1, node2 ); + order.verify( entityWriteOps ).relationshipCreate( state, 1, 2, 3 ); } @Test public void shouldAcquireEntityWriteLockBeforeAddingLabelToNode() throws Exception { // when - NodeItem nodeCursor = asNode( 123 ); - lockingOps.nodeAddLabel( state, nodeCursor, 456 ); + lockingOps.nodeAddLabel( state, 123, 456 ); // then order.verify( locks ).acquireExclusive( ResourceTypes.NODE, 123 ); - order.verify( entityWriteOps ).nodeAddLabel( state, nodeCursor, 456 ); + order.verify( entityWriteOps ).nodeAddLabel( state, 123, 456 ); } @Test public void shouldAcquireSchemaReadLockBeforeAddingLabelToNode() throws Exception { // when - NodeItem nodeCursor = asNode( 123 ); - lockingOps.nodeAddLabel( state, nodeCursor, 456 ); + lockingOps.nodeAddLabel( state, 123, 456 ); // then order.verify( locks ).acquireShared( ResourceTypes.SCHEMA, schemaResource() ); - order.verify( entityWriteOps ).nodeAddLabel( state, nodeCursor, 456 ); + order.verify( entityWriteOps ).nodeAddLabel( state, 123, 456 ); } @Test @@ -126,12 +118,11 @@ public void shouldAcquireEntityWriteLockBeforeSettingPropertyOnNode() throws Exc DefinedProperty property = Property.property( 8, 9 ); // when - NodeItem nodeCursor = asNode( 123 ); - lockingOps.nodeSetProperty( state, nodeCursor, property ); + lockingOps.nodeSetProperty( state, 123, property ); // then order.verify( locks ).acquireExclusive( ResourceTypes.NODE, 123 ); - order.verify( entityWriteOps ).nodeSetProperty( state, nodeCursor, property ); + order.verify( entityWriteOps ).nodeSetProperty( state, 123, property ); } @Test @@ -141,24 +132,22 @@ public void shouldAcquireSchemaReadLockBeforeSettingPropertyOnNode() throws Exce DefinedProperty property = Property.property( 8, 9 ); // when - NodeItem nodeCursor = asNode( 123 ); - lockingOps.nodeSetProperty( state, nodeCursor, property ); + lockingOps.nodeSetProperty( state, 123, property ); // then order.verify( locks ).acquireShared( ResourceTypes.SCHEMA, schemaResource() ); - order.verify( entityWriteOps ).nodeSetProperty( state, nodeCursor, property ); + order.verify( entityWriteOps ).nodeSetProperty( state, 123, property ); } @Test public void shouldAcquireEntityWriteLockBeforeDeletingNode() throws EntityNotFoundException { // WHEN - NodeItem nodeCursor = asNode( 123 ); - lockingOps.nodeDelete( state, nodeCursor ); + lockingOps.nodeDelete( state, 123 ); //THEN order.verify( locks ).acquireExclusive( ResourceTypes.NODE, 123 ); - order.verify( entityWriteOps ).nodeDelete( state, nodeCursor ); + order.verify( entityWriteOps ).nodeDelete( state, 123 ); } @Test diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/IndexQueryTransactionStateTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/IndexQueryTransactionStateTest.java index 3adc119496b46..579f17e1d8ca7 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/IndexQueryTransactionStateTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/IndexQueryTransactionStateTest.java @@ -19,11 +19,11 @@ */ package org.neo4j.kernel.impl.api.state; -import java.util.Collections; - import org.junit.Before; import org.junit.Test; +import java.util.Collections; + import org.neo4j.collection.primitive.PrimitiveLongCollections; import org.neo4j.collection.primitive.PrimitiveLongIterator; import org.neo4j.graphdb.Resource; @@ -46,12 +46,10 @@ import org.neo4j.kernel.impl.util.PrimitiveLongResourceIterator; import static java.util.Arrays.asList; - import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; - import static org.neo4j.graphdb.Neo4jMockitoHelpers.answerAsIteratorFrom; import static org.neo4j.graphdb.Neo4jMockitoHelpers.answerAsPrimitiveLongIteratorFrom; import static org.neo4j.helpers.collection.IteratorUtil.asSet; @@ -60,7 +58,7 @@ import static org.neo4j.kernel.api.properties.Property.noNodeProperty; import static org.neo4j.kernel.api.properties.Property.stringProperty; import static org.neo4j.kernel.impl.api.state.StubCursors.asLabelCursor; -import static org.neo4j.kernel.impl.api.state.StubCursors.asNode; +import static org.neo4j.kernel.impl.api.state.StubCursors.asNodeCursor; import static org.neo4j.kernel.impl.api.state.StubCursors.asPropertyCursor; public class IndexQueryTransactionStateTest @@ -104,14 +102,18 @@ public void before() throws Exception txContext = new ConstraintEnforcingEntityOperations( stateHandlingOperations, stateHandlingOperations, stateHandlingOperations, stateHandlingOperations ); } + @Test public void shouldExcludeRemovedNodesFromIndexQuery() throws Exception { // Given + long nodeId = 2l; when( store.nodesGetFromIndexSeek( state, indexDescriptor, value ) ) - .then( answerAsPrimitiveLongIteratorFrom( asList( 1l, 2l, 3l ) ) ); + .then( answerAsPrimitiveLongIteratorFrom( asList( 1l, nodeId, 3l ) ) ); + + when( statement.acquireSingleNodeCursor( nodeId ) ).thenReturn( asNodeCursor( nodeId ) ); - txContext.nodeDelete( state, asNode( 2l ) ); + txContext.nodeDelete( state, nodeId ); // When PrimitiveLongIterator result = txContext.nodesGetFromIndexSeek( state, indexDescriptor, value ); @@ -124,10 +126,13 @@ public void shouldExcludeRemovedNodesFromIndexQuery() throws Exception public void shouldExcludeRemovedNodeFromUniqueIndexQuery() throws Exception { // Given + long nodeId = 1l; when( store.nodeGetFromUniqueIndexSeek( state, indexDescriptor, value ) ).thenReturn( - asPrimitiveResourceIterator( 1l ) ); + asPrimitiveResourceIterator( nodeId ) ); + + when( statement.acquireSingleNodeCursor( nodeId ) ).thenReturn( asNodeCursor( nodeId ) ); - txContext.nodeDelete( state, asNode( 1l ) ); + txContext.nodeDelete( state, nodeId ); // When long result = txContext.nodeGetFromUniqueIndexSeek( state, indexDescriptor, value ); @@ -176,17 +181,22 @@ public void shouldIncludeCreatedNodesWithCorrectLabelAndProperty() throws Except when( store.nodesGetFromIndexSeek( state, indexDescriptor, value ) ) .then( answerAsPrimitiveLongIteratorFrom( asList( 2l, 3l ) ) ); - state.txState().nodeDoReplaceProperty( 1l, noNodeProperty( 1l, propertyKeyId ), + long nodeId = 1l; + state.txState().nodeDoReplaceProperty( nodeId, noNodeProperty( nodeId, propertyKeyId ), stringProperty( propertyKeyId, value ) ); - txContext.nodeAddLabel( state, asNode( 1l, asPropertyCursor( stringProperty( propertyKeyId, value ) ), - Cursors.empty() ), labelId ); + when( statement.acquireSingleNodeCursor( nodeId ) ).thenReturn( + asNodeCursor( nodeId, + asPropertyCursor( stringProperty( propertyKeyId, value ) ), + Cursors.empty() ) ); + + txContext.nodeAddLabel( state, nodeId, labelId ); // When PrimitiveLongIterator result = txContext.nodesGetFromIndexSeek( state, indexDescriptor, value ); // Then - assertThat( asSet( result ), equalTo( asSet( 1l, 2l, 3l ) ) ); + assertThat( asSet( result ), equalTo( asSet( nodeId, 2l, 3l ) ) ); } @Test @@ -196,17 +206,22 @@ public void shouldIncludeUniqueCreatedNodeWithCorrectLabelAndProperty() throws E when( store.nodeGetFromUniqueIndexSeek( state, indexDescriptor, value ) ).thenReturn( asPrimitiveResourceIterator() ); - state.txState().nodeDoReplaceProperty( 1l, noNodeProperty( 1l, propertyKeyId ), + long nodeId = 1l; + state.txState().nodeDoReplaceProperty( nodeId, noNodeProperty( nodeId, propertyKeyId ), stringProperty( propertyKeyId, value ) ); - txContext.nodeAddLabel( state, asNode( 1l, asPropertyCursor( stringProperty( propertyKeyId, value ) ), - Cursors.empty() ), labelId ); + when( statement.acquireSingleNodeCursor( nodeId ) ).thenReturn( + asNodeCursor( nodeId, + asPropertyCursor( stringProperty( propertyKeyId, value ) ), + Cursors.empty() ) ); + + txContext.nodeAddLabel( state, nodeId, labelId ); // When long result = txContext.nodeGetFromUniqueIndexSeek( state, indexDescriptor, value ); // Then - assertThat( result, equalTo( 1l ) ); + assertThat( result, equalTo( nodeId ) ); } @Test @@ -216,14 +231,20 @@ public void shouldIncludeExistingNodesWithCorrectPropertyAfterAddingLabel() thro when( store.nodesGetFromIndexSeek( state, indexDescriptor, value ) ) .then( answerAsPrimitiveLongIteratorFrom( asList( 2l, 3l ) ) ); - txContext.nodeAddLabel( state, asNode( 1l, asPropertyCursor( stringProperty( propertyKeyId, value ) ), - asLabelCursor() ), labelId ); + long nodeId = 1l; + + when( statement.acquireSingleNodeCursor( nodeId ) ).thenReturn( + asNodeCursor( nodeId, + asPropertyCursor( stringProperty( propertyKeyId, value ) ), + asLabelCursor() ) ); + + txContext.nodeAddLabel( state, nodeId, labelId ); // When PrimitiveLongIterator result = txContext.nodesGetFromIndexSeek( state, indexDescriptor, value ); // Then - assertThat( asSet( result ), equalTo( asSet( 1l, 2l, 3l ) ) ); + assertThat( asSet( result ), equalTo( asSet( nodeId, 2l, 3l ) ) ); } @Test @@ -233,25 +254,36 @@ public void shouldIncludeExistingUniqueNodeWithCorrectPropertyAfterAddingLabel() when( store.nodeGetFromUniqueIndexSeek( state, indexDescriptor, value ) ).thenReturn( asPrimitiveResourceIterator() ); - txContext.nodeAddLabel( state, asNode( 2l, asPropertyCursor( stringProperty( propertyKeyId, value ) ), - asLabelCursor() ), labelId ); + long nodeId = 2l; + + when( statement.acquireSingleNodeCursor( nodeId ) ).thenReturn( + asNodeCursor( nodeId, + asPropertyCursor( stringProperty( propertyKeyId, value ) ), + asLabelCursor() ) ); + + txContext.nodeAddLabel( state, nodeId, labelId ); // When long result = txContext.nodeGetFromUniqueIndexSeek( state, indexDescriptor, value ); // Then - assertThat( result, equalTo( 2l ) ); + assertThat( result, equalTo( nodeId ) ); } @Test public void shouldExcludeExistingNodesWithCorrectPropertyAfterRemovingLabel() throws Exception { // Given + long nodeId = 1l; when( store.nodesGetFromIndexSeek( state, indexDescriptor, value ) ) - .then( answerAsPrimitiveLongIteratorFrom( asList( 1l, 2l, 3l ) ) ); + .then( answerAsPrimitiveLongIteratorFrom( asList( nodeId, 2l, 3l ) ) ); + + when( statement.acquireSingleNodeCursor( nodeId ) ).thenReturn( + asNodeCursor( nodeId, + asPropertyCursor( stringProperty( propertyKeyId, value ) ), + asLabelCursor( labelId ) ) ); - txContext.nodeRemoveLabel( state, asNode( 1l, asPropertyCursor( stringProperty( propertyKeyId, value ) ), - asLabelCursor( labelId ) ), labelId ); + txContext.nodeRemoveLabel( state, nodeId, labelId ); // When PrimitiveLongIterator result = txContext.nodesGetFromIndexSeek( state, indexDescriptor, value ); @@ -264,11 +296,16 @@ public void shouldExcludeExistingNodesWithCorrectPropertyAfterRemovingLabel() th public void shouldExcludeExistingUniqueNodeWithCorrectPropertyAfterRemovingLabel() throws Exception { // Given + long nodeId = 1l; when( store.nodeGetFromUniqueIndexSeek( state, indexDescriptor, value ) ).thenReturn( - asPrimitiveResourceIterator( 1l ) ); + asPrimitiveResourceIterator( nodeId ) ); - txContext.nodeRemoveLabel( state, asNode( 1l, asPropertyCursor( stringProperty( propertyKeyId, value ) ), - asLabelCursor( labelId ) ), labelId ); + when( statement.acquireSingleNodeCursor( nodeId ) ).thenReturn( + asNodeCursor( nodeId, + asPropertyCursor( stringProperty( propertyKeyId, value ) ), + asLabelCursor( labelId ) ) ); + + txContext.nodeRemoveLabel( state, nodeId, labelId ); // When long result = txContext.nodeGetFromUniqueIndexSeek( state, indexDescriptor, value ); @@ -284,10 +321,16 @@ public void shouldExcludeNodesWithRemovedProperty() throws Exception when( store.nodesGetFromIndexSeek( state, indexDescriptor, value ) ) .then( answerAsPrimitiveLongIteratorFrom( asList( 2l, 3l ) ) ); - state.txState().nodeDoReplaceProperty( 1l, Property.noNodeProperty( 1l, propertyKeyId ), + long nodeId = 1l; + state.txState().nodeDoReplaceProperty( nodeId, Property.noNodeProperty( nodeId, propertyKeyId ), Property.intProperty( propertyKeyId, 10 ) ); - txContext.nodeAddLabel( state, asNode( 1l, asPropertyCursor(), asLabelCursor( labelId ) ), labelId ); + when( statement.acquireSingleNodeCursor( nodeId ) ).thenReturn( + asNodeCursor( nodeId, + asPropertyCursor(), + asLabelCursor( labelId ) ) ); + + txContext.nodeAddLabel( state, nodeId, labelId ); // When PrimitiveLongIterator result = txContext.nodesGetFromIndexSeek( state, indexDescriptor, value ); @@ -300,12 +343,16 @@ public void shouldExcludeNodesWithRemovedProperty() throws Exception public void shouldExcludeUniqueNodeWithRemovedProperty() throws Exception { // Given - + long nodeId = 1l; when( store.nodeGetFromUniqueIndexSeek( state, indexDescriptor, value ) ).thenReturn( - asPrimitiveResourceIterator( 1l ) ); + asPrimitiveResourceIterator( nodeId ) ); + + when( statement.acquireSingleNodeCursor( nodeId ) ).thenReturn( + asNodeCursor( nodeId, + asPropertyCursor( stringProperty( propertyKeyId, value ) ), + asLabelCursor( labelId ) ) ); - txContext.nodeRemoveProperty( state, asNode( 1l, asPropertyCursor( stringProperty( propertyKeyId, value ) ), - asLabelCursor( labelId ) ), propertyKeyId ); + txContext.nodeRemoveProperty( state, nodeId, propertyKeyId ); // When long result = txContext.nodeGetFromUniqueIndexSeek( state, indexDescriptor, value ); diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/LabelTransactionStateTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/LabelTransactionStateTest.java index 582830c04b51f..728da9c0b7e22 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/LabelTransactionStateTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/LabelTransactionStateTest.java @@ -19,15 +19,15 @@ */ package org.neo4j.kernel.impl.api.state; +import org.junit.Before; +import org.junit.Test; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import org.junit.Before; -import org.junit.Test; - import org.neo4j.cursor.Cursor; import org.neo4j.kernel.api.cursor.NodeItem; import org.neo4j.kernel.api.exceptions.EntityNotFoundException; @@ -46,12 +46,10 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; - import static org.neo4j.graphdb.Neo4jMockitoHelpers.answerAsIteratorFrom; import static org.neo4j.graphdb.Neo4jMockitoHelpers.answerAsPrimitiveLongIteratorFrom; import static org.neo4j.helpers.collection.IteratorUtil.asSet; import static org.neo4j.kernel.impl.api.state.StubCursors.asLabelCursor; -import static org.neo4j.kernel.impl.api.state.StubCursors.asNode; import static org.neo4j.kernel.impl.api.state.StubCursors.asNodeCursor; import static org.neo4j.kernel.impl.api.state.StubCursors.asPropertyCursor; @@ -83,13 +81,7 @@ public void addOnlyLabelShouldBeVisibleInTx() throws Exception commitNoLabels(); // WHEN - try ( Cursor cursor = txContext.nodeCursor( state, nodeId ) ) - { - if ( cursor.next() ) - { - txContext.nodeAddLabel( state, cursor.get(), labelId1 ); - } - } + txContext.nodeAddLabel( state, nodeId, labelId1 ); // THEN assertLabels( labelId1 ); @@ -102,13 +94,7 @@ public void addAdditionalLabelShouldBeReflectedWithinTx() throws Exception commitLabels( labelId1 ); // WHEN - try ( Cursor cursor = txContext.nodeCursor( state, nodeId ) ) - { - if ( cursor.next() ) - { - txContext.nodeAddLabel( state, cursor.get(), labelId2 ); - } - } + txContext.nodeAddLabel( state, nodeId, labelId2 ); // THEN assertLabels( labelId1, labelId2 ); @@ -121,13 +107,7 @@ public void addAlreadyExistingLabelShouldBeReflectedWithinTx() throws Exception commitLabels( labelId1 ); // WHEN - try ( Cursor cursor = txContext.nodeCursor( state, nodeId ) ) - { - if ( cursor.next() ) - { - txContext.nodeAddLabel( state, cursor.get(), labelId1 ); - } - } + txContext.nodeAddLabel( state, nodeId, labelId1 ); // THEN assertLabels( labelId1 ); @@ -140,13 +120,8 @@ public void removeCommittedLabelShouldBeReflectedWithinTx() throws Exception commitLabels( labelId1, labelId2 ); // WHEN - try ( Cursor cursor = txContext.nodeCursor( state, nodeId ) ) - { - if ( cursor.next() ) - { - txContext.nodeRemoveLabel( state, cursor.get(), labelId1 ); - } - } + txContext.nodeRemoveLabel( state, nodeId, labelId1 ); + // THEN assertLabels( labelId2 ); } @@ -158,21 +133,8 @@ public void removeAddedLabelInTxShouldBeReflectedWithinTx() throws Exception commitLabels( labelId1 ); // WHEN - try ( Cursor cursor = txContext.nodeCursor( state, nodeId ) ) - { - if ( cursor.next() ) - { - txContext.nodeAddLabel( state, cursor.get(), labelId2 ); - } - } - - try ( Cursor cursor = txContext.nodeCursor( state, nodeId ) ) - { - if ( cursor.next() ) - { - txContext.nodeRemoveLabel( state, cursor.get(), labelId2 ); - } - } + txContext.nodeAddLabel( state, nodeId, labelId2 ); + txContext.nodeRemoveLabel( state, nodeId, labelId2 ); // THEN assertLabels( labelId1 ); @@ -185,21 +147,8 @@ public void addRemovedLabelInTxShouldBeReflectedWithinTx() throws Exception commitLabels( labelId1 ); // WHEN - try ( Cursor cursor = txContext.nodeCursor( state, nodeId ) ) - { - if ( cursor.next() ) - { - txContext.nodeRemoveLabel( state, cursor.get(), labelId1 ); - } - } - - try ( Cursor cursor = txContext.nodeCursor( state, nodeId ) ) - { - if ( cursor.next() ) - { - txContext.nodeAddLabel( state, cursor.get(), labelId1 ); - } - } + txContext.nodeRemoveLabel( state, nodeId, labelId1 ); + txContext.nodeAddLabel( state, nodeId, labelId1 ); // THEN assertLabels( labelId1 ); @@ -215,7 +164,7 @@ public void addedLabelsShouldBeReflectedWhenGettingNodesForLabel() throws Except labels( 2, 1, 3 ) ); // WHEN - txContext.nodeAddLabel( state, asNode( 2 ), 2 ); + txContext.nodeAddLabel( state, 2, 2 ); // THEN assertEquals( asSet( 0L, 1L, 2L ), asSet( txContext.nodesGetForLabel( state, 2 ) ) ); @@ -231,13 +180,7 @@ public void removedLabelsShouldBeReflectedWhenGettingNodesForLabel() throws Exce labels( 2, 1, 3 ) ); // WHEN - try ( Cursor cursor = txContext.nodeCursor( state, 1 ) ) - { - if ( cursor.next() ) - { - txContext.nodeRemoveLabel( state, cursor.get(), 2 ); - } - } + txContext.nodeRemoveLabel( state, 1, 2 ); // THEN assertEquals( asSet( 0L ), asSet( txContext.nodesGetForLabel( state, 2 ) ) ); @@ -250,16 +193,10 @@ public void addingNewLabelToNodeShouldRespondTrue() throws Exception commitNoLabels(); // WHEN - try ( Cursor cursor = txContext.nodeCursor( state, nodeId ) ) - { - if ( cursor.next() ) - { - boolean added = txContext.nodeAddLabel( state, cursor.get(), labelId1 ); + boolean added = txContext.nodeAddLabel( state, nodeId, labelId1 ); - // THEN - assertTrue( "Should have been added now", added ); - } - } + // THEN + assertTrue( "Should have been added now", added ); } @Test @@ -269,16 +206,10 @@ public void addingExistingLabelToNodeShouldRespondFalse() throws Exception commitLabels( labelId1 ); // WHEN - try ( Cursor cursor = txContext.nodeCursor( state, nodeId ) ) - { - if ( cursor.next() ) - { - boolean added = txContext.nodeAddLabel( state, cursor.get(), labelId1 ); + boolean added = txContext.nodeAddLabel( state, nodeId, labelId1 ); - // THEN - assertFalse( "Shouldn't have been added now", added ); - } - } + // THEN + assertFalse( "Shouldn't have been added now", added ); } @Test @@ -288,17 +219,10 @@ public void removingExistingLabelFromNodeShouldRespondTrue() throws Exception commitLabels( labelId1 ); // WHEN - try ( Cursor cursor = txContext.nodeCursor( state, nodeId ) ) - { - if ( cursor.next() ) - { - boolean removed = txContext.nodeRemoveLabel( state, cursor.get(), labelId1 ); - - // THEN - assertTrue( "Should have been removed now", removed ); - } + boolean removed = txContext.nodeRemoveLabel( state, nodeId, labelId1 ); - } + // THEN + assertTrue( "Should have been removed now", removed ); } @Test @@ -308,13 +232,7 @@ public void removingNonExistentLabelFromNodeShouldRespondFalse() throws Exceptio commitNoLabels(); // WHEN - try ( Cursor cursor = txContext.nodeCursor( state, nodeId ) ) - { - if ( cursor.next() ) - { - txContext.nodeAddLabel( state, cursor.get(), labelId1 ); - } - } + txContext.nodeAddLabel( state, nodeId, labelId1 ); // THEN assertLabels( labelId1 ); @@ -326,14 +244,11 @@ public void should_return_true_when_adding_new_label() throws Exception // GIVEN when( storeStatement.acquireSingleNodeCursor( 1337 ) ).thenReturn( asNodeCursor( 1337 ) ); - // WHEN and THEN - try ( Cursor cursor = txContext.nodeCursor( state, 1337 ) ) - { - if ( cursor.next() ) - { - assertTrue( "Label should have been added", txContext.nodeAddLabel( state, cursor.get(), 12 ) ); - } - } + // WHEN + boolean added = txContext.nodeAddLabel( state, 1337, 12 ); + + // THEN + assertTrue( "Label should have been added", added ); } @Test @@ -343,14 +258,11 @@ public void should_return_false_when_adding_existing_label() throws Exception when( storeStatement.acquireSingleNodeCursor( 1337 ) ).thenReturn( asNodeCursor( 1337, asPropertyCursor(), asLabelCursor( 12 ) ) ); - // WHEN and THEN - try ( Cursor cursor = txContext.nodeCursor( state, 1337 ) ) - { - if ( cursor.next() ) - { - assertFalse( "Label should have been added", txContext.nodeAddLabel( state, cursor.get(), 12 ) ); - } - } + // WHEN + boolean added = txContext.nodeAddLabel( state, 1337, 12 ); + + // THEN + assertFalse( "Label should have been added", added ); } @Test @@ -360,14 +272,11 @@ public void should_return_true_when_removing_existing_label() throws Exception when( storeStatement.acquireSingleNodeCursor( 1337 ) ).thenReturn( asNodeCursor( 1337, asPropertyCursor(), asLabelCursor( 12 ) ) ); - // WHEN and THEN - try ( Cursor cursor = txContext.nodeCursor( state, 1337 ) ) - { - if ( cursor.next() ) - { - assertTrue( "Label should have been removed", txContext.nodeRemoveLabel( state, cursor.get(), 12 ) ); - } - } + // WHEN + boolean added = txContext.nodeRemoveLabel( state, 1337, 12 ); + + // THEN + assertTrue( "Label should have been removed", added ); } @Test @@ -376,15 +285,11 @@ public void should_return_true_when_removing_non_existant_label() throws Excepti // GIVEN when( storeStatement.acquireSingleNodeCursor( 1337 ) ).thenReturn( asNodeCursor( 1337 ) ); - // WHEN and THEN - try ( Cursor cursor = txContext.nodeCursor( state, 1337 ) ) - { - if ( cursor.next() ) - { - assertFalse( "Label should have been removed", - txContext.nodeRemoveLabel( state, cursor.get(), 12 ) ); - } - } + // WHEN + boolean removed = txContext.nodeRemoveLabel( state, 1337, 12 ); + + // THEN + assertFalse( "Label should have been removed", removed ); } // exists @@ -418,7 +323,7 @@ private static Labels labels( long nodeId, Integer... labelIds ) private void commitLabels( Labels... labels ) throws Exception { - Map> allLabels = new HashMap<>(); + Map> allLabels = new HashMap<>(); for ( Labels nodeLabels : labels ) { when( storeStatement.acquireSingleNodeCursor( nodeLabels.nodeId ) ).thenReturn( StubCursors.asNodeCursor( @@ -436,7 +341,7 @@ private void commitLabels( Labels... labels ) throws Exception } } - for ( Map.Entry> entry : allLabels.entrySet() ) + for ( Map.Entry> entry : allLabels.entrySet() ) { when( store.nodesGetForLabel( state, entry.getKey() ) ).then( answerAsPrimitiveLongIteratorFrom( entry .getValue() ) ); diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/StateHandlingStatementOperationsTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/StateHandlingStatementOperationsTest.java index 29511d5c70a03..d382f6f493211 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/StateHandlingStatementOperationsTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/api/state/StateHandlingStatementOperationsTest.java @@ -19,14 +19,14 @@ */ package org.neo4j.kernel.impl.api.state; -import java.util.Collections; -import java.util.Iterator; -import java.util.Set; - import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import java.util.Collections; +import java.util.Iterator; +import java.util.Set; + import org.neo4j.collection.primitive.PrimitiveLongCollections; import org.neo4j.collection.primitive.PrimitiveLongIterator; import org.neo4j.helpers.collection.IteratorUtil; @@ -44,7 +44,6 @@ import org.neo4j.kernel.impl.util.diffsets.DiffSets; import static java.util.Arrays.asList; - import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; @@ -54,11 +53,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; - import static org.neo4j.helpers.collection.IteratorUtil.asIterable; import static org.neo4j.helpers.collection.IteratorUtil.asSet; import static org.neo4j.kernel.impl.api.StatementOperationsTestHelper.mockedState; -import static org.neo4j.kernel.impl.api.state.StubCursors.asNode; import static org.neo4j.kernel.impl.api.state.StubCursors.asNodeCursor; public class StateHandlingStatementOperationsTest @@ -85,18 +82,12 @@ public void shouldNeverDelegateWrites() throws Exception // When ctx.indexCreate( state, 0, 0 ); - ctx.nodeAddLabel( state, asNode( 0 ), 0 ); + ctx.nodeAddLabel( state, 0, 0 ); ctx.indexDrop( state, new IndexDescriptor( 0, 0 ) ); - ctx.nodeRemoveLabel( state, asNode( 0 ), 0 ); - - // These are kind of in between.. property key ids are created in - // micro-transactions, so these methods - // circumvent the normal state of affairs. We may want to rub the - // genius-bumps over this at some point. - // ctx.getOrCreateLabelId("0"); - // ctx.getOrCreatePropertyKeyId("0"); + ctx.nodeRemoveLabel( state, 0, 0 ); - verify( storeStatement, times( 0 ) ).acquireSingleNodeCursor( 0 ); + // one for add and one for remove + verify( storeStatement, times( 2 ) ).acquireSingleNodeCursor( 0 ); verifyNoMoreInteractions( storeStatement ); } diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/core/TestRelationship.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/core/TestRelationship.java index 136973d1ca3e0..1866636abcc49 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/core/TestRelationship.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/core/TestRelationship.java @@ -19,12 +19,12 @@ */ package org.neo4j.kernel.impl.core; +import org.junit.Test; + import java.util.ArrayList; import java.util.HashSet; import java.util.Set; -import org.junit.Test; - import org.neo4j.graphdb.Direction; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; @@ -41,7 +41,6 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; - import static org.neo4j.graphdb.DynamicRelationshipType.withName; import static org.neo4j.helpers.collection.IteratorUtil.addToCollection; import static org.neo4j.helpers.collection.IteratorUtil.count; @@ -994,7 +993,7 @@ public void deletionOfAlreadyDeletedRelationshipShouldThrow() // When try ( Transaction tx = db.beginTx() ) { - relationship.delete(); // should throw IllegalStateException as this relationship is already deleted + relationship.delete(); tx.success(); } } diff --git a/community/neo4j/src/test/java/ConcurrentChangesOnEntitiesTest.java b/community/neo4j/src/test/java/ConcurrentChangesOnEntitiesTest.java new file mode 100644 index 0000000000000..d0b7bfc3dac60 --- /dev/null +++ b/community/neo4j/src/test/java/ConcurrentChangesOnEntitiesTest.java @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2002-2015 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.io.IOException; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.atomic.AtomicReference; + +import org.neo4j.consistency.ConsistencyCheckService; +import org.neo4j.consistency.checking.full.ConsistencyCheckIncompleteException; +import org.neo4j.function.Consumer; +import org.neo4j.graphdb.DynamicLabel; +import org.neo4j.graphdb.DynamicRelationshipType; +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Relationship; +import org.neo4j.graphdb.Transaction; +import org.neo4j.graphdb.factory.GraphDatabaseFactory; +import org.neo4j.helpers.progress.ProgressMonitorFactory; +import org.neo4j.kernel.configuration.Config; +import org.neo4j.logging.FormattedLogProvider; +import org.neo4j.logging.LogProvider; +import org.neo4j.test.TargetDirectory; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +public class ConcurrentChangesOnEntitiesTest +{ + @Rule + public TargetDirectory.TestDirectory testDirectory = TargetDirectory.testDirForTest( getClass() ); + private final CyclicBarrier barrier = new CyclicBarrier( 2 ); + private final AtomicReference ex = new AtomicReference<>(); + private GraphDatabaseService db; + + @Before + public void setup() + { + db = new GraphDatabaseFactory().newEmbeddedDatabase( testDirectory.graphDbDir() ); + } + + @Test + public void addConcurrentlySameLabelToANode() throws Throwable + { + + final long nodeId = initWithNode( db ); + + Thread t1 = newThreadForNodeAction( nodeId, new Consumer() + { + @Override + public void accept( Node node ) + { + node.addLabel( DynamicLabel.label( "A" ) ); + } + } ); + + Thread t2 = newThreadForNodeAction( nodeId, new Consumer() + { + @Override + public void accept( Node node ) + { + node.addLabel( DynamicLabel.label( "A" ) ); + } + } ); + + startAndWait( t1, t2 ); + + db.shutdown(); + + assertDatabaseConsistent(); + } + + @Test + public void setConcurrentlySamePropertyWithDifferentValuesOnANode() throws Throwable + { + final long nodeId = initWithNode( db ); + + Thread t1 = newThreadForNodeAction( nodeId, new Consumer() + { + @Override + public void accept( Node node ) + { + node.setProperty( "a", 0.788 ); + } + } ); + + Thread t2 = newThreadForNodeAction( nodeId, new Consumer() + { + @Override + public void accept( Node node ) + { + node.setProperty( "a", new double[]{0.999, 0.77} ); + } + } ); + + startAndWait( t1, t2 ); + + db.shutdown(); + + assertDatabaseConsistent(); + } + + @Test + public void setConcurrentlySamePropertyWithDifferentValuesOnARelationship() throws Throwable + { + final long relId = initWithRel( db ); + + Thread t1 = newThreadForRelationshipAction( relId, new Consumer() + { + @Override + public void accept( Relationship relationship ) + { + relationship.setProperty( "a", 0.788 ); + } + } ); + + Thread t2 = newThreadForRelationshipAction( relId, new Consumer() + { + @Override + public void accept( Relationship relationship ) + { + relationship.setProperty( "a", new double[]{0.999, 0.77} ); + } + } ); + + startAndWait( t1, t2 ); + + db.shutdown(); + + assertDatabaseConsistent(); + } + + private long initWithNode( GraphDatabaseService db ) + { + try ( Transaction tx = db.beginTx() ) + { + Node theNode = db.createNode(); + long id = theNode.getId(); + tx.success(); + return id; + } + + } + + private long initWithRel( GraphDatabaseService db ) + { + try ( Transaction tx = db.beginTx() ) + { + Node node = db.createNode(); + node.setProperty( "a", "prop" ); + Relationship rel = node.createRelationshipTo( db.createNode(), DynamicRelationshipType.withName( "T" ) ); + long id = rel.getId(); + tx.success(); + return id; + } + } + + private Thread newThreadForNodeAction( final long nodeId, final Consumer nodeConsumer ) + { + return new Thread() + { + @Override + public void run() + { + try ( Transaction tx = db.beginTx() ) + { + Node node = db.getNodeById( nodeId ); + barrier.await(); + nodeConsumer.accept( node ); + tx.success(); + } + catch ( Exception e ) + { + ex.set( e ); + } + } + }; + } + + private Thread newThreadForRelationshipAction( final long relationshipId, final Consumer relConsumer ) + { + return new Thread() + { + @Override + public void run() + { + try ( Transaction tx = db.beginTx() ) + { + Relationship relationship = db.getRelationshipById( relationshipId ); + barrier.await(); + relConsumer.accept( relationship ); + tx.success(); + } + catch ( Exception e ) + { + ex.set( e ); + } + } + }; + } + + private void startAndWait( Thread t1, Thread t2 ) throws Exception + { + t1.start(); + t2.start(); + + t1.join(); + t2.join(); + + if ( ex.get() != null ) + { + throw ex.get(); + } + } + + private void assertDatabaseConsistent() throws IOException + { + LogProvider logProvider = FormattedLogProvider.toOutputStream( System.out ); + try + { + ConsistencyCheckService.Result result = new ConsistencyCheckService().runFullConsistencyCheck( + testDirectory.graphDbDir(), new Config(), ProgressMonitorFactory.textual( System.err ), + logProvider ); + assertTrue( result.isSuccessful() ); + } + catch ( ConsistencyCheckIncompleteException e ) + { + fail( e.getMessage() ); + } + } +}