From 2d1c557458b4cd47751c58783d5a00f5917b16a4 Mon Sep 17 00:00:00 2001 From: MishaDemianenko Date: Thu, 25 May 2017 18:37:18 +0200 Subject: [PATCH] Revert "Merge pull request #9 from davidegrohmann/3.3-relationship-group-cursor" This reverts commit 06380f4f77a353b201e16e8f41e7c8a8bf11bbf6, reversing changes made to a204b9ccb2e9250548499c02b3175f9f88914b37. --- .../AccessStatsKeepingStoreAccess.java | 7 +- .../impl/api/store/DegreeVisitable.java | 9 +- .../api/store/RelationshipGroupCursor.java | 124 ------------------ .../kernel/impl/api/store/StorageLayer.java | 13 +- .../impl/api/store/StorePropertyCursor.java | 1 + .../api/store/StoreSinglePropertyCursor.java | 1 + .../store/StoreSingleRelationshipCursor.java | 2 + .../kernel/impl/api/store/StoreStatement.java | 31 ++--- .../neo4j/kernel/impl/store/RecordCursor.java | 20 +++ .../kernel/impl/store/RecordCursors.java | 56 ++++++++ .../kernel/impl/store/StoreRecordCursor.java | 8 +- .../kernel/impl/store/record/Record.java | 1 - .../api/RelationshipGroupItem.java | 27 ---- .../storageengine/api/StorageStatement.java | 12 +- .../CommonAbstractStoreBehaviourTest.java | 10 +- .../kernel/impl/store/RecordCursorsTest.java | 110 ++++++++++++++++ 16 files changed, 249 insertions(+), 183 deletions(-) delete mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/RelationshipGroupCursor.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/store/RecordCursors.java delete mode 100644 community/kernel/src/main/java/org/neo4j/storageengine/api/RelationshipGroupItem.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/store/RecordCursorsTest.java diff --git a/community/consistency-check/src/main/java/org/neo4j/consistency/statistics/AccessStatsKeepingStoreAccess.java b/community/consistency-check/src/main/java/org/neo4j/consistency/statistics/AccessStatsKeepingStoreAccess.java index cf204761c363..6fe91ae0a085 100644 --- a/community/consistency-check/src/main/java/org/neo4j/consistency/statistics/AccessStatsKeepingStoreAccess.java +++ b/community/consistency-check/src/main/java/org/neo4j/consistency/statistics/AccessStatsKeepingStoreAccess.java @@ -44,7 +44,7 @@ protected RecordStore wrapStore( RecordStore( store, accessStats ); + return new AccessStatsKeepingRecordStore( store, accessStats ); } private static class AccessStatsKeepingRecordStore @@ -58,6 +58,11 @@ private static class AccessStatsKeepingRecordStore cache ) { this.relationshipStore = relationshipStore; @@ -74,7 +73,7 @@ public void accept( DegreeVisitor visitor ) boolean keepGoing = true; while ( keepGoing && groupId != NO_NEXT_RELATIONSHIP.longValue() ) { - RelationshipGroupRecord record = read( groupId, groupStore, groupRecord, FORCE, groupCursor ); + RelationshipGroupRecord record = StoreStatement.read( groupId, groupStore, groupRecord, FORCE, groupCursor ); if ( record.inUse() ) { int type = record.getType(); @@ -89,12 +88,12 @@ public void accept( DegreeVisitor visitor ) private long countByFirstPrevPointer( long relationshipId ) { - if ( Record.NO_NEXT_RELATIONSHIP.is( relationshipId ) ) + if ( relationshipId == Record.NO_NEXT_RELATIONSHIP.longValue() ) { return 0; } RelationshipRecord record = - read( relationshipId, relationshipStore, relationshipRecord, FORCE, relationshipCursor ); + StoreStatement.read( relationshipId, relationshipStore, relationshipRecord, FORCE, relationshipCursor ); if ( record.getFirstNode() == nodeId ) { return record.getFirstPrevRel(); diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/RelationshipGroupCursor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/RelationshipGroupCursor.java deleted file mode 100644 index b8f5623be1eb..000000000000 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/RelationshipGroupCursor.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2002-2017 "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 . - */ -package org.neo4j.kernel.impl.api.store; - -import java.util.function.Consumer; - -import org.neo4j.cursor.Cursor; -import org.neo4j.function.Disposable; -import org.neo4j.io.pagecache.PageCursor; -import org.neo4j.kernel.api.StatementConstants; -import org.neo4j.kernel.impl.store.RelationshipGroupStore; -import org.neo4j.kernel.impl.store.record.Record; -import org.neo4j.kernel.impl.store.record.RelationshipGroupRecord; -import org.neo4j.storageengine.api.RelationshipGroupItem; - -import static org.neo4j.kernel.impl.api.store.StoreStatement.read; -import static org.neo4j.kernel.impl.store.record.RecordLoad.FORCE; - -public class RelationshipGroupCursor implements RelationshipGroupItem, Cursor, Disposable -{ - private final RelationshipGroupStore relationshipGroupStore; - private final RelationshipGroupRecord relationshipGroupRecord; - private final PageCursor cursor; - private final Consumer cache; - - private boolean fetched; - private long relationshipGroupId; - - RelationshipGroupCursor( RelationshipGroupStore relationshipGroupStore, Consumer cache ) - { - this.relationshipGroupStore = relationshipGroupStore; - this.relationshipGroupRecord = relationshipGroupStore.newRecord(); - this.cursor = relationshipGroupStore.newPageCursor(); - this.cache = cache; - } - - public RelationshipGroupCursor init( long relationshipGroupId ) - { - this.relationshipGroupId = relationshipGroupId; - return this; - } - - @Override - public boolean next() - { - return fetched = fetchNext(); - } - - private boolean fetchNext() - { - - while ( true ) - { - if ( Record.NO_NEXT_RELATIONSHIP_GROUP.is( relationshipGroupId ) ) - { - return false; - } - - RelationshipGroupRecord record = - read( relationshipGroupId, relationshipGroupStore, relationshipGroupRecord, FORCE, cursor ); - relationshipGroupId = record.getNext(); - if ( record.inUse() ) - { - return true; - } - - // this record is not in use try with the next - } - } - - @Override - public RelationshipGroupItem get() - { - if ( fetched ) - { - return this; - } - - throw new IllegalStateException( "Nothing available" ); - } - - @Override - public long id() - { - return relationshipGroupRecord.getId(); - } - - @Override - public int type() - { - return relationshipGroupRecord.getType(); - } - - @Override - public void close() - { - fetched = false; - relationshipGroupId = StatementConstants.NO_SUCH_RELATIONSHIP; - cache.accept( this ); - } - - @Override - public void dispose() - { - cursor.close(); - } -} diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StorageLayer.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StorageLayer.java index 672aef72d3ed..7c53c25992b8 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StorageLayer.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StorageLayer.java @@ -56,6 +56,7 @@ import org.neo4j.kernel.impl.store.InvalidRecordException; import org.neo4j.kernel.impl.store.NeoStores; import org.neo4j.kernel.impl.store.NodeStore; +import org.neo4j.kernel.impl.store.RecordCursor; import org.neo4j.kernel.impl.store.RecordStore; import org.neo4j.kernel.impl.store.RelationshipStore; import org.neo4j.kernel.impl.store.SchemaStorage; @@ -83,7 +84,9 @@ import org.neo4j.storageengine.api.txstate.ReadableTransactionState; import static org.neo4j.collection.primitive.Primitive.intSet; +import static org.neo4j.kernel.impl.store.record.Record.NO_NEXT_RELATIONSHIP; import static org.neo4j.kernel.impl.store.record.RecordLoad.CHECK; +import static org.neo4j.kernel.impl.store.record.RecordLoad.FORCE; import static org.neo4j.register.Registers.newDoubleLongRegister; import static org.neo4j.storageengine.api.Direction.BOTH; import static org.neo4j.storageengine.api.Direction.INCOMING; @@ -588,7 +591,15 @@ public PrimitiveIntSet relationshipTypes( StorageStatement statement, NodeItem n PrimitiveIntSet set = intSet(); if ( node.isDense() ) { - statement.acquireRelationshipGroupCursor( node.nextGroupId() ).forAll( group -> set.add( group.type() ) ); + RelationshipGroupRecord groupRecord = relationshipGroupStore.newRecord(); + RecordCursor cursor = statement.recordCursors().relationshipGroup(); + for ( long id = node.nextGroupId(); id != NO_NEXT_RELATIONSHIP.intValue(); id = groupRecord.getNext() ) + { + if ( cursor.next( id, groupRecord, FORCE ) ) + { + set.add( groupRecord.getType() ); + } + } } else { diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StorePropertyCursor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StorePropertyCursor.java index 5e3e4fa46850..912ca52d4210 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StorePropertyCursor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StorePropertyCursor.java @@ -25,6 +25,7 @@ import org.neo4j.kernel.api.properties.DefinedProperty; import org.neo4j.kernel.impl.locking.Lock; import org.neo4j.kernel.impl.store.PropertyStore; +import org.neo4j.kernel.impl.store.RecordCursors; import org.neo4j.storageengine.api.StorageProperty; import org.neo4j.storageengine.api.txstate.PropertyContainerState; diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreSinglePropertyCursor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreSinglePropertyCursor.java index b704a72a054c..b5158c5b58f7 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreSinglePropertyCursor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreSinglePropertyCursor.java @@ -24,6 +24,7 @@ import org.neo4j.kernel.api.properties.DefinedProperty; import org.neo4j.kernel.impl.locking.Lock; import org.neo4j.kernel.impl.store.PropertyStore; +import org.neo4j.kernel.impl.store.RecordCursors; import org.neo4j.storageengine.api.txstate.PropertyContainerState; public class StoreSinglePropertyCursor extends StoreAbstractPropertyCursor diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreSingleRelationshipCursor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreSingleRelationshipCursor.java index 72096d61b471..4610de3edf5c 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreSingleRelationshipCursor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreSingleRelationshipCursor.java @@ -20,7 +20,9 @@ package org.neo4j.kernel.impl.api.store; import org.neo4j.kernel.impl.locking.LockService; +import org.neo4j.kernel.impl.store.RecordCursors; import org.neo4j.kernel.impl.store.RelationshipStore; +import org.neo4j.kernel.impl.store.record.RelationshipRecord; import org.neo4j.kernel.impl.util.InstanceCache; import org.neo4j.storageengine.api.txstate.ReadableTransactionState; diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreStatement.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreStatement.java index 24f5888ea376..6f5a29f3d1a3 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreStatement.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/store/StoreStatement.java @@ -32,6 +32,7 @@ import org.neo4j.kernel.impl.locking.LockService; import org.neo4j.kernel.impl.store.CommonAbstractStore; import org.neo4j.kernel.impl.store.NeoStores; +import org.neo4j.kernel.impl.store.RecordCursors; import org.neo4j.kernel.impl.store.UnderlyingStorageException; import org.neo4j.kernel.impl.store.record.AbstractBaseRecord; import org.neo4j.kernel.impl.store.record.RecordLoad; @@ -39,7 +40,6 @@ import org.neo4j.storageengine.api.Direction; import org.neo4j.storageengine.api.NodeItem; import org.neo4j.storageengine.api.PropertyItem; -import org.neo4j.storageengine.api.RelationshipGroupItem; import org.neo4j.storageengine.api.RelationshipItem; import org.neo4j.storageengine.api.StorageStatement; import org.neo4j.storageengine.api.schema.IndexReader; @@ -62,11 +62,11 @@ public class StoreStatement implements StorageStatement private final InstanceCache nodeRelationshipsCursor; private final InstanceCache propertyCursorCache; private final InstanceCache singlePropertyCursorCache; - private final InstanceCache relationshipGroupCursorCache; private final InstanceCache degreeVisitableCache; private final NeoStores neoStores; private final Supplier indexReaderFactorySupplier; private final Supplier labelScanStore; + private final RecordCursors recordCursors; private IndexReaderFactory indexReaderFactory; private LabelScanReader labelScanReader; @@ -80,6 +80,7 @@ public StoreStatement( NeoStores neoStores, Supplier indexRe this.neoStores = neoStores; this.indexReaderFactorySupplier = indexReaderFactory; this.labelScanStore = labelScanReaderSupplier; + this.recordCursors = new RecordCursors( neoStores ); nodeCursor = new InstanceCache() { @@ -139,14 +140,6 @@ protected DegreeVisitable create() this ); } }; - relationshipGroupCursorCache = new InstanceCache() - { - @Override - protected RelationshipGroupCursor create() - { - return new RelationshipGroupCursor( neoStores.getRelationshipGroupStore(), this ); - } - }; } @Override @@ -209,15 +202,9 @@ public Cursor acquireSinglePropertyCursor( long propertyId, int pr } @Override - public Cursor acquireRelationshipGroupCursor( long relationshipGroupId ) + public DegreeVisitor.Visitable acquireDenseNodeDegreeCounter( long nodeId, long groupId ) { - return relationshipGroupCursorCache.get().init( relationshipGroupId ); - } - - @Override - public DegreeVisitor.Visitable acquireDenseNodeDegreeCounter( long nodeId, long relationshipGroupId ) - { - return degreeVisitableCache.get().init( nodeId, relationshipGroupId ); + return degreeVisitableCache.get().init( nodeId, groupId ); } @Override @@ -240,8 +227,8 @@ public void close() nodeRelationshipsCursor.close(); propertyCursorCache.close(); singlePropertyCursorCache.close(); - relationshipGroupCursorCache.close(); degreeVisitableCache.close(); + recordCursors.close(); closed = true; } @@ -284,6 +271,12 @@ public IndexReader getFreshIndexReader( IndexDescriptor descriptor ) throws Inde return indexReaderFactory().newUnCachedReader( descriptor ); } + @Override + public RecordCursors recordCursors() + { + return recordCursors; + } + public static > RECORD read( long id, STORE store, RECORD record, RecordLoad mode, PageCursor cursor ) { diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/RecordCursor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/RecordCursor.java index 120cbbc18421..231d4c151ff0 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/RecordCursor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/RecordCursor.java @@ -77,6 +77,20 @@ public interface RecordCursor extends Cursor */ boolean next( long id ); + /** + * An additional way of placing this cursor at an arbitrary record id. + * Calling this method will not advance the "current id" as to change which {@link #next()} will load next. + * This method is useful when there's an opportunity to load a record from an already acquired + * {@link PageCursor} and potentially even an already pinned page. + * + * @param id record id to place cursor at. + * @param record record to load the record data into. + * @param mode {@link RecordLoad} mode temporarily overriding the default provided in + * {@link #acquire(long, RecordLoad)}. + * @return whether or not that record is in use. + */ + boolean next( long id, R record, RecordLoad mode ); + /** * Read all records in the chain starting from the id this cursor is positioned at using either * {@link #acquire(long, RecordLoad)} or {@link #placeAt(long, RecordLoad)}. Each next record in the chain is @@ -141,5 +155,11 @@ public boolean next( long id ) { return actual.next( id ); } + + @Override + public boolean next( long id, R record, RecordLoad mode ) + { + return actual.next( id, record, mode ); + } } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/RecordCursors.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/RecordCursors.java new file mode 100644 index 000000000000..d23f0275d943 --- /dev/null +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/RecordCursors.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2002-2017 "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 . + */ +package org.neo4j.kernel.impl.store; + +import org.neo4j.io.IOUtils; +import org.neo4j.kernel.impl.store.record.AbstractBaseRecord; +import org.neo4j.kernel.impl.store.record.RelationshipGroupRecord; +import org.neo4j.kernel.impl.store.record.RelationshipRecord; + +import static org.neo4j.kernel.impl.store.record.RecordLoad.NORMAL; + +/** + * Container for {@link RecordCursor}s for different stores. Intended to be reused by pooled transactions. + */ +public class RecordCursors implements AutoCloseable +{ + private final RecordCursor relationshipGroup; + + public RecordCursors( NeoStores neoStores ) + { + relationshipGroup = newCursor( neoStores.getRelationshipGroupStore() ); + } + + private static RecordCursor newCursor( RecordStore store ) + { + return store.newRecordCursor( store.newRecord() ).acquire( store.getNumberOfReservedLowIds(), NORMAL ); + } + + @Override + public void close() + { + IOUtils.closeAll( RuntimeException.class, relationshipGroup ); + } + + public RecordCursor relationshipGroup() + { + return relationshipGroup; + } +} diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/StoreRecordCursor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/StoreRecordCursor.java index 57906850dae4..0688cf9d2e8f 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/StoreRecordCursor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/StoreRecordCursor.java @@ -47,7 +47,7 @@ public boolean next() { try { - return next( currentId ); + return next( currentId, record, mode ); } finally { @@ -62,6 +62,12 @@ public boolean next() @Override public boolean next( long id ) + { + return next( id, record, mode ); + } + + @Override + public boolean next( long id, RECORD record, RecordLoad mode ) { assert pageCursor != null : "Not initialized"; if ( NULL_REFERENCE.is( id ) ) diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/Record.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/Record.java index 33977da8c747..8c0f5d5d5832 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/Record.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/store/record/Record.java @@ -37,7 +37,6 @@ public enum Record NO_PREVIOUS_PROPERTY( NULL_REFERENCE ), NO_NEXT_RELATIONSHIP( NULL_REFERENCE ), NO_PREV_RELATIONSHIP( NULL_REFERENCE ), - NO_NEXT_RELATIONSHIP_GROUP( NULL_REFERENCE ), NO_NEXT_BLOCK( NULL_REFERENCE ), NODE_PROPERTY( (byte) 0, 0 ), diff --git a/community/kernel/src/main/java/org/neo4j/storageengine/api/RelationshipGroupItem.java b/community/kernel/src/main/java/org/neo4j/storageengine/api/RelationshipGroupItem.java deleted file mode 100644 index 985bf9104fe1..000000000000 --- a/community/kernel/src/main/java/org/neo4j/storageengine/api/RelationshipGroupItem.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2002-2017 "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 . - */ -package org.neo4j.storageengine.api; - -public interface RelationshipGroupItem -{ - long id(); - - int type(); -} diff --git a/community/kernel/src/main/java/org/neo4j/storageengine/api/StorageStatement.java b/community/kernel/src/main/java/org/neo4j/storageengine/api/StorageStatement.java index 39b525418e68..e413fa2cb4fc 100644 --- a/community/kernel/src/main/java/org/neo4j/storageengine/api/StorageStatement.java +++ b/community/kernel/src/main/java/org/neo4j/storageengine/api/StorageStatement.java @@ -24,6 +24,7 @@ import org.neo4j.kernel.api.schema.index.IndexDescriptor; import org.neo4j.kernel.impl.api.DegreeVisitor; import org.neo4j.kernel.impl.locking.Lock; +import org.neo4j.kernel.impl.store.RecordCursors; import org.neo4j.storageengine.api.schema.IndexReader; import org.neo4j.storageengine.api.schema.LabelScanReader; import org.neo4j.storageengine.api.txstate.PropertyContainerState; @@ -123,9 +124,7 @@ Cursor acquireNodeRelationshipCursor( boolean isDense, long no Cursor acquireSinglePropertyCursor( long propertyId, int propertyKeyId, Lock shortLivedReadLock, PropertyContainerState state ); - Cursor acquireRelationshipGroupCursor( long relationshipGroupId ); - - DegreeVisitor.Visitable acquireDenseNodeDegreeCounter( long nodeId, long relationshipGroupId ); + DegreeVisitor.Visitable acquireDenseNodeDegreeCounter( long nodeId, long groupId ); /** * @return {@link LabelScanReader} capable of reading nodes for specific label ids. @@ -159,4 +158,11 @@ Cursor acquireSinglePropertyCursor( long propertyId, int propertyK * @throws IndexNotFoundKernelException if no such index exists. */ IndexReader getFreshIndexReader( IndexDescriptor index ) throws IndexNotFoundKernelException; + + /** + * Access to low level record cursors + * + * @return record cursors + */ + RecordCursors recordCursors(); } diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/store/CommonAbstractStoreBehaviourTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/store/CommonAbstractStoreBehaviourTest.java index ace891b3c937..322ad1d2b563 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/store/CommonAbstractStoreBehaviourTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/store/CommonAbstractStoreBehaviourTest.java @@ -257,7 +257,7 @@ public void pageCursorErrorsMustNotLingerInRecordCursor() throws Exception // This will encounter a decoding error, which is ignored because FORCE assertTrue( cursor.next() ); // Then this should not fail because of the previous decoding error, even though we stay on the same page - assertTrue( cursor.next( 2 ) ); + assertTrue( cursor.next( 2, new IntRecord( 2 ), NORMAL ) ); } @Test @@ -277,6 +277,10 @@ public void shouldReadTheCorrectRecordWhenGivenAnExplicitIdAndNotUseTheCurrentId assertTrue( cursor.next( 43 ) ); assertEquals( record43, cursor.get() ); + IntRecord record = new IntRecord( -1 ); + assertTrue( cursor.next( 43, record, NORMAL ) ); + assertEquals( record43, record ); + // next with id does not affect the old pointer either, so 42 is read now assertTrue( cursor.next() ); assertEquals( record42, cursor.get() ); @@ -301,6 +305,10 @@ public void shouldJumpAroundPageIds() throws Exception assertTrue( cursor.next( idOnAnotherPage ) ); assertEquals( record43, cursor.get() ); + IntRecord record = new IntRecord( -1 ); + assertTrue( cursor.next( idOnAnotherPage, record, NORMAL ) ); + assertEquals( record43, record ); + // next with id does not affect the old pointer either, so 42 is read now assertTrue( cursor.next() ); assertEquals( record42, cursor.get() ); diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/store/RecordCursorsTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/store/RecordCursorsTest.java new file mode 100644 index 000000000000..b03be9eb7afd --- /dev/null +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/store/RecordCursorsTest.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2002-2017 "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 . + */ +package org.neo4j.kernel.impl.store; + +import org.junit.Test; + +import org.neo4j.kernel.impl.store.record.AbstractBaseRecord; +import org.neo4j.kernel.impl.store.record.RelationshipGroupRecord; + +import static org.junit.Assert.assertSame; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class RecordCursorsTest +{ + @Test + public void closesCursors() + { + RecordCursors cursors = newRecordCursorsWithMockedNeoStores(); + + cursors.close(); + + verifyAllCursorsClosed( cursors ); + } + + @Test + public void closesCursorsEvenIfSomeCursorFailsToClose() + { + RecordCursors cursors = newRecordCursorsWithMockedNeoStores(); + RecordCursor relGroupCursor = cursors.relationshipGroup(); + RuntimeException exception = new RuntimeException( "Close failure" ); + doThrow( exception ).when( relGroupCursor ).close(); + + try + { + cursors.close(); + } + catch ( Exception e ) + { + assertSame( exception, e.getCause() ); + } + + verifyAllCursorsClosed( cursors ); + } + + private static void verifyAllCursorsClosed( RecordCursors recordCursors ) + { + verify( recordCursors.relationshipGroup() ).close(); + } + + private static RecordCursors newRecordCursorsWithMockedNeoStores() + { + NeoStores neoStores = mock( NeoStores.class ); + NodeStore nodeStore = newStoreMockWithRecordCursor( NodeStore.class ); + RelationshipStore relStore = newStoreMockWithRecordCursor( RelationshipStore.class ); + RelationshipGroupStore relGroupStore = newStoreMockWithRecordCursor( RelationshipGroupStore.class ); + PropertyStore propertyStore = newStoreMockWithRecordCursor( PropertyStore.class ); + DynamicStringStore dynamicStringStore = newStoreMockWithRecordCursor( DynamicStringStore.class ); + DynamicArrayStore dynamicArrayStore = newStoreMockWithRecordCursor( DynamicArrayStore.class ); + DynamicArrayStore dynamicLabelStore = newStoreMockWithRecordCursor( DynamicArrayStore.class ); + + when( neoStores.getNodeStore() ).thenReturn( nodeStore ); + when( neoStores.getRelationshipStore() ).thenReturn( relStore ); + when( neoStores.getRelationshipGroupStore() ).thenReturn( relGroupStore ); + when( neoStores.getPropertyStore() ).thenReturn( propertyStore ); + when( propertyStore.getStringStore() ).thenReturn( dynamicStringStore ); + when( propertyStore.getArrayStore() ).thenReturn( dynamicArrayStore ); + when( nodeStore.getDynamicLabelStore() ).thenReturn( dynamicLabelStore ); + + return new RecordCursors( neoStores ); + } + + private static , R extends AbstractBaseRecord> S newStoreMockWithRecordCursor( + Class storeClass ) + { + S storeMock = mock( storeClass ); + RecordCursor cursor = newCursorMock(); + when( storeMock.newRecordCursor( any() ) ).thenReturn( cursor ); + return storeMock; + } + + @SuppressWarnings( "unchecked" ) + private static RecordCursor newCursorMock() + { + RecordCursor cursor = mock( RecordCursor.class ); + when( cursor.acquire( anyLong(), any() ) ).thenReturn( cursor ); + return cursor; + } +}