From 84cb49333ee0e20fb3cdbd799f79fd96dd6511ee Mon Sep 17 00:00:00 2001 From: MishaDemianenko Date: Thu, 3 Jan 2019 15:14:07 +0100 Subject: [PATCH] Close all index readers opened during lockingNodeUniqueIndexSeek Previously one of the readers was leaked and that forcing lucene index to keep snapshot forever till next restart. After X calls that where cause uncontrollable index size and the number of files grows that were causing all sort of problems: OOD, OOM, inability to calculate store size over JMX, etc. --- .../newapi/DefaultNodeValueIndexCursor.java | 18 +-- .../kernel/impl/newapi/IndexReaders.java | 56 +++++++ .../neo4j/kernel/impl/newapi/Operations.java | 11 +- .../org/neo4j/kernel/impl/newapi/Read.java | 25 ++-- .../impl/index/schema/UniqueIndexSeekIT.java | 137 ++++++++++++++++++ .../TrackingIndexExtensionFactory.java | 56 +++++++ .../schema/tracking/TrackingIndexReader.java | 87 +++++++++++ .../TrackingReadersIndexAccessor.java | 124 ++++++++++++++++ .../TrackingReadersIndexProvider.java | 135 +++++++++++++++++ 9 files changed, 614 insertions(+), 35 deletions(-) create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/IndexReaders.java create mode 100644 community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/UniqueIndexSeekIT.java create mode 100644 community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingIndexExtensionFactory.java create mode 100644 community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingIndexReader.java create mode 100644 community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingReadersIndexAccessor.java create mode 100644 community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingReadersIndexProvider.java diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultNodeValueIndexCursor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultNodeValueIndexCursor.java index 3fbccfccea0cd..9d1e4060585a2 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultNodeValueIndexCursor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/DefaultNodeValueIndexCursor.java @@ -24,7 +24,6 @@ import org.neo4j.collection.primitive.PrimitiveLongCollections; import org.neo4j.collection.primitive.PrimitiveLongIterator; import org.neo4j.collection.primitive.PrimitiveLongSet; -import org.neo4j.graphdb.Resource; import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.internal.kernel.api.NodeCursor; import org.neo4j.internal.kernel.api.NodeValueIndexCursor; @@ -47,7 +46,6 @@ final class DefaultNodeValueIndexCursor extends IndexCursor implements NodeValueIndexCursor, NodeValueClient { private Read read; - private Resource resource; private long node; private IndexQuery[] query; private Value[] values; @@ -151,10 +149,9 @@ public boolean next() } } - public void setRead( Read read, Resource resource ) + public void setRead( Read read ) { this.read = read; - this.resource = resource; } @Override @@ -206,18 +203,7 @@ public void close() this.added = emptyIterator(); this.removed = PrimitiveLongCollections.emptySet(); - try - { - if ( resource != null ) - { - resource.close(); - resource = null; - } - } - finally - { - pool.accept( this ); - } + pool.accept( this ); } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/IndexReaders.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/IndexReaders.java new file mode 100644 index 0000000000000..afe46b68cdad3 --- /dev/null +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/IndexReaders.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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.newapi; + +import java.io.Closeable; +import java.util.ArrayList; +import java.util.List; + +import org.neo4j.internal.kernel.api.IndexReference; +import org.neo4j.kernel.api.exceptions.index.IndexNotFoundKernelException; +import org.neo4j.storageengine.api.schema.IndexReader; + +import static org.neo4j.io.IOUtils.closeAllSilently; + +class IndexReaders implements Closeable +{ + private final List indexReaders = new ArrayList<>(); + private final IndexReference indexReference; + private final Read read; + + IndexReaders( IndexReference indexReference, Read read ) + { + this.indexReference = indexReference; + this.read = read; + } + + IndexReader createReader() throws IndexNotFoundKernelException + { + IndexReader indexReader = read.indexReader( indexReference, true ); + indexReaders.add( indexReader ); + return indexReader; + } + + @Override + public void close() + { + closeAllSilently( indexReaders ); + } +} diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Operations.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Operations.java index 0c6846516d931..405a262ea90e2 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Operations.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Operations.java @@ -27,7 +27,6 @@ import java.util.Map; import org.neo4j.helpers.collection.CastingIterator; -import org.neo4j.helpers.collection.Iterators; import org.neo4j.internal.kernel.api.CapableIndexReference; import org.neo4j.internal.kernel.api.CursorFactory; import org.neo4j.internal.kernel.api.ExplicitIndexRead; @@ -98,7 +97,6 @@ import static java.lang.Math.min; import static org.neo4j.internal.kernel.api.exceptions.schema.ConstraintValidationException.Phase.VALIDATION; import static org.neo4j.internal.kernel.api.exceptions.schema.SchemaKernelException.OperationContext.CONSTRAINT_CREATION; -import static org.neo4j.internal.kernel.api.schema.SchemaDescriptorPredicates.hasProperty; import static org.neo4j.kernel.api.StatementConstants.NO_SUCH_NODE; import static org.neo4j.kernel.api.StatementConstants.NO_SUCH_PROPERTY_KEY; import static org.neo4j.kernel.api.schema.index.SchemaIndexDescriptor.Type.UNIQUE; @@ -418,9 +416,11 @@ private void validateNoExistingNodeWithExactValues( IndexBackedConstraintDescrip IndexQuery.ExactPredicate[] propertyValues, long modifiedNode ) throws UniquePropertyValueValidationException, UnableToValidateConstraintException { - try ( DefaultNodeValueIndexCursor valueCursor = cursors.allocateNodeValueIndexCursor() ) + SchemaIndexDescriptor schemaIndexDescriptor = constraint.ownedIndexDescriptor(); + CapableIndexReference indexReference = allStoreHolder.indexGetCapability( schemaIndexDescriptor ); + try ( DefaultNodeValueIndexCursor valueCursor = cursors.allocateNodeValueIndexCursor(); + IndexReaders indexReaders = new IndexReaders( indexReference, allStoreHolder ) ) { - SchemaIndexDescriptor schemaIndexDescriptor = constraint.ownedIndexDescriptor(); assertIndexOnline( schemaIndexDescriptor ); int labelId = schemaIndexDescriptor.schema().keyId(); @@ -430,8 +430,7 @@ private void validateNoExistingNodeWithExactValues( IndexBackedConstraintDescrip indexEntryResourceId( labelId, propertyValues ) ); - allStoreHolder.nodeIndexSeekWithFreshIndexReader( - allStoreHolder.indexGetCapability( schemaIndexDescriptor ), valueCursor, propertyValues ); + allStoreHolder.nodeIndexSeekWithFreshIndexReader( valueCursor, indexReaders.createReader(), propertyValues ); if ( valueCursor.next() && valueCursor.nodeReference() != modifiedNode ) { throw new UniquePropertyValueValidationException( constraint, VALIDATION, diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Read.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Read.java index 78c9ac9eb1239..a3e9aba2c2145 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Read.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/newapi/Read.java @@ -111,7 +111,7 @@ public final void nodeIndexSeek( DefaultNodeValueIndexCursor cursorImpl = (DefaultNodeValueIndexCursor) cursor; IndexReader reader = indexReader( index, false ); - cursorImpl.setRead( this, null ); + cursorImpl.setRead( this ); IndexProgressor.NodeValueClient target = withFullValuePrecision( cursorImpl, query, reader ); reader.query( target, indexOrder, query ); } @@ -122,7 +122,7 @@ public void nodeIndexDistinctValues( IndexReference index, NodeValueIndexCursor ktx.assertOpen(); DefaultNodeValueIndexCursor cursorImpl = (DefaultNodeValueIndexCursor) cursor; IndexReader reader = indexReader( index, true ); - cursorImpl.setRead( this, null ); + cursorImpl.setRead( this ); try ( CursorPropertyAccessor accessor = new CursorPropertyAccessor( cursors.allocateNodeCursor(), cursors.allocatePropertyCursor(), this ) ) { reader.distinctValues( cursorImpl, accessor ); @@ -189,14 +189,15 @@ public final long lockingNodeUniqueIndexSeek( //First try to find node under a shared lock //if not found upgrade to exclusive and try again locks.acquireShared( lockTracer, INDEX_ENTRY, indexEntryId ); - try ( DefaultNodeValueIndexCursor cursor = cursors.allocateNodeValueIndexCursor() ) + try ( DefaultNodeValueIndexCursor cursor = cursors.allocateNodeValueIndexCursor(); + IndexReaders readers = new IndexReaders( index, this ) ) { - nodeIndexSeekWithFreshIndexReader( index, cursor, predicates ); + nodeIndexSeekWithFreshIndexReader( cursor, readers.createReader(), predicates ); if ( !cursor.next() ) { locks.releaseShared( INDEX_ENTRY, indexEntryId ); locks.acquireExclusive( lockTracer, INDEX_ENTRY, indexEntryId ); - nodeIndexSeekWithFreshIndexReader( index, cursor, predicates ); + nodeIndexSeekWithFreshIndexReader( cursor, readers.createReader(), predicates ); if ( cursor.next() ) // we found it under the exclusive lock { // downgrade to a shared lock @@ -210,14 +211,13 @@ public final long lockingNodeUniqueIndexSeek( } void nodeIndexSeekWithFreshIndexReader( - IndexReference index, DefaultNodeValueIndexCursor cursor, - IndexQuery.ExactPredicate... query ) throws IndexNotFoundKernelException, IndexNotApplicableKernelException + IndexReader indexReader, + IndexQuery.ExactPredicate... query ) throws IndexNotApplicableKernelException { - IndexReader reader = indexReader( index, true ); - cursor.setRead( this, reader ); - IndexProgressor.NodeValueClient target = withFullValuePrecision( cursor, query, reader ); - reader.query( target, IndexOrder.NONE, query ); + cursor.setRead( this ); + IndexProgressor.NodeValueClient target = withFullValuePrecision( cursor, query, indexReader ); + indexReader.query( target, IndexOrder.NONE, query ); } @Override @@ -235,7 +235,7 @@ public final void nodeIndexScan( // for a scan, we simply query for existence of the first property, which covers all entries in an index int firstProperty = index.properties()[0]; - ((DefaultNodeValueIndexCursor) cursor).setRead( this, null ); + ((DefaultNodeValueIndexCursor) cursor).setRead( this ); indexReader( index, false ).query( (DefaultNodeValueIndexCursor) cursor, indexOrder, IndexQuery.exists( firstProperty ) ); } @@ -804,5 +804,4 @@ private void assertPredicatesMatchSchema( IndexReference index, IndexQuery.Exact } } } - } diff --git a/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/UniqueIndexSeekIT.java b/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/UniqueIndexSeekIT.java new file mode 100644 index 0000000000000..8c91cb7f2e9fe --- /dev/null +++ b/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/UniqueIndexSeekIT.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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.index.schema; + +import org.junit.Rule; +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +import org.neo4j.graphdb.Label; +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Transaction; +import org.neo4j.internal.kernel.api.CapableIndexReference; +import org.neo4j.internal.kernel.api.IndexQuery; +import org.neo4j.internal.kernel.api.Read; +import org.neo4j.internal.kernel.api.TokenRead; +import org.neo4j.internal.kernel.api.exceptions.KernelException; +import org.neo4j.kernel.api.KernelTransaction; +import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge; +import org.neo4j.kernel.impl.index.schema.tracking.TrackingIndexExtensionFactory; +import org.neo4j.kernel.internal.GraphDatabaseAPI; +import org.neo4j.test.TestGraphDatabaseFactory; +import org.neo4j.test.rule.TestDirectory; +import org.neo4j.test.rule.fs.DefaultFileSystemRule; + +import static java.util.Collections.singletonList; +import static org.hamcrest.Matchers.greaterThan; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.neo4j.graphdb.Label.label; +import static org.neo4j.kernel.impl.index.schema.tracking.TrackingReadersIndexAccessor.numberOfClosedReaders; +import static org.neo4j.kernel.impl.index.schema.tracking.TrackingReadersIndexAccessor.numberOfOpenReaders; + +public class UniqueIndexSeekIT +{ + @Rule + public final DefaultFileSystemRule fs = new DefaultFileSystemRule(); + @Rule + public final TestDirectory directory = TestDirectory.testDirectory( fs ); + + @Test + public void uniqueIndexSeekDoNotLeakIndexReaders() throws KernelException + { + TrackingIndexExtensionFactory indexExtensionFactory = new TrackingIndexExtensionFactory(); + GraphDatabaseAPI database = createDatabase( indexExtensionFactory ); + try + { + + Label label = label( "spaceship" ); + String nameProperty = "name"; + createUniqueConstraint( database, label, nameProperty ); + + generateRandomData( database, label, nameProperty ); + + assertNotNull( indexExtensionFactory.getIndexProvider() ); + assertThat( numberOfClosedReaders(), greaterThan( 0L ) ); + assertThat( numberOfOpenReaders(), greaterThan( 0L ) ); + assertEquals( numberOfClosedReaders(), numberOfOpenReaders() ); + + lockNodeUsingUniqueIndexSeek( database, label, nameProperty ); + + assertEquals( numberOfClosedReaders(), numberOfOpenReaders() ); + } + finally + { + database.shutdown(); + } + } + + private GraphDatabaseAPI createDatabase( TrackingIndexExtensionFactory indexExtensionFactory ) + { + return (GraphDatabaseAPI) new TestGraphDatabaseFactory() + .setKernelExtensions( singletonList( indexExtensionFactory ) ).newEmbeddedDatabaseBuilder( directory.graphDbDir() ).newGraphDatabase(); + } + + private void lockNodeUsingUniqueIndexSeek( GraphDatabaseAPI database, Label label, String nameProperty ) throws KernelException + { + try ( Transaction transaction = database.beginTx() ) + { + ThreadToStatementContextBridge contextBridge = database.getDependencyResolver().resolveDependency( ThreadToStatementContextBridge.class ); + KernelTransaction kernelTransaction = contextBridge.getKernelTransactionBoundToThisThread( true ); + TokenRead tokenRead = kernelTransaction.tokenRead(); + Read dataRead = kernelTransaction.dataRead(); + + int labelId = tokenRead.nodeLabel( label.name() ); + int propertyId = tokenRead.propertyKey( nameProperty ); + CapableIndexReference indexReference = kernelTransaction.schemaRead().index( labelId, propertyId ); + dataRead.lockingNodeUniqueIndexSeek( indexReference, IndexQuery.ExactPredicate.exact( propertyId, "value" ) ); + transaction.success(); + } + } + + private void generateRandomData( GraphDatabaseAPI database, Label label, String nameProperty ) + { + for ( int i = 0; i < 1000; i++ ) + { + try ( Transaction transaction = database.beginTx() ) + { + Node node = database.createNode( label ); + node.setProperty( nameProperty, "PlanetExpress" + i ); + transaction.success(); + } + } + } + + private void createUniqueConstraint( GraphDatabaseAPI database, Label label, String nameProperty ) + { + try ( Transaction transaction = database.beginTx() ) + { + database.schema().constraintFor( label ).assertPropertyIsUnique( nameProperty ).create(); + transaction.success(); + } + try ( Transaction transaction = database.beginTx() ) + { + database.schema().awaitIndexesOnline( 1, TimeUnit.MINUTES ); + transaction.success(); + } + } +} diff --git a/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingIndexExtensionFactory.java b/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingIndexExtensionFactory.java new file mode 100644 index 0000000000000..93890955cfdb1 --- /dev/null +++ b/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingIndexExtensionFactory.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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.index.schema.tracking; + +import org.neo4j.kernel.api.impl.schema.NativeLuceneFusionIndexProviderFactory20; +import org.neo4j.kernel.api.index.IndexProvider; +import org.neo4j.kernel.extension.KernelExtensionFactory; +import org.neo4j.kernel.impl.index.schema.fusion.FusionIndexProvider; +import org.neo4j.kernel.impl.spi.KernelContext; + +public class TrackingIndexExtensionFactory extends KernelExtensionFactory +{ + private TrackingReadersIndexProvider indexProvider; + + public TrackingIndexExtensionFactory() + { + super( "trackingIndex" ); + } + + public interface Dependencies extends NativeLuceneFusionIndexProviderFactory20.Dependencies + { + } + + @Override + public synchronized IndexProvider newInstance( KernelContext context, Dependencies dependencies ) + { + if ( indexProvider == null ) + { + FusionIndexProvider indexProvider = new NativeLuceneFusionIndexProviderFactory20().newInstance( context, dependencies ); + this.indexProvider = new TrackingReadersIndexProvider( indexProvider ); + } + return indexProvider; + } + + public TrackingReadersIndexProvider getIndexProvider() + { + return indexProvider; + } +} diff --git a/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingIndexReader.java b/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingIndexReader.java new file mode 100644 index 0000000000000..0e6145e6f18b6 --- /dev/null +++ b/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingIndexReader.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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.index.schema.tracking; + +import java.util.concurrent.atomic.AtomicLong; + +import org.neo4j.collection.primitive.PrimitiveLongResourceIterator; +import org.neo4j.internal.kernel.api.IndexOrder; +import org.neo4j.internal.kernel.api.IndexQuery; +import org.neo4j.kernel.api.exceptions.index.IndexNotApplicableKernelException; +import org.neo4j.kernel.api.index.PropertyAccessor; +import org.neo4j.storageengine.api.schema.IndexProgressor; +import org.neo4j.storageengine.api.schema.IndexReader; +import org.neo4j.storageengine.api.schema.IndexSampler; +import org.neo4j.values.storable.Value; + +public class TrackingIndexReader implements IndexReader +{ + private final IndexReader delegate; + private final AtomicLong closeReadersCounter; + + public TrackingIndexReader( IndexReader delegate, AtomicLong closeReadersCounter ) + { + this.delegate = delegate; + this.closeReadersCounter = closeReadersCounter; + } + + @Override + public long countIndexedNodes( long nodeId, Value... propertyValues ) + { + return delegate.countIndexedNodes( nodeId, propertyValues ); + } + + @Override + public IndexSampler createSampler() + { + return delegate.createSampler(); + } + + @Override + public PrimitiveLongResourceIterator query( IndexQuery... predicates ) throws IndexNotApplicableKernelException + { + return delegate.query( predicates ); + } + + @Override + public void query( IndexProgressor.NodeValueClient client, IndexOrder indexOrder, IndexQuery... query ) throws IndexNotApplicableKernelException + { + delegate.query( client, indexOrder, query ); + } + + @Override + public boolean hasFullValuePrecision( IndexQuery... predicates ) + { + return delegate.hasFullValuePrecision( predicates ); + } + + @Override + public void distinctValues( IndexProgressor.NodeValueClient client, PropertyAccessor propertyAccessor ) + { + delegate.distinctValues( client, propertyAccessor ); + } + + @Override + public void close() + { + delegate.close(); + closeReadersCounter.incrementAndGet(); + } +} diff --git a/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingReadersIndexAccessor.java b/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingReadersIndexAccessor.java new file mode 100644 index 0000000000000..d22e6aa4005da --- /dev/null +++ b/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingReadersIndexAccessor.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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.index.schema.tracking; + +import java.io.File; +import java.io.IOException; +import java.util.concurrent.atomic.AtomicLong; + +import org.neo4j.graphdb.ResourceIterator; +import org.neo4j.helpers.collection.BoundedIterable; +import org.neo4j.io.pagecache.IOLimiter; +import org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException; +import org.neo4j.kernel.api.index.IndexAccessor; +import org.neo4j.kernel.api.index.IndexUpdater; +import org.neo4j.kernel.api.index.PropertyAccessor; +import org.neo4j.kernel.impl.api.index.IndexUpdateMode; +import org.neo4j.storageengine.api.schema.IndexReader; +import org.neo4j.values.storable.Value; + +public class TrackingReadersIndexAccessor implements IndexAccessor +{ + private final IndexAccessor accessor; + private static final AtomicLong openReaders = new AtomicLong(); + private static final AtomicLong closedReaders = new AtomicLong(); + + public static long numberOfOpenReaders() + { + return openReaders.get(); + } + + public static long numberOfClosedReaders() + { + return closedReaders.get(); + } + + TrackingReadersIndexAccessor( IndexAccessor accessor ) + { + this.accessor = accessor; + } + + @Override + public void drop() throws IOException + { + accessor.drop(); + } + + @Override + public IndexUpdater newUpdater( IndexUpdateMode mode ) + { + return accessor.newUpdater( mode ); + } + + @Override + public void force( IOLimiter ioLimiter ) throws IOException + { + accessor.force( ioLimiter ); + } + + @Override + public void refresh() throws IOException + { + accessor.refresh(); + } + + @Override + public void close() throws IOException + { + accessor.close(); + } + + @Override + public IndexReader newReader() + { + openReaders.incrementAndGet(); + return new TrackingIndexReader( accessor.newReader(), closedReaders ); + } + + @Override + public BoundedIterable newAllEntriesReader() + { + return accessor.newAllEntriesReader(); + } + + @Override + public ResourceIterator snapshotFiles() throws IOException + { + return accessor.snapshotFiles(); + } + + @Override + public void verifyDeferredConstraints( PropertyAccessor propertyAccessor ) throws IndexEntryConflictException, IOException + { + accessor.verifyDeferredConstraints( propertyAccessor ); + } + + @Override + public boolean isDirty() + { + return accessor.isDirty(); + } + + @Override + public void validateBeforeCommit( Value[] tuple ) + { + accessor.validateBeforeCommit( tuple ); + } +} diff --git a/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingReadersIndexProvider.java b/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingReadersIndexProvider.java new file mode 100644 index 0000000000000..596d579ea9782 --- /dev/null +++ b/community/neo4j/src/test/java/org/neo4j/kernel/impl/index/schema/tracking/TrackingReadersIndexProvider.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2002-2018 "Neo4j," + * Neo4j Sweden AB [http://neo4j.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.index.schema.tracking; + +import java.io.IOException; + +import org.neo4j.internal.kernel.api.IndexCapability; +import org.neo4j.internal.kernel.api.InternalIndexState; +import org.neo4j.io.fs.FileSystemAbstraction; +import org.neo4j.io.pagecache.PageCache; +import org.neo4j.kernel.api.index.IndexAccessor; +import org.neo4j.kernel.api.index.IndexDirectoryStructure; +import org.neo4j.kernel.api.index.IndexPopulator; +import org.neo4j.kernel.api.index.IndexProvider; +import org.neo4j.kernel.api.schema.index.SchemaIndexDescriptor; +import org.neo4j.kernel.impl.api.index.sampling.IndexSamplingConfig; +import org.neo4j.kernel.impl.storemigration.StoreMigrationParticipant; + +public class TrackingReadersIndexProvider extends IndexProvider +{ + private final IndexProvider indexProvider; + + public TrackingReadersIndexProvider( IndexProvider copySource ) + { + super( copySource ); + this.indexProvider = copySource; + } + + @Override + public IndexAccessor getOnlineAccessor( long indexId, SchemaIndexDescriptor descriptor, IndexSamplingConfig samplingConfig ) throws IOException + { + return new TrackingReadersIndexAccessor( indexProvider.getOnlineAccessor( indexId, descriptor, samplingConfig ) ); + } + + @Override + public String getPopulationFailure( long indexId, SchemaIndexDescriptor descriptor ) throws IllegalStateException + { + return indexProvider.getPopulationFailure( indexId, descriptor ); + } + + @Override + public InternalIndexState getInitialState( long indexId, SchemaIndexDescriptor descriptor ) + { + return indexProvider.getInitialState( indexId, descriptor ); + } + + @Override + public IndexCapability getCapability( SchemaIndexDescriptor schemaIndexDescriptor ) + { + return indexProvider.getCapability( schemaIndexDescriptor ); + } + + @Override + public Descriptor getProviderDescriptor() + { + return indexProvider.getProviderDescriptor(); + } + + @Override + public int compareTo( IndexProvider o ) + { + return indexProvider.compareTo( o ); + } + + @Override + public boolean equals( Object o ) + { + return indexProvider.equals( o ); + } + + @Override + public int hashCode() + { + return indexProvider.hashCode(); + } + + @Override + public IndexDirectoryStructure directoryStructure() + { + return indexProvider.directoryStructure(); + } + + @Override + public StoreMigrationParticipant storeMigrationParticipant( FileSystemAbstraction fs, PageCache pageCache ) + { + return indexProvider.storeMigrationParticipant( fs, pageCache ); + } + + @Override + public void init() throws Throwable + { + indexProvider.init(); + } + + @Override + public void start() throws Throwable + { + indexProvider.start(); + } + + @Override + public void stop() throws Throwable + { + indexProvider.stop(); + } + + @Override + public void shutdown() throws Throwable + { + indexProvider.shutdown(); + } + + @Override + public IndexPopulator getPopulator( long indexId, SchemaIndexDescriptor descriptor, IndexSamplingConfig samplingConfig ) + { + return indexProvider.getPopulator( indexId, descriptor, samplingConfig ); + } +}