diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/index/IndexProvider.java b/community/kernel/src/main/java/org/neo4j/kernel/api/index/IndexProvider.java index 67503d78ef4d2..fe408e90c86c7 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/api/index/IndexProvider.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/index/IndexProvider.java @@ -370,4 +370,48 @@ public String toString() return "{key=" + key + ", version=" + version + "}"; } } + + public static class Adaptor extends IndexProvider + { + protected Adaptor( Descriptor descriptor, int priority, IndexDirectoryStructure.Factory directoryStructureFactory ) + { + super( descriptor, priority, directoryStructureFactory ); + } + + @Override + public IndexPopulator getPopulator( long indexId, SchemaIndexDescriptor descriptor, IndexSamplingConfig samplingConfig ) + { + return null; + } + + @Override + public IndexAccessor getOnlineAccessor( long indexId, SchemaIndexDescriptor descriptor, IndexSamplingConfig samplingConfig ) throws IOException + { + return null; + } + + @Override + public String getPopulationFailure( long indexId, SchemaIndexDescriptor descriptor ) throws IllegalStateException + { + return null; + } + + @Override + public InternalIndexState getInitialState( long indexId, SchemaIndexDescriptor descriptor ) + { + return null; + } + + @Override + public IndexCapability getCapability( SchemaIndexDescriptor schemaIndexDescriptor ) + { + return null; + } + + @Override + public StoreMigrationParticipant storeMigrationParticipant( FileSystemAbstraction fs, PageCache pageCache ) + { + return null; + } + } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/sampling/OnlineIndexSamplingJob.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/sampling/OnlineIndexSamplingJob.java index b6b3611e6b4ca..d696a0c342878 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/sampling/OnlineIndexSamplingJob.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/api/index/sampling/OnlineIndexSamplingJob.java @@ -63,9 +63,9 @@ public void run() { try { - try ( IndexReader reader = indexProxy.newReader() ) + try ( IndexReader reader = indexProxy.newReader(); + IndexSampler sampler = reader.createSampler() ) { - IndexSampler sampler = reader.createSampler(); IndexSample sample = sampler.sampleIndex(); // check again if the index is online before saving the counts in the store diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexSampler.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexSampler.java index 029c2affee68c..7d6f7c29e39eb 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexSampler.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexSampler.java @@ -23,6 +23,8 @@ import org.neo4j.storageengine.api.schema.IndexSample; import org.neo4j.storageengine.api.schema.IndexSampler; +import static org.neo4j.io.IOUtils.closeAllSilently; + public class FusionIndexSampler implements IndexSampler { private final IndexSampler[] samplers; @@ -56,4 +58,10 @@ public static IndexSample combineSamples( IndexSample... samples ) } return new IndexSample( indexSize, uniqueValues, sampleSize ); } + + @Override + public void close() + { + closeAllSilently( samplers ); + } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/SlotSelector.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/SlotSelector.java index 5de6b9ea33640..365ace01dca5f 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/SlotSelector.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/SlotSelector.java @@ -29,8 +29,10 @@ /** * Given a set of values selects a slot to use. */ -interface SlotSelector +public interface SlotSelector { + SlotSelector nullInstance = new NullInstance(); + int INSTANCE_COUNT = 5; int UNKNOWN = -1; @@ -73,4 +75,18 @@ static void validateSelectorInstances( Object[] instances, int... aliveIndex ) } } } + + class NullInstance implements SlotSelector + { + @Override + public void validateSatisfied( IndexProvider[] instances ) + { // no-op + } + + @Override + public int selectSlot( V[] values, Function groupOf ) + { + throw new UnsupportedOperationException( "NullInstance cannot select a slot for you. Please use the real deal." ); + } + } } diff --git a/community/kernel/src/main/java/org/neo4j/storageengine/api/schema/IndexReader.java b/community/kernel/src/main/java/org/neo4j/storageengine/api/schema/IndexReader.java index 6c1322ea4bae1..fef6817bc67ff 100644 --- a/community/kernel/src/main/java/org/neo4j/storageengine/api/schema/IndexReader.java +++ b/community/kernel/src/main/java/org/neo4j/storageengine/api/schema/IndexReader.java @@ -128,4 +128,46 @@ public void distinctValues( IndexProgressor.NodeValueClient client, PropertyAcce // do nothing } }; + + class Adaptor implements IndexReader + { + @Override + public long countIndexedNodes( long nodeId, Value... propertyValues ) + { + return 0; + } + + @Override + public IndexSampler createSampler() + { + return null; + } + + @Override + public PrimitiveLongResourceIterator query( IndexQuery... predicates ) throws IndexNotApplicableKernelException + { + return null; + } + + @Override + public void query( IndexProgressor.NodeValueClient client, IndexOrder indexOrder, IndexQuery... query ) throws IndexNotApplicableKernelException + { + } + + @Override + public boolean hasFullValuePrecision( IndexQuery... predicates ) + { + return false; + } + + @Override + public void distinctValues( IndexProgressor.NodeValueClient client, PropertyAccessor propertyAccessor ) + { + } + + @Override + public void close() + { + } + } } diff --git a/community/kernel/src/main/java/org/neo4j/storageengine/api/schema/IndexSampler.java b/community/kernel/src/main/java/org/neo4j/storageengine/api/schema/IndexSampler.java index 946df736a21b4..fe3800c8f98ca 100644 --- a/community/kernel/src/main/java/org/neo4j/storageengine/api/schema/IndexSampler.java +++ b/community/kernel/src/main/java/org/neo4j/storageengine/api/schema/IndexSampler.java @@ -19,12 +19,14 @@ */ package org.neo4j.storageengine.api.schema; +import java.io.Closeable; + import org.neo4j.kernel.api.exceptions.index.IndexNotFoundKernelException; /** * Component able to sample schema index. */ -public interface IndexSampler +public interface IndexSampler extends Closeable { IndexSampler EMPTY = IndexSample::new; @@ -35,4 +37,9 @@ public interface IndexSampler * @throws IndexNotFoundKernelException if the index is dropped while sampling */ IndexSample sampleIndex() throws IndexNotFoundKernelException; + + @Override + default void close() + { // no-op + } } diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NativeSchemaIndexAccessorTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NativeSchemaIndexAccessorTest.java index 03728102b1514..09caf40de9813 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NativeSchemaIndexAccessorTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/NativeSchemaIndexAccessorTest.java @@ -592,10 +592,9 @@ public void shouldSampleIndex() throws Exception // given IndexEntryUpdate[] updates = layoutUtil.someUpdates(); processAll( updates ); - try ( IndexReader reader = accessor.newReader() ) + try ( IndexReader reader = accessor.newReader(); + IndexSampler sampler = reader.createSampler() ) { - IndexSampler sampler = reader.createSampler(); - // when IndexSample sample = sampler.sampleIndex(); diff --git a/community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/index/sampler/AggregatingIndexSampler.java b/community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/index/sampler/AggregatingIndexSampler.java index be193a1f31a7f..7a15530eb496e 100644 --- a/community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/index/sampler/AggregatingIndexSampler.java +++ b/community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/index/sampler/AggregatingIndexSampler.java @@ -21,6 +21,7 @@ import java.util.List; +import org.neo4j.io.IOUtils; import org.neo4j.kernel.api.exceptions.index.IndexNotFoundKernelException; import org.neo4j.storageengine.api.schema.IndexSample; import org.neo4j.storageengine.api.schema.IndexSampler; @@ -66,4 +67,10 @@ public IndexSample combine( IndexSample sample1, IndexSample sample2 ) long sampleSize = Math.addExact( sample1.sampleSize(), sample2.sampleSize() ); return new IndexSample( indexSize, uniqueValues, sampleSize ); } + + @Override + public void close() + { + IOUtils.closeAllSilently( indexSamplers ); + } } diff --git a/community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/schema/sampler/LuceneIndexSampler.java b/community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/schema/sampler/LuceneIndexSampler.java index c927c01f9be1c..68818a7e53488 100644 --- a/community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/schema/sampler/LuceneIndexSampler.java +++ b/community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/schema/sampler/LuceneIndexSampler.java @@ -22,7 +22,6 @@ import org.neo4j.helpers.TaskControl; import org.neo4j.helpers.TaskCoordinator; import org.neo4j.kernel.api.exceptions.index.IndexNotFoundKernelException; -import org.neo4j.storageengine.api.schema.IndexSample; import org.neo4j.storageengine.api.schema.IndexSampler; /** @@ -38,27 +37,6 @@ abstract class LuceneIndexSampler implements IndexSampler this.executionTicket = taskControl; } - /** - * Perform actual sampling. - * - * @return the index sampling result. - * @throws IndexNotFoundKernelException if the index is dropped while sampling. - */ - protected abstract IndexSample performSampling() throws IndexNotFoundKernelException; - - @Override - public final IndexSample sampleIndex() throws IndexNotFoundKernelException - { - try - { - return performSampling(); - } - finally - { - completeSampling(); - } - } - /** * Check if sampling was canceled. * @@ -72,7 +50,8 @@ void checkCancellation() throws IndexNotFoundKernelException } } - private void completeSampling() + @Override + public void close() { executionTicket.close(); } diff --git a/community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/schema/sampler/NonUniqueLuceneIndexSampler.java b/community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/schema/sampler/NonUniqueLuceneIndexSampler.java index 35cc1376a67da..61df66a665ecd 100644 --- a/community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/schema/sampler/NonUniqueLuceneIndexSampler.java +++ b/community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/schema/sampler/NonUniqueLuceneIndexSampler.java @@ -57,7 +57,7 @@ public NonUniqueLuceneIndexSampler( IndexSearcher indexSearcher, TaskControl tas } @Override - protected IndexSample performSampling() throws IndexNotFoundKernelException + public IndexSample sampleIndex() throws IndexNotFoundKernelException { NonUniqueIndexSampler sampler = new DefaultNonUniqueIndexSampler( indexSamplingConfig.sampleSizeLimit() ); IndexReader indexReader = indexSearcher.getIndexReader(); diff --git a/community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/schema/sampler/UniqueLuceneIndexSampler.java b/community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/schema/sampler/UniqueLuceneIndexSampler.java index f220023df622c..38ef4e6cf1777 100644 --- a/community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/schema/sampler/UniqueLuceneIndexSampler.java +++ b/community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/schema/sampler/UniqueLuceneIndexSampler.java @@ -41,7 +41,7 @@ public UniqueLuceneIndexSampler( IndexSearcher indexSearcher, TaskControl taskCo } @Override - protected IndexSample performSampling() throws IndexNotFoundKernelException + public IndexSample sampleIndex() throws IndexNotFoundKernelException { UniqueIndexSampler sampler = new UniqueIndexSampler(); sampler.increment( indexSearcher.getIndexReader().numDocs() ); diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/index/LuceneSchemaIndexPopulationIT.java b/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/index/LuceneSchemaIndexPopulationIT.java index a88969c46c616..65e5bd538afc8 100644 --- a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/index/LuceneSchemaIndexPopulationIT.java +++ b/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/index/LuceneSchemaIndexPopulationIT.java @@ -32,6 +32,7 @@ import java.util.List; import org.neo4j.collection.primitive.PrimitiveLongCollections; +import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.io.pagecache.IOLimiter; import org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException; import org.neo4j.kernel.api.impl.schema.LuceneIndexAccessor; @@ -39,7 +40,6 @@ import org.neo4j.kernel.api.impl.schema.SchemaIndex; import org.neo4j.kernel.api.index.IndexEntryUpdate; import org.neo4j.kernel.api.index.IndexUpdater; -import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.kernel.api.schema.index.SchemaIndexDescriptor; import org.neo4j.kernel.api.schema.index.SchemaIndexDescriptorFactory; import org.neo4j.kernel.configuration.Config; @@ -111,12 +111,12 @@ public void partitionedIndexPopulation() throws Exception // now index is online and should contain updates data assertTrue( uniqueIndex.isOnline() ); - try ( IndexReader indexReader = indexAccessor.newReader() ) + try ( IndexReader indexReader = indexAccessor.newReader(); + IndexSampler indexSampler = indexReader.createSampler() ) { long[] nodes = PrimitiveLongCollections.asArray( indexReader.query( IndexQuery.exists( 1 )) ); assertEquals( affectedNodes, nodes.length ); - IndexSampler indexSampler = indexReader.createSampler(); IndexSample sample = indexSampler.sampleIndex(); assertEquals( affectedNodes, sample.indexSize() ); assertEquals( affectedNodes, sample.uniqueValues() ); diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/DatabaseCompositeIndexAccessorTest.java b/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/DatabaseCompositeIndexAccessorTest.java index 5727f9632d2f7..94d996bcf0168 100644 --- a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/DatabaseCompositeIndexAccessorTest.java +++ b/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/DatabaseCompositeIndexAccessorTest.java @@ -38,13 +38,13 @@ import org.neo4j.collection.primitive.PrimitiveLongIterator; import org.neo4j.function.IOFunction; import org.neo4j.helpers.TaskCoordinator; +import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException; import org.neo4j.kernel.api.exceptions.index.IndexNotFoundKernelException; import org.neo4j.kernel.api.impl.index.storage.DirectoryFactory; import org.neo4j.kernel.api.index.IndexEntryUpdate; import org.neo4j.kernel.api.index.IndexQueryHelper; import org.neo4j.kernel.api.index.IndexUpdater; -import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.kernel.api.schema.index.SchemaIndexDescriptor; import org.neo4j.kernel.api.schema.index.SchemaIndexDescriptorFactory; import org.neo4j.kernel.configuration.Config; @@ -246,9 +246,10 @@ public void shouldStopSamplingWhenIndexIsDropped() throws Exception return nothing; }, null, waitingWhileIn( TaskCoordinator.class, "awaitCompletion" ), 3, SECONDS ); - try ( IndexReader reader = indexReader /* do not inline! */ ) + try ( IndexReader reader = indexReader /* do not inline! */; + IndexSampler sampler = indexSampler /* do not inline! */ ) { - indexSampler.sampleIndex(); + sampler.sampleIndex(); fail( "expected exception" ); } catch ( IndexNotFoundKernelException e ) diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/DatabaseIndexAccessorTest.java b/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/DatabaseIndexAccessorTest.java index 34d9c26c39a27..bc378df11ff4d 100644 --- a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/DatabaseIndexAccessorTest.java +++ b/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/DatabaseIndexAccessorTest.java @@ -38,13 +38,13 @@ import org.neo4j.collection.primitive.PrimitiveLongIterator; import org.neo4j.function.IOFunction; import org.neo4j.helpers.TaskCoordinator; +import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException; import org.neo4j.kernel.api.exceptions.index.IndexNotFoundKernelException; import org.neo4j.kernel.api.impl.index.storage.DirectoryFactory; import org.neo4j.kernel.api.index.IndexEntryUpdate; import org.neo4j.kernel.api.index.IndexQueryHelper; import org.neo4j.kernel.api.index.IndexUpdater; -import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.kernel.api.schema.index.SchemaIndexDescriptor; import org.neo4j.kernel.api.schema.index.SchemaIndexDescriptorFactory; import org.neo4j.kernel.configuration.Config; @@ -314,9 +314,10 @@ public void shouldStopSamplingWhenIndexIsDropped() throws Exception return nothing; }, null, waitingWhileIn( TaskCoordinator.class, "awaitCompletion" ), 3, SECONDS ); - try ( IndexReader reader = indexReader /* do not inline! */ ) + try ( IndexReader reader = indexReader /* do not inline! */; + IndexSampler sampler = indexSampler /* do not inline! */ ) { - indexSampler.sampleIndex(); + sampler.sampleIndex(); fail( "expected exception" ); } catch ( IndexNotFoundKernelException e ) diff --git a/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/sampler/LuceneIndexSamplerReleaseTaskControlUnderFusion.java b/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/sampler/LuceneIndexSamplerReleaseTaskControlUnderFusion.java new file mode 100644 index 0000000000000..a6f511d540a7a --- /dev/null +++ b/community/lucene-index/src/test/java/org/neo4j/kernel/api/impl/schema/sampler/LuceneIndexSamplerReleaseTaskControlUnderFusion.java @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2002-2019 "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.api.impl.schema.sampler; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.io.IOException; + +import org.neo4j.helpers.TaskCoordinator; +import org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException; +import org.neo4j.kernel.api.impl.index.storage.DirectoryFactory; +import org.neo4j.kernel.api.impl.schema.LuceneIndexProvider; +import org.neo4j.kernel.api.index.IndexAccessor; +import org.neo4j.kernel.api.index.IndexDirectoryStructure; +import org.neo4j.kernel.api.index.IndexEntryUpdate; +import org.neo4j.kernel.api.index.IndexProvider; +import org.neo4j.kernel.api.index.IndexUpdater; +import org.neo4j.kernel.api.schema.index.SchemaIndexDescriptor; +import org.neo4j.kernel.configuration.Config; +import org.neo4j.kernel.impl.api.index.IndexProxyAdapter; +import org.neo4j.kernel.impl.api.index.IndexStoreView; +import org.neo4j.kernel.impl.api.index.IndexUpdateMode; +import org.neo4j.kernel.impl.api.index.sampling.IndexSamplingConfig; +import org.neo4j.kernel.impl.api.index.sampling.IndexSamplingJob; +import org.neo4j.kernel.impl.api.index.sampling.OnlineIndexSamplingJobFactory; +import org.neo4j.kernel.impl.factory.OperationalMode; +import org.neo4j.kernel.impl.index.schema.fusion.FusionIndexProvider; +import org.neo4j.kernel.impl.index.schema.fusion.SlotSelector; +import org.neo4j.storageengine.api.schema.IndexReader; +import org.neo4j.storageengine.api.schema.IndexSampler; +import org.neo4j.test.rule.TestDirectory; +import org.neo4j.test.rule.fs.EphemeralFileSystemRule; +import org.neo4j.test.rule.fs.FileSystemRule; +import org.neo4j.values.storable.Values; + +import static org.junit.Assert.assertSame; +import static org.neo4j.kernel.api.impl.schema.LuceneIndexProvider.defaultDirectoryStructure; +import static org.neo4j.kernel.api.index.IndexProvider.EMPTY; +import static org.neo4j.kernel.api.schema.SchemaTestUtil.simpleNameLookup; +import static org.neo4j.kernel.api.schema.index.SchemaIndexDescriptorFactory.forLabel; +import static org.neo4j.logging.NullLogProvider.getInstance; + +public class LuceneIndexSamplerReleaseTaskControlUnderFusion +{ + @Rule + public FileSystemRule fs = new EphemeralFileSystemRule(); + + @Rule + public TestDirectory dir = TestDirectory.testDirectory( fs.get() ); + + private static final int indexId = 1; + private static final SchemaIndexDescriptor indexDescriptor = forLabel( 1, 1 ); + private static final IndexProvider.Descriptor providerDescriptor = IndexProvider.UNDECIDED; + private static final DirectoryFactory.InMemoryDirectoryFactory luceneDirectoryFactory = new DirectoryFactory.InMemoryDirectoryFactory(); + private static final Config config = Config.defaults(); + private static final IndexSamplingConfig samplingConfig = new IndexSamplingConfig( config ); + private static final RuntimeException sampleException = new RuntimeException( "Killroy messed with your index sample." ); + private IndexDirectoryStructure.Factory directoryFactory; + + @Before + public void setup() + { + directoryFactory = defaultDirectoryStructure( dir.graphDbDir() ); + } + + /** + * This test come from a support case where dropping an index would block forever after index sampling failed. + *

+ * A fusion index has multiple {@link IndexSampler index samplers} that are called sequentially. If one fails, then the other will never be invoked. + * This was a problem for {@link LuceneIndexSampler}. It owns a {@link org.neo4j.helpers.TaskControl} that it will try to release in try-finally + * in {@link LuceneIndexSampler#sampleIndex()}. But it never gets here because a prior {@link IndexSampler} fails. + *

+ * Because the {@link org.neo4j.helpers.TaskControl} was never released the lucene accessor would block forever, waiting for + * {@link TaskCoordinator#awaitCompletion()}. + *

+ * This situation was solved by making {@link IndexSampler} {@link java.io.Closeable} and include it in try-with-resource together with + * {@link IndexReader} that created it. + */ + @Test( timeout = 5_000L ) + public void failedIndexSamplingMustNotPreventIndexDrop() throws IOException, IndexEntryConflictException + { + LuceneIndexProvider luceneProvider = luceneProvider(); + makeSureIndexHasSomeData( luceneProvider ); // Otherwise no sampler will be created. + + IndexProvider failingProvider = failingProvider(); + FusionIndexProvider fusionProvider = createFusionProvider( luceneProvider, failingProvider ); + try ( IndexAccessor fusionAccessor = fusionProvider.getOnlineAccessor( indexId, indexDescriptor, samplingConfig ) ) + { + IndexSamplingJob indexSamplingJob = createIndexSamplingJob( fusionAccessor ); + + // Call run from other thread + try + { + indexSamplingJob.run(); + } + catch ( RuntimeException e ) + { + assertSame( e, sampleException ); + } + + // then + fusionAccessor.drop(); + // should not block forever + } + } + + private void makeSureIndexHasSomeData( IndexProvider provider ) throws IOException, IndexEntryConflictException + { + try ( IndexAccessor accessor = provider.getOnlineAccessor( indexId, indexDescriptor, samplingConfig ); + IndexUpdater updater = accessor.newUpdater( IndexUpdateMode.ONLINE ) ) + { + updater.process( IndexEntryUpdate.add( 1, indexDescriptor, Values.of( "some string" ) ) ); + } + } + + private FusionIndexProvider createFusionProvider( LuceneIndexProvider luceneProvider, IndexProvider failingProvider ) + { + SlotSelector slotSelector = SlotSelector.nullInstance; + return new FusionIndexProvider( failingProvider, EMPTY, EMPTY, EMPTY, luceneProvider, slotSelector, providerDescriptor, 1, directoryFactory, fs.get(), + false ); + } + + private IndexSamplingJob createIndexSamplingJob( IndexAccessor fusionAccessor ) + { + IndexStoreView storeView = IndexStoreView.EMPTY; + IndexProxyAdapter indexProxy = new IndexProxyAdapter() + { + @Override + public SchemaIndexDescriptor getDescriptor() + { + return indexDescriptor; + } + + @Override + public IndexReader newReader() + { + return fusionAccessor.newReader(); + } + }; + OnlineIndexSamplingJobFactory onlineIndexSamplingJobFactory = + new OnlineIndexSamplingJobFactory( storeView, simpleNameLookup, getInstance() ); + return onlineIndexSamplingJobFactory.create( 1, indexProxy ); + } + + private LuceneIndexProvider luceneProvider() + { + return new LuceneIndexProvider( fs.get(), luceneDirectoryFactory, directoryFactory, IndexProvider.Monitor.EMPTY, config, OperationalMode.single ); + } + + /** + * @return an {@link IndexProvider} that create an {@link IndexAccessor} that create an {@link IndexReader} that create an {@link IndexSampler} that + * throws exception... yeah. + */ + private IndexProvider failingProvider() + { + return new IndexProvider.Adaptor( providerDescriptor, 1, directoryFactory ) + { + @Override + public IndexAccessor getOnlineAccessor( long indexId, SchemaIndexDescriptor descriptor, IndexSamplingConfig samplingConfig ) + { + return failingIndexAccessor(); + } + }; + } + + private IndexAccessor failingIndexAccessor() + { + return new IndexAccessor.Adapter() + { + @Override + public IndexReader newReader() + { + return new IndexReader.Adaptor() + { + @Override + public IndexSampler createSampler() + { + return () -> { + throw sampleException; + }; + } + }; + } + }; + } +}