From 88020d30bebbaab95d51ebc467ecdbbbcc6ec714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Finn=C3=A9?= Date: Fri, 20 Apr 2018 10:27:38 +0200 Subject: [PATCH] Fusion layer instantiates parts lazily Instead of always instantiating eagerly. E.g. an IndexReader would previously instantiate sub-readers for lucene, number, string, spatial and temporal even if it would only need one of those. Likewise they would all be closed afterwards. This proved to be an unnecessary overhead. --- .../impl/index/schema/SpatialIndexReader.java | 2 - .../index/schema/TemporalIndexReader.java | 2 - .../index/schema/TemporalIndexUpdater.java | 5 +- .../schema/fusion/FusionIndexAccessor.java | 45 +++--- .../index/schema/fusion/FusionIndexBase.java | 129 +++++++++++------- .../schema/fusion/FusionIndexPopulator.java | 46 +++---- .../schema/fusion/FusionIndexProvider.java | 65 ++++----- .../schema/fusion/FusionIndexReader.java | 48 +++---- .../schema/fusion/FusionIndexUpdater.java | 15 +- .../index/schema/fusion/FusionSelector00.java | 46 ++----- .../index/schema/fusion/FusionSelector10.java | 47 ++----- .../index/schema/fusion/FusionSelector20.java | 50 ++----- .../impl/index/schema/fusion/Selector.java | 73 ++++++++++ .../index/schema/fusion/SlotSelector.java | 53 +++++++ .../schema/fusion/ThrowingIntFunction.java | 33 +++++ .../fusion/FusionIndexAccessorTest.java | 16 ++- .../fusion/FusionIndexPopulatorTest.java | 16 +-- .../fusion/FusionIndexProviderTest.java | 26 ++-- .../schema/fusion/FusionIndexReaderTest.java | 15 +- .../schema/fusion/FusionIndexUpdaterTest.java | 14 +- .../index/schema/fusion/FusionVersion.java | 18 +-- 21 files changed, 423 insertions(+), 341 deletions(-) create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/Selector.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/SlotSelector.java create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/ThrowingIntFunction.java diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/SpatialIndexReader.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/SpatialIndexReader.java index 3cf12ba1277aa..732634fd367c3 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/SpatialIndexReader.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/SpatialIndexReader.java @@ -19,8 +19,6 @@ */ package org.neo4j.kernel.impl.index.schema; -import java.io.IOException; - import org.neo4j.collection.primitive.PrimitiveLongResourceIterator; import org.neo4j.graphdb.Resource; import org.neo4j.helpers.collection.Iterators; diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/TemporalIndexReader.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/TemporalIndexReader.java index 5b2f45b32352a..252e1c61843a3 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/TemporalIndexReader.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/TemporalIndexReader.java @@ -19,8 +19,6 @@ */ package org.neo4j.kernel.impl.index.schema; -import java.io.IOException; - import org.neo4j.collection.primitive.PrimitiveLongResourceIterator; import org.neo4j.graphdb.Resource; import org.neo4j.helpers.collection.Iterators; diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/TemporalIndexUpdater.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/TemporalIndexUpdater.java index c9704453bfc61..059323b8ecb6a 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/TemporalIndexUpdater.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/TemporalIndexUpdater.java @@ -25,9 +25,10 @@ import org.neo4j.kernel.api.index.IndexEntryUpdate; import org.neo4j.kernel.api.index.IndexUpdater; import org.neo4j.kernel.impl.api.index.IndexUpdateMode; -import org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase; import org.neo4j.values.storable.ValueGroup; +import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.forAll; + public class TemporalIndexUpdater extends TemporalIndexCache> implements IndexUpdater { TemporalIndexUpdater( TemporalIndexAccessor accessor, IndexUpdateMode mode ) @@ -68,7 +69,7 @@ public void process( IndexEntryUpdate update ) throws IOException, IndexEntry @Override public void close() throws IOException { - FusionIndexBase.forAll( NativeSchemaIndexUpdater::close, this ); + forAll( NativeSchemaIndexUpdater::close, this ); } static class PartFactory implements TemporalIndexCache.Factory> diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexAccessor.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexAccessor.java index 61222aa32fd0b..5168f0f27a71a 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexAccessor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexAccessor.java @@ -21,9 +21,7 @@ import java.io.File; import java.io.IOException; -import java.util.ArrayList; import java.util.Iterator; -import java.util.List; import org.neo4j.graphdb.ResourceIterator; import org.neo4j.helpers.collection.BoundedIterable; @@ -36,12 +34,12 @@ import org.neo4j.kernel.api.schema.index.SchemaIndexDescriptor; import org.neo4j.kernel.impl.api.index.IndexUpdateMode; import org.neo4j.kernel.impl.index.schema.fusion.FusionIndexProvider.DropAction; -import org.neo4j.kernel.impl.index.schema.fusion.FusionIndexProvider.Selector; import org.neo4j.storageengine.api.schema.IndexReader; import org.neo4j.values.storable.Value; -import static java.util.Arrays.stream; import static org.neo4j.helpers.collection.Iterators.concatResourceIterators; +import static org.neo4j.helpers.collection.Iterators.iterator; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.INSTANCE_COUNT; class FusionIndexAccessor extends FusionIndexBase implements IndexAccessor { @@ -49,13 +47,13 @@ class FusionIndexAccessor extends FusionIndexBase implements Inde private final SchemaIndexDescriptor descriptor; private final DropAction dropAction; - FusionIndexAccessor( IndexAccessor[] accessors, - Selector selector, + FusionIndexAccessor( SlotSelector slotSelector, + Selector selector, long indexId, SchemaIndexDescriptor descriptor, DropAction dropAction ) { - super( accessors, selector ); + super( slotSelector, selector ); this.indexId = indexId; this.descriptor = descriptor; this.dropAction = dropAction; @@ -64,44 +62,46 @@ class FusionIndexAccessor extends FusionIndexBase implements Inde @Override public void drop() throws IOException { - forAll( IndexAccessor::drop, instances ); + forAll( IndexAccessor::drop, selector ); dropAction.drop( indexId ); } @Override public IndexUpdater newUpdater( IndexUpdateMode mode ) { - return new FusionIndexUpdater( instancesAs( IndexUpdater.class, accessor -> accessor.newUpdater( mode ) ), selector ); + Selector updaterSelector = new Selector<>( new IndexUpdater[INSTANCE_COUNT], slot -> selector.select( slot ).newUpdater( mode ) ); + return new FusionIndexUpdater( slotSelector, updaterSelector ); } @Override public void force( IOLimiter ioLimiter ) throws IOException { - forAll( accessor -> accessor.force( ioLimiter ), instances ); + forAll( accessor -> accessor.force( ioLimiter ), selector ); } @Override public void refresh() throws IOException { - forAll( IndexAccessor::refresh, instances ); + forAll( IndexAccessor::refresh, selector ); } @Override public void close() throws IOException { - forAll( IndexAccessor::close, instances ); + forAll( IndexAccessor::close, selector ); } @Override public IndexReader newReader() { - return new FusionIndexReader( instancesAs( IndexReader.class, IndexAccessor::newReader ), selector, descriptor ); + Selector readerSelector = new Selector<>( new IndexReader[INSTANCE_COUNT], slot -> selector.select( slot ).newReader() ); + return new FusionIndexReader( slotSelector, readerSelector, descriptor ); } @Override public BoundedIterable newAllEntriesReader() { - BoundedIterable[] entries = instancesAs( BoundedIterable.class, IndexAccessor::newAllEntriesReader ); + BoundedIterable[] entries = instancesAs( new BoundedIterable[INSTANCE_COUNT], IndexAccessor::newAllEntriesReader ); return new BoundedIterable() { @Override @@ -147,30 +147,33 @@ public Iterator iterator() @Override public ResourceIterator snapshotFiles() throws IOException { - List> snapshots = new ArrayList<>(); - forAll( accessor -> snapshots.add( accessor.snapshotFiles() ), instances ); - return concatResourceIterators( snapshots.iterator() ); + return concatResourceIterators( iterator( instancesAs( new ResourceIterator[INSTANCE_COUNT], accessor -> accessor.snapshotFiles() ) ) ); } @Override public void verifyDeferredConstraints( PropertyAccessor propertyAccessor ) throws IndexEntryConflictException, IOException { - for ( IndexAccessor accessor : instances ) + for ( int slot = 0; slot < INSTANCE_COUNT; slot++ ) { - accessor.verifyDeferredConstraints( propertyAccessor ); + selector.select( slot ).verifyDeferredConstraints( propertyAccessor ); } } @Override public boolean isDirty() { - return stream( instances ).anyMatch( IndexAccessor::isDirty ); + boolean isDirty = false; + for ( int slot = 0; slot < INSTANCE_COUNT; slot++ ) + { + isDirty |= selector.select( slot ).isDirty(); + } + return isDirty; } @Override public void validateBeforeCommit( Value[] tuple ) { - selector.select( instances, tuple ).validateBeforeCommit( tuple ); + selector.select( slotSelector.selectSlot( tuple, GROUP_OF ) ).validateBeforeCommit( tuple ); } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexBase.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexBase.java index be05d66df1917..512184233e943 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexBase.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexBase.java @@ -19,14 +19,18 @@ */ package org.neo4j.kernel.impl.index.schema.fusion; -import java.lang.reflect.Array; import java.util.Arrays; +import java.util.function.Function; import org.neo4j.collection.primitive.PrimitiveIntCollections; import org.neo4j.function.ThrowingConsumer; import org.neo4j.function.ThrowingFunction; import org.neo4j.helpers.Exceptions; import org.neo4j.kernel.api.index.IndexProvider; +import org.neo4j.values.storable.Value; +import org.neo4j.values.storable.ValueGroup; + +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.INSTANCE_COUNT; /** * Acting as a simplifier for the multiplexing that is going in inside a fusion index. A fusion index consists of multiple parts, @@ -37,42 +41,80 @@ */ public abstract class FusionIndexBase { - static final int INSTANCE_COUNT = 5; - - static final int STRING = 0; - static final int NUMBER = 1; - static final int SPATIAL = 2; - static final int TEMPORAL = 3; - static final int LUCENE = 4; + static Function GROUP_OF = Value::valueGroup; - final T[] instances; - final FusionIndexProvider.Selector selector; + final SlotSelector slotSelector; + final Selector selector; - FusionIndexBase( T[] instances, FusionIndexProvider.Selector selector ) + FusionIndexBase( SlotSelector slotSelector, Selector selector ) { - assert instances.length == INSTANCE_COUNT; - this.instances = instances; + this.slotSelector = slotSelector; this.selector = selector; } - R[] instancesAs( Class cls, ThrowingFunction converter ) throws E + /** + * Short-hand for calling the static {@link #instancesAs(Selector, Object[], ThrowingFunction)}, here with the local {@link #selector}. + */ + R[] instancesAs( R[] target, ThrowingFunction converter ) throws E { - return instancesAs( instances, cls, converter ); + return instancesAs( selector, target, converter ); } - static R[] instancesAs( T[] instances, Class cls, ThrowingFunction converter ) throws E + /** + * Convenience method typically for calling a method on each of the sub-parts of a fusion entity, + * one which creates another instance. All those instances are returned as an array, or actually put into an array + * created by the caller to avoid reflection to instantiate the array. + * + * @param selector {@link Selector} to use as the source. + * @param target array to put the created instances into, also returned. + * @param converter {@link ThrowingFunction} which converts from the source to target instance. + * @param type of source instance. + * @param type of target instance. + * @param type of exception that converter may throw. + * @return the target array which was passed in, now populated. + * @throws E exception from converter. + */ + static T[] instancesAs( Selector selector, T[] target, ThrowingFunction converter ) throws E { - R[] result = (R[]) Array.newInstance( cls, instances.length ); - for ( int i = 0; i < instances.length; i++ ) + for ( int slot = 0; slot < INSTANCE_COUNT; slot++ ) { - result[i] = converter.apply( instances[i] ); + target[slot] = converter.apply( selector.select( slot ) ); + } + return target; + } + + static void forInstantiated( ThrowingConsumer consumer, Selector selector ) throws E + { + E exception = null; + for ( int slot = 0; slot < INSTANCE_COUNT; slot++ ) + { + T instance = selector.getIfInstantiated( slot ); + if ( instance != null ) + { + exception = consume( exception, consumer, instance ); + } + } + if ( exception != null ) + { + throw exception; + } + } + + public static void forAll( ThrowingConsumer consumer, Selector selector ) throws E + { + E exception = null; + for ( int slot = 0; slot < INSTANCE_COUNT; slot++ ) + { + exception = consume( exception, consumer, selector.select( slot ) ); + } + if ( exception != null ) + { + throw exception; } - return result; } /** - * NOTE: duplicate of {@link #forAll(ThrowingConsumer, Iterable)} to avoid having to wrap subjects of one form into another. - * There are some real use cases for passing in an array instead of {@link Iterable} out there... + * See {@link #forAll(ThrowingConsumer, Object[])} * * Method for calling a lambda function on many objects when it is expected that the function might * throw an exception. First exception will be thrown and subsequent will be suppressed. @@ -90,21 +132,12 @@ static R[] instancesAs( T[] instances, Class cls, T * @param the type of exception anticipated, inferred from the lambda * @throws E if consumption fails with this exception */ - @SafeVarargs - public static void forAll( ThrowingConsumer consumer, T... subjects ) throws E + public static void forAll( ThrowingConsumer consumer, Iterable subjects ) throws E { - // Duplicate this method for array to avoid creating a purely internal list to shove that in to the other method. E exception = null; - for ( T subject : subjects ) + for ( T instance : subjects ) { - try - { - consumer.accept( subject ); - } - catch ( Exception e ) - { - exception = Exceptions.chain( exception, (E) e ); - } + exception = consume( exception, consumer, instance ); } if ( exception != null ) { @@ -113,10 +146,6 @@ public static void forAll( ThrowingConsumer consum } /** - * See {@link #forAll(ThrowingConsumer, Object[])} - * NOTE: duplicate of {@link #forAll(ThrowingConsumer, Object[])} to avoid having to wrap subjects of one form into another. - * There are some real use cases for passing in an Iterable instead of array out there... - * * Method for calling a lambda function on many objects when it is expected that the function might * throw an exception. First exception will be thrown and subsequent will be suppressed. * @@ -133,24 +162,22 @@ public static void forAll( ThrowingConsumer consum * @param the type of exception anticipated, inferred from the lambda * @throws E if consumption fails with this exception */ - public static void forAll( ThrowingConsumer consumer, Iterable subjects ) throws E + public static void forAll( ThrowingConsumer consumer, T[] subjects ) throws E { - E exception = null; - for ( T subject : subjects ) + forAll( consumer, Arrays.asList( subjects ) ); + } + + private static E consume( E exception, ThrowingConsumer consumer, T instance ) + { + try { - try - { - consumer.accept( subject ); - } - catch ( Exception e ) - { - exception = Exceptions.chain( exception, (E) e ); - } + consumer.accept( instance ); } - if ( exception != null ) + catch ( Exception e ) { - throw exception; + exception = Exceptions.chain( exception, (E) e ); } + return exception; } static void validateSelectorInstances( Object[] instances, int... aliveIndex ) diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexPopulator.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexPopulator.java index da379363db93e..e13fc6b7f6394 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexPopulator.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexPopulator.java @@ -29,10 +29,10 @@ import org.neo4j.kernel.api.index.IndexUpdater; import org.neo4j.kernel.api.index.PropertyAccessor; import org.neo4j.kernel.impl.index.schema.fusion.FusionIndexProvider.DropAction; -import org.neo4j.kernel.impl.index.schema.fusion.FusionIndexProvider.Selector; import org.neo4j.storageengine.api.schema.IndexSample; import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexSampler.combineSamples; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.INSTANCE_COUNT; class FusionIndexPopulator extends FusionIndexBase implements IndexPopulator { @@ -40,9 +40,9 @@ class FusionIndexPopulator extends FusionIndexBase implements In private final DropAction dropAction; private final boolean archiveFailedIndex; - FusionIndexPopulator( IndexPopulator[] populators, Selector selector, long indexId, DropAction dropAction, boolean archiveFailedIndex ) + FusionIndexPopulator( SlotSelector slotSelector, Selector selector, long indexId, DropAction dropAction, boolean archiveFailedIndex ) { - super( populators, selector ); + super( slotSelector, selector ); this.indexId = indexId; this.dropAction = dropAction; this.archiveFailedIndex = archiveFailedIndex; @@ -52,37 +52,32 @@ class FusionIndexPopulator extends FusionIndexBase implements In public void create() throws IOException { dropAction.drop( indexId, archiveFailedIndex ); - forAll( IndexPopulator::create, instances ); + forAll( IndexPopulator::create, selector ); } @Override public void drop() throws IOException { - forAll( IndexPopulator::drop, instances ); + forAll( IndexPopulator::drop, selector ); dropAction.drop( indexId ); } @Override public void add( Collection> updates ) throws IndexEntryConflictException, IOException { - Collection>[] batches = new Collection[instances.length]; + Selector>> batchSelector = new Selector<>( new Collection[INSTANCE_COUNT], slot -> new ArrayList<>() ); for ( IndexEntryUpdate update : updates ) { - int slot = selector.selectSlot( update.values() ); - Collection> batch = batches[slot]; - if ( batch == null ) - { - batch = new ArrayList<>(); - batches[slot] = batch; - } - batch.add( update ); + batchSelector.select( slotSelector.selectSlot( update.values(), GROUP_OF ) ).add( update ); } - for ( int i = 0; i < instances.length; i++ ) + // Manual loop due do multiple exception types + for ( int slot = 0; slot < INSTANCE_COUNT; slot++ ) { - if ( batches[i] != null ) + Collection> batch = batchSelector.getIfInstantiated( slot ); + if ( batch != null ) { - instances[i].add( batches[i] ); + this.selector.select( slot ).add( batch ); } } } @@ -91,39 +86,42 @@ public void add( Collection> updates ) throws Inde public void verifyDeferredConstraints( PropertyAccessor propertyAccessor ) throws IndexEntryConflictException, IOException { - for ( IndexPopulator populator : instances ) + // Manual loop due do multiple exception types + for ( int slot = 0; slot < INSTANCE_COUNT; slot++ ) { - populator.verifyDeferredConstraints( propertyAccessor ); + selector.select( slot ).verifyDeferredConstraints( propertyAccessor ); } } @Override public IndexUpdater newPopulatingUpdater( PropertyAccessor accessor ) throws IOException { - return new FusionIndexUpdater( instancesAs( IndexUpdater.class, populator -> populator.newPopulatingUpdater( accessor ) ), selector ); + Selector updaterSelector = + new Selector<>( new IndexUpdater[INSTANCE_COUNT], slot -> selector.select( slot ).newPopulatingUpdater( accessor ) ); + return new FusionIndexUpdater( slotSelector, updaterSelector ); } @Override public void close( boolean populationCompletedSuccessfully ) throws IOException { - forAll( populator -> populator.close( populationCompletedSuccessfully ), instances ); + forAll( populator -> populator.close( populationCompletedSuccessfully ), selector ); } @Override public void markAsFailed( String failure ) throws IOException { - forAll( populator -> populator.markAsFailed( failure ), instances ); + forAll( populator -> populator.markAsFailed( failure ), selector ); } @Override public void includeSample( IndexEntryUpdate update ) { - selector.select( instances, update.values() ).includeSample( update ); + selector.select( slotSelector.selectSlot( update.values(), GROUP_OF ) ).includeSample( update ); } @Override public IndexSample sampleResult() { - return combineSamples( instancesAs( IndexSample.class, IndexPopulator::sampleResult ) ); + return combineSamples( instancesAs( new IndexSample[INSTANCE_COUNT], IndexPopulator::sampleResult ) ); } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexProvider.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexProvider.java index 1b630b96b5ee7..8caf89013597e 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexProvider.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexProvider.java @@ -26,7 +26,6 @@ import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.internal.kernel.api.IndexCapability; import org.neo4j.internal.kernel.api.IndexOrder; -import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.internal.kernel.api.InternalIndexState; import org.neo4j.io.compress.ZipUtils; import org.neo4j.io.fs.FileSystemAbstraction; @@ -40,20 +39,18 @@ import org.neo4j.kernel.impl.api.index.sampling.IndexSamplingConfig; import org.neo4j.kernel.impl.newapi.UnionIndexCapability; import org.neo4j.kernel.impl.storemigration.StoreMigrationParticipant; -import org.neo4j.storageengine.api.schema.IndexReader; -import org.neo4j.values.storable.Value; import org.neo4j.values.storable.ValueCategory; import static org.neo4j.internal.kernel.api.InternalIndexState.FAILED; import static org.neo4j.internal.kernel.api.InternalIndexState.POPULATING; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.INSTANCE_COUNT; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.LUCENE; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.NUMBER; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.SPATIAL; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.STRING; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.TEMPORAL; import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.forAll; import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.instancesAs; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.INSTANCE_COUNT; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.LUCENE; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.NUMBER; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.SPATIAL; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.STRING; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.TEMPORAL; /** * This {@link IndexProvider index provider} act as one logical index but is backed by four physical @@ -61,26 +58,11 @@ */ public class FusionIndexProvider extends IndexProvider { - interface Selector - { - void validateSatisfied( Object[] instances ); - - int selectSlot( Value... values ); - - default T select( T[] instances, Value... values ) - { - return instances[selectSlot( values )]; - } - - /** - * @return Appropriate IndexReader for given predicate or null if predicate needs all readers. - */ - IndexReader select( IndexReader[] instances, IndexQuery... predicates ); - } private final boolean archiveFailedIndex; private final IndexProvider[] providers = new IndexProvider[INSTANCE_COUNT]; - private final Selector selector; + private final Selector selector; + private final SlotSelector slotSelector; private final DropAction dropAction; public FusionIndexProvider( @@ -90,7 +72,7 @@ public FusionIndexProvider( IndexProvider spatialProvider, IndexProvider temporalProvider, IndexProvider luceneProvider, - Selector selector, + SlotSelector slotSelector, Descriptor descriptor, int priority, IndexDirectoryStructure.Factory directoryStructure, @@ -99,9 +81,10 @@ public FusionIndexProvider( { super( descriptor, priority, directoryStructure ); fillProvidersArray( stringProvider, numberProvider, spatialProvider, temporalProvider, luceneProvider ); - selector.validateSatisfied( providers ); + slotSelector.validateSatisfied( providers ); this.archiveFailedIndex = archiveFailedIndex; - this.selector = selector; + this.slotSelector = slotSelector; + this.selector = new Selector<>( providers ); this.dropAction = new FileSystemDropAction( fs, directoryStructure() ); } @@ -118,24 +101,25 @@ private void fillProvidersArray( IndexProvider stringProvider, IndexProvider num @Override public IndexPopulator getPopulator( long indexId, SchemaIndexDescriptor descriptor, IndexSamplingConfig samplingConfig ) { - return new FusionIndexPopulator( instancesAs( providers, IndexPopulator.class, - provider -> provider.getPopulator( indexId, descriptor, samplingConfig ) ), selector, indexId, dropAction, archiveFailedIndex ); + IndexPopulator[] populators = + instancesAs( selector, new IndexPopulator[INSTANCE_COUNT], provider -> provider.getPopulator( indexId, descriptor, samplingConfig ) ); + return new FusionIndexPopulator( slotSelector, new Selector<>( populators ), indexId, dropAction, archiveFailedIndex ); } @Override public IndexAccessor getOnlineAccessor( long indexId, SchemaIndexDescriptor descriptor, IndexSamplingConfig samplingConfig ) throws IOException { - return new FusionIndexAccessor( - instancesAs( providers, IndexAccessor.class, provider -> provider.getOnlineAccessor( indexId, descriptor, samplingConfig ) ), - selector, indexId, descriptor, dropAction ); + IndexAccessor[] accessors = + instancesAs( selector, new IndexAccessor[INSTANCE_COUNT], provider -> provider.getOnlineAccessor( indexId, descriptor, samplingConfig ) ); + return new FusionIndexAccessor( slotSelector, new Selector<>( accessors ), indexId, descriptor, dropAction ); } @Override public String getPopulationFailure( long indexId, SchemaIndexDescriptor descriptor ) throws IllegalStateException { StringBuilder builder = new StringBuilder(); - forAll( p -> writeFailure( p.getClass().getSimpleName(), builder, p, indexId, descriptor ), providers ); + forAll( p -> writeFailure( p.getClass().getSimpleName(), builder, p, indexId, descriptor ), selector ); String failure = builder.toString(); if ( !failure.isEmpty() ) { @@ -162,7 +146,7 @@ private void writeFailure( String indexName, StringBuilder builder, IndexProvide @Override public InternalIndexState getInitialState( long indexId, SchemaIndexDescriptor descriptor ) { - InternalIndexState[] states = FusionIndexBase.instancesAs( providers, InternalIndexState.class, p -> p.getInitialState( indexId, descriptor ) ); + InternalIndexState[] states = instancesAs( selector, new InternalIndexState[INSTANCE_COUNT], p -> p.getInitialState( indexId, descriptor ) ); if ( Arrays.stream( states ).anyMatch( state -> state == FAILED ) ) { // One of the state is FAILED, the whole state must be considered FAILED @@ -180,11 +164,8 @@ public InternalIndexState getInitialState( long indexId, SchemaIndexDescriptor d @Override public IndexCapability getCapability( SchemaIndexDescriptor schemaIndexDescriptor ) { - IndexCapability[] capabilities = new IndexCapability[providers.length]; - for ( int i = 0; i < providers.length; i++ ) - { - capabilities[i] = providers[i].getCapability( schemaIndexDescriptor ); - } + IndexCapability[] capabilities = + instancesAs( selector, new IndexCapability[providers.length], provider -> provider.getCapability( schemaIndexDescriptor ) ); return new UnionIndexCapability( capabilities ) { @Override @@ -193,7 +174,7 @@ public IndexOrder[] orderCapability( ValueCategory... valueCategories ) // No order capability when combining results from different indexes if ( valueCategories.length == 1 && valueCategories[0] == ValueCategory.UNKNOWN ) { - return new IndexOrder[0]; + return ORDER_NONE; } // Otherwise union of capabilities return super.orderCapability( valueCategories ); diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexReader.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexReader.java index d196b7b5edfaa..67ffce65aea82 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexReader.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexReader.java @@ -21,7 +21,6 @@ import java.util.Arrays; -import org.neo4j.collection.primitive.PrimitiveLongResourceCollections; import org.neo4j.collection.primitive.PrimitiveLongResourceIterator; import org.neo4j.graphdb.Resource; import org.neo4j.internal.kernel.api.IndexOrder; @@ -30,65 +29,60 @@ import org.neo4j.kernel.api.exceptions.index.IndexNotApplicableKernelException; import org.neo4j.kernel.api.schema.index.SchemaIndexDescriptor; import org.neo4j.kernel.impl.api.schema.BridgingIndexProgressor; -import org.neo4j.kernel.impl.index.schema.fusion.FusionIndexProvider.Selector; 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; import static java.lang.String.format; +import static org.neo4j.collection.primitive.PrimitiveLongResourceCollections.concat; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.INSTANCE_COUNT; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.UNKNOWN; class FusionIndexReader extends FusionIndexBase implements IndexReader { private final SchemaIndexDescriptor descriptor; - FusionIndexReader( IndexReader[] readers, Selector selector, SchemaIndexDescriptor descriptor ) + FusionIndexReader( SlotSelector slotSelector, Selector selector, SchemaIndexDescriptor descriptor ) { - super( readers, selector ); + super( slotSelector, selector ); this.descriptor = descriptor; } @Override public void close() { - forAll( Resource::close, instances ); + forInstantiated( Resource::close, selector ); } @Override public long countIndexedNodes( long nodeId, Value... propertyValues ) { - return selector.select( instances, propertyValues ).countIndexedNodes( nodeId, propertyValues ); + return selector.select( slotSelector.selectSlot( propertyValues, GROUP_OF ) ).countIndexedNodes( nodeId, propertyValues ); } @Override public IndexSampler createSampler() { - return new FusionIndexSampler( instancesAs( IndexSampler.class, IndexReader::createSampler ) ); + return new FusionIndexSampler( instancesAs( new IndexSampler[INSTANCE_COUNT], IndexReader::createSampler ) ); } @Override public PrimitiveLongResourceIterator query( IndexQuery... predicates ) throws IndexNotApplicableKernelException { - IndexReader instance = selector.select( instances, predicates ); - if ( instance != null ) - { - return instance.query( predicates ); - } - else - { - PrimitiveLongResourceIterator[] converted = instancesAs( PrimitiveLongResourceIterator.class, reader -> reader.query( predicates ) ); - return PrimitiveLongResourceCollections.concat( converted ); - } + int slot = slotSelector.selectSlot( predicates, IndexQuery::valueGroup ); + return slot != UNKNOWN ? selector.select( slot ).query( predicates ) + : concat( instancesAs( new PrimitiveLongResourceIterator[INSTANCE_COUNT], reader -> reader.query( predicates ) ) ); } @Override public void query( IndexProgressor.NodeValueClient cursor, IndexOrder indexOrder, IndexQuery... predicates ) throws IndexNotApplicableKernelException { - IndexReader instance = selector.select( instances, predicates ); - if ( instance != null ) + int slot = slotSelector.selectSlot( predicates, IndexQuery::valueGroup ); + if ( slot != UNKNOWN ) { - instance.query( cursor, indexOrder, predicates ); + selector.select( slot ).query( cursor, indexOrder, predicates ); } else { @@ -101,18 +95,19 @@ public void query( IndexProgressor.NodeValueClient cursor, IndexOrder indexOrder BridgingIndexProgressor multiProgressor = new BridgingIndexProgressor( cursor, descriptor.schema().getPropertyIds() ); cursor.initialize( descriptor, multiProgressor, predicates ); - for ( IndexReader reader : instances ) - { - reader.query( multiProgressor, indexOrder, predicates ); - } + forAll( reader -> reader.query( multiProgressor, indexOrder, predicates ), selector ); } } @Override public boolean hasFullValuePrecision( IndexQuery... predicates ) { - IndexReader instance = selector.select( instances, predicates ); - if ( instance == null ) + int slot = slotSelector.selectSlot( predicates, IndexQuery::valueGroup ); + if ( slot != UNKNOWN ) + { + return selector.select( slot ).hasFullValuePrecision( predicates ); + } + else { if ( !(predicates.length == 1 && predicates[0] instanceof ExistsPredicate) ) { @@ -122,6 +117,5 @@ public boolean hasFullValuePrecision( IndexQuery... predicates ) // full value precision for that, therefor true. return true; } - return instance.hasFullValuePrecision( predicates ); } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexUpdater.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexUpdater.java index 3592ce5b2e76f..68d7b7442d8b9 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexUpdater.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexUpdater.java @@ -24,13 +24,12 @@ import org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException; import org.neo4j.kernel.api.index.IndexEntryUpdate; import org.neo4j.kernel.api.index.IndexUpdater; -import org.neo4j.kernel.impl.index.schema.fusion.FusionIndexProvider.Selector; class FusionIndexUpdater extends FusionIndexBase implements IndexUpdater { - FusionIndexUpdater( IndexUpdater[] updaters, Selector selector ) + FusionIndexUpdater( SlotSelector slotSelector, Selector selector ) { - super( updaters, selector ); + super( slotSelector, selector ); } @Override @@ -39,14 +38,14 @@ public void process( IndexEntryUpdate update ) throws IOException, IndexEntry switch ( update.updateMode() ) { case ADDED: - selector.select( instances, update.values() ).process( update ); + selector.select( slotSelector.selectSlot( update.values(), GROUP_OF ) ).process( update ); break; case CHANGED: // Hmm, here's a little conundrum. What if we change from a value that goes into native // to a value that goes into fallback, or vice versa? We also don't want to blindly pass // all CHANGED updates to both updaters since not all values will work in them. - IndexUpdater from = selector.select( instances, update.beforeValues() ); - IndexUpdater to = selector.select( instances, update.values() ); + IndexUpdater from = selector.select( slotSelector.selectSlot( update.beforeValues(), GROUP_OF ) ); + IndexUpdater to = selector.select( slotSelector.selectSlot( update.values(), GROUP_OF ) ); // There are two cases: // - both before/after go into the same updater --> pass update into that updater if ( from == to ) @@ -61,7 +60,7 @@ public void process( IndexEntryUpdate update ) throws IOException, IndexEntry } break; case REMOVED: - selector.select( instances, update.values() ).process( update ); + selector.select( slotSelector.selectSlot( update.values(), GROUP_OF ) ).process( update ); break; default: throw new IllegalArgumentException( "Unknown update mode" ); @@ -73,7 +72,7 @@ public void close() throws IOException, IndexEntryConflictException { try { - forAll( IndexUpdater::close, instances ); + forInstantiated( IndexUpdater::close, selector ); } catch ( IOException | IndexEntryConflictException | RuntimeException e ) { diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionSelector00.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionSelector00.java index ecefc63ad292f..e925f8b4cfedc 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionSelector00.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionSelector00.java @@ -19,68 +19,42 @@ */ package org.neo4j.kernel.impl.index.schema.fusion; -import org.neo4j.internal.kernel.api.IndexQuery; -import org.neo4j.internal.kernel.api.IndexQuery.RangePredicate; -import org.neo4j.storageengine.api.schema.IndexReader; -import org.neo4j.values.storable.Value; +import java.util.function.Function; -import static org.neo4j.internal.kernel.api.IndexQuery.ExactPredicate; -import static org.neo4j.internal.kernel.api.IndexQuery.ExistsPredicate; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.LUCENE; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.SPATIAL; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.TEMPORAL; +import org.neo4j.kernel.api.index.IndexProvider; +import org.neo4j.values.storable.ValueGroup; /** * Selector for index provider "lucene-1.x". * The version name "00" comes from lucene-1.x originally not being a fusion index. */ -public class FusionSelector00 implements FusionIndexProvider.Selector +public class FusionSelector00 implements SlotSelector { @Override - public void validateSatisfied( Object[] instances ) + public void validateSatisfied( IndexProvider[] instances ) { FusionIndexBase.validateSelectorInstances( instances, LUCENE, SPATIAL, TEMPORAL ); } @Override - public int selectSlot( Value... values ) + public int selectSlot( V[] values, Function groupOf ) { if ( values.length > 1 ) { return LUCENE; } - Value singleValue = values[0]; - switch ( singleValue.valueGroup().category() ) + ValueGroup singleGroup = groupOf.apply( values[0] ); + switch ( singleGroup.category() ) { case GEOMETRY: return SPATIAL; case TEMPORAL: return TEMPORAL; - default: - return LUCENE; - } - } - - @Override - public IndexReader select( IndexReader[] instances, IndexQuery... predicates ) - { - if ( predicates.length > 1 ) - { - return instances[LUCENE]; - } - - IndexQuery predicate = predicates[0]; - switch ( predicate.valueGroup().category() ) - { - case GEOMETRY: - return instances[SPATIAL]; - case TEMPORAL: - return instances[TEMPORAL]; case UNKNOWN: - return null; + return UNKNOWN; default: - return instances[LUCENE]; + return LUCENE; } } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionSelector10.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionSelector10.java index 0b0cd52df6b7a..dfb9e1812639e 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionSelector10.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionSelector10.java @@ -19,38 +19,33 @@ */ package org.neo4j.kernel.impl.index.schema.fusion; -import org.neo4j.internal.kernel.api.IndexQuery; -import org.neo4j.storageengine.api.schema.IndexReader; -import org.neo4j.values.storable.Value; - -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.LUCENE; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.NUMBER; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.SPATIAL; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.TEMPORAL; +import java.util.function.Function; +import org.neo4j.kernel.api.index.IndexProvider; +import org.neo4j.values.storable.ValueGroup; /** * Selector for "lucene+native-1.x". * Separates numbers into native index. */ -public class FusionSelector10 implements FusionIndexProvider.Selector +public class FusionSelector10 implements SlotSelector { @Override - public void validateSatisfied( Object[] instances ) + public void validateSatisfied( IndexProvider[] instances ) { FusionIndexBase.validateSelectorInstances( instances, NUMBER, LUCENE, SPATIAL, TEMPORAL ); } @Override - public int selectSlot( Value... values ) + public int selectSlot( V[] values, Function groupOf ) { if ( values.length > 1 ) { return LUCENE; } - Value singleValue = values[0]; - switch ( singleValue.valueGroup().category() ) + ValueGroup singleGroup = groupOf.apply( values[0] ); + switch ( singleGroup.category() ) { case NUMBER: return NUMBER; @@ -58,32 +53,10 @@ public int selectSlot( Value... values ) return SPATIAL; case TEMPORAL: return TEMPORAL; - default: - return LUCENE; - } - } - - @Override - public IndexReader select( IndexReader[] instances, IndexQuery... predicates ) - { - if ( predicates.length > 1 ) - { - return instances[LUCENE]; - } - - IndexQuery predicate = predicates[0]; - switch ( predicate.valueGroup().category() ) - { - case NUMBER: - return instances[NUMBER]; - case GEOMETRY: - return instances[SPATIAL]; - case TEMPORAL: - return instances[TEMPORAL]; case UNKNOWN: - return null; + return UNKNOWN; default: - return instances[LUCENE]; + return LUCENE; } } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionSelector20.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionSelector20.java index 13f3ae3736636..e825adb2685fe 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionSelector20.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionSelector20.java @@ -19,38 +19,34 @@ */ package org.neo4j.kernel.impl.index.schema.fusion; -import org.neo4j.internal.kernel.api.IndexQuery; -import org.neo4j.storageengine.api.schema.IndexReader; -import org.neo4j.values.storable.Value; +import java.util.function.Function; + +import org.neo4j.kernel.api.index.IndexProvider; +import org.neo4j.values.storable.ValueGroup; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.LUCENE; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.NUMBER; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.SPATIAL; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.STRING; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.TEMPORAL; /** * Selector for "lucene+native-2.x". * Separates strings, numbers, temporal and spatial into native index. */ -public class FusionSelector20 implements FusionIndexProvider.Selector +public class FusionSelector20 implements SlotSelector { @Override - public void validateSatisfied( Object[] instances ) + public void validateSatisfied( IndexProvider[] instances ) { FusionIndexBase.validateSelectorInstances( instances, STRING, NUMBER, SPATIAL, TEMPORAL, LUCENE ); } @Override - public int selectSlot( Value... values ) + public int selectSlot( V[] values, Function groupOf ) { if ( values.length > 1 ) { return LUCENE; } - Value singleValue = values[0]; - switch ( singleValue.valueGroup().category() ) + ValueGroup singleGroup = groupOf.apply( values[0] ); + switch ( singleGroup.category() ) { case NUMBER: return NUMBER; @@ -60,34 +56,10 @@ public int selectSlot( Value... values ) return SPATIAL; case TEMPORAL: return TEMPORAL; - default: - return LUCENE; - } - } - - @Override - public IndexReader select( IndexReader[] instances, IndexQuery... predicates ) - { - if ( predicates.length > 1 ) - { - return instances[LUCENE]; - } - - IndexQuery predicate = predicates[0]; - switch ( predicate.valueGroup().category() ) - { - case NUMBER: - return instances[NUMBER]; - case TEXT: - return instances[STRING]; - case GEOMETRY: - return instances[SPATIAL]; - case TEMPORAL: - return instances[TEMPORAL]; case UNKNOWN: - return null; + return UNKNOWN; default: - return instances[LUCENE]; + return LUCENE; } } } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/Selector.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/Selector.java new file mode 100644 index 0000000000000..2c9cfcdc6beab --- /dev/null +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/Selector.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2002-2018 "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.index.schema.fusion; + +/** + * Selects an instance given a certain slot. + * @param type of instance + */ +class Selector +{ + private final T[] instances; + private final ThrowingIntFunction factory; + + /** + * @param instances fully instantiated instances so that no factory is needed. + */ + Selector( T[] instances ) + { + this( instances, slot -> + { + throw new IllegalStateException( "No instantiation expected" ); + } ); + } + + /** + * @param instances uninstantiated instances, instantiated lazily by the {@code factory}. + * @param factory {@link ThrowingIntFunction} for instantiating instances for specific slots. + */ + Selector( T[] instances, ThrowingIntFunction factory ) + { + this.instances = instances; + this.factory = factory; + } + + T select( int slot ) + { + if ( instances[slot] == null ) + { + try + { + instances[slot] = factory.apply( slot ); + } + catch ( Exception e ) + { + // TODO figure out how to handle this + throw new RuntimeException( e ); + } + } + return instances[slot]; + } + + T getIfInstantiated( int slot ) + { + return instances[slot]; + } +} 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 new file mode 100644 index 0000000000000..d4b54841edf7f --- /dev/null +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/SlotSelector.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2002-2018 "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.index.schema.fusion; + +import java.util.function.Function; + +import org.neo4j.kernel.api.index.IndexProvider; +import org.neo4j.values.storable.ValueGroup; + +/** + * Given a set of values selects a slot to use. + */ +interface SlotSelector +{ + int INSTANCE_COUNT = 5; + + int UNKNOWN = -1; + int STRING = 0; + int NUMBER = 1; + int SPATIAL = 2; + int TEMPORAL = 3; + int LUCENE = 4; + + void validateSatisfied( IndexProvider[] instances ); + + /** + * Selects a slot to use based on the given values. The values can be anything that can yield a {@link ValueGroup value group}, + * which is what the {@code groupOf} function extracts from each value. + * + * @param values values, something which can yield a {@link ValueGroup}. + * @param groupOf {@link Function} to get {@link ValueGroup} for the given values. + * @param type of value to extract {@link ValueGroup} from. + * @return a slot number, or {@link #UNKNOWN} if no single slot could be selected. This typically means that all slots are needed. + */ + int selectSlot( V[] values, Function groupOf ); +} diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/ThrowingIntFunction.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/ThrowingIntFunction.java new file mode 100644 index 0000000000000..d1fea0eabb001 --- /dev/null +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/ThrowingIntFunction.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2002-2018 "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.index.schema.fusion; + +import java.util.function.IntFunction; + +/** + * Like {@link IntFunction}, but one that can throw {@link Exception}. + * + * @param type of return value. + * @param type of {@link Exception}. + */ +public interface ThrowingIntFunction +{ + T apply( int value ) throws E; +} diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexAccessorTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexAccessorTest.java index aa9e0ccbbe4b9..26ec995f71737 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexAccessorTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexAccessorTest.java @@ -56,18 +56,18 @@ import static org.mockito.Mockito.when; import static org.mockito.internal.verification.VerificationModeFactory.times; import static org.neo4j.helpers.ArrayUtil.without; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.INSTANCE_COUNT; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.LUCENE; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.NUMBER; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.SPATIAL; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.STRING; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.TEMPORAL; import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexTestHelp.verifyFusionCloseThrowIfAllThrow; import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexTestHelp.verifyFusionCloseThrowOnSingleCloseThrow; import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexTestHelp.verifyOtherIsClosedOnSingleThrow; import static org.neo4j.kernel.impl.index.schema.fusion.FusionVersion.v00; import static org.neo4j.kernel.impl.index.schema.fusion.FusionVersion.v10; import static org.neo4j.kernel.impl.index.schema.fusion.FusionVersion.v20; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.INSTANCE_COUNT; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.LUCENE; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.NUMBER; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.SPATIAL; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.STRING; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.TEMPORAL; import static org.neo4j.values.storable.Values.stringValue; @RunWith( Parameterized.class ) @@ -131,7 +131,9 @@ private void initiateMocks() throw new RuntimeException(); } } - fusionIndexAccessor = new FusionIndexAccessor( accessors, fusionVersion.selector(), indexId, mock( SchemaIndexDescriptor.class ), dropAction ); + fusionIndexAccessor = + new FusionIndexAccessor( fusionVersion.slotSelector(), new Selector<>( accessors ), indexId, mock( SchemaIndexDescriptor.class ), + dropAction ); } private void resetMocks() diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexPopulatorTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexPopulatorTest.java index 4c2c0ca80ead4..9ea43c4fab642 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexPopulatorTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexPopulatorTest.java @@ -49,17 +49,17 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.INSTANCE_COUNT; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.LUCENE; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.NUMBER; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.SPATIAL; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.STRING; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.TEMPORAL; import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexTestHelp.add; import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexTestHelp.verifyCallFail; import static org.neo4j.kernel.impl.index.schema.fusion.FusionVersion.v00; import static org.neo4j.kernel.impl.index.schema.fusion.FusionVersion.v10; import static org.neo4j.kernel.impl.index.schema.fusion.FusionVersion.v20; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.INSTANCE_COUNT; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.LUCENE; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.NUMBER; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.SPATIAL; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.STRING; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.TEMPORAL; @RunWith( Parameterized.class ) public class FusionIndexPopulatorTest @@ -119,7 +119,7 @@ private void initiateMocks() throw new RuntimeException(); } } - fusionIndexPopulator = new FusionIndexPopulator( populators, fusionVersion.selector(), indexId, dropAction, false ); + fusionIndexPopulator = new FusionIndexPopulator( fusionVersion.slotSelector(), new Selector<>( populators ), indexId, dropAction, false ); } private void resetMocks() @@ -231,7 +231,7 @@ public void addMustSelectCorrectPopulator() throws Exception { for ( Value secondValue : allValues ) { - verifyAddWithCorrectPopulator( populators[FusionIndexBase.LUCENE], firstValue, secondValue ); + verifyAddWithCorrectPopulator( populators[LUCENE], firstValue, secondValue ); } } } diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexProviderTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexProviderTest.java index f044aa450f357..87944cd39d3a7 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexProviderTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexProviderTest.java @@ -38,7 +38,6 @@ import org.neo4j.kernel.impl.index.schema.SpatialIndexProvider; import org.neo4j.kernel.impl.index.schema.StringIndexProvider; import org.neo4j.kernel.impl.index.schema.TemporalIndexProvider; -import org.neo4j.kernel.impl.index.schema.fusion.FusionIndexProvider.Selector; import org.neo4j.storageengine.api.schema.IndexSample; import org.neo4j.test.rule.RandomRule; import org.neo4j.values.storable.Value; @@ -55,15 +54,16 @@ import static org.neo4j.helpers.ArrayUtil.array; import static org.neo4j.kernel.api.index.IndexDirectoryStructure.NONE; import static org.neo4j.kernel.api.schema.index.SchemaIndexDescriptorFactory.forLabel; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.INSTANCE_COUNT; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.LUCENE; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.NUMBER; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.SPATIAL; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.STRING; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.TEMPORAL; +import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.GROUP_OF; import static org.neo4j.kernel.impl.index.schema.fusion.FusionVersion.v00; import static org.neo4j.kernel.impl.index.schema.fusion.FusionVersion.v10; import static org.neo4j.kernel.impl.index.schema.fusion.FusionVersion.v20; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.INSTANCE_COUNT; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.LUCENE; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.NUMBER; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.SPATIAL; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.STRING; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.TEMPORAL; @RunWith( Parameterized.class ) public class FusionIndexProviderTest @@ -73,7 +73,8 @@ public class FusionIndexProviderTest private IndexProvider[] providers; private IndexProvider[] aliveProviders; private IndexProvider fusionIndexProvider; - private Selector selector; + private SlotSelector slotSelector; + private Selector selector; @Parameterized.Parameters( name = "{0}" ) public static FusionVersion[] versions() @@ -90,7 +91,7 @@ public static FusionVersion[] versions() @Before public void setup() { - selector = fusionVersion.selector(); + slotSelector = fusionVersion.slotSelector(); setupMocks(); } @@ -110,7 +111,7 @@ public void mustSelectCorrectTargetForAllGivenValueCombinations() for ( Value value : group ) { // when - IndexProvider selected = selector.select( providers, value ); + IndexProvider selected = selector.select( slotSelector.selectSlot( array( value ), GROUP_OF ) ); // then assertSame( orLucene( providers[i] ), selected ); @@ -123,7 +124,7 @@ public void mustSelectCorrectTargetForAllGivenValueCombinations() for ( Value secondValue : allValues ) { // when - IndexProvider selected = selector.select( providers, firstValue, secondValue ); + IndexProvider selected = selector.select( slotSelector.selectSlot( array( firstValue, secondValue ), GROUP_OF ) ); // then assertSame( providers[LUCENE], selected ); @@ -318,7 +319,8 @@ private void setupMocks() providers[SPATIAL], providers[TEMPORAL], providers[LUCENE], - fusionVersion.selector(), DESCRIPTOR, 10, NONE, mock( FileSystemAbstraction.class ), false ); + fusionVersion.slotSelector(), DESCRIPTOR, 10, NONE, mock( FileSystemAbstraction.class ), false ); + selector = new Selector<>( providers ); } private IndexProvider mockProvider( Class providerClass, String name ) diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexReaderTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexReaderTest.java index c61f0f94d1861..294312fe7827a 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexReaderTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexReaderTest.java @@ -53,15 +53,15 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.INSTANCE_COUNT; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.LUCENE; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.NUMBER; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.SPATIAL; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.STRING; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.TEMPORAL; import static org.neo4j.kernel.impl.index.schema.fusion.FusionVersion.v00; import static org.neo4j.kernel.impl.index.schema.fusion.FusionVersion.v10; import static org.neo4j.kernel.impl.index.schema.fusion.FusionVersion.v20; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.INSTANCE_COUNT; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.LUCENE; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.NUMBER; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.SPATIAL; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.STRING; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.TEMPORAL; @RunWith( Parameterized.class ) public class FusionIndexReaderTest @@ -121,7 +121,8 @@ private void initiateMocks() throw new RuntimeException(); } } - fusionIndexReader = new FusionIndexReader( readers, fusionVersion.selector(), SchemaIndexDescriptorFactory.forLabel( LABEL_KEY, PROP_KEY ) ); + fusionIndexReader = new FusionIndexReader( fusionVersion.slotSelector(), new Selector<>( readers ), + SchemaIndexDescriptorFactory.forLabel( LABEL_KEY, PROP_KEY ) ); } /* close */ diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexUpdaterTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexUpdaterTest.java index 1b40941050be5..0367e33d63bb1 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexUpdaterTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionIndexUpdaterTest.java @@ -42,18 +42,18 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.neo4j.helpers.ArrayUtil.without; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.INSTANCE_COUNT; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.LUCENE; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.NUMBER; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.SPATIAL; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.STRING; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.TEMPORAL; import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexTestHelp.add; import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexTestHelp.change; import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexTestHelp.remove; import static org.neo4j.kernel.impl.index.schema.fusion.FusionVersion.v00; import static org.neo4j.kernel.impl.index.schema.fusion.FusionVersion.v10; import static org.neo4j.kernel.impl.index.schema.fusion.FusionVersion.v20; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.INSTANCE_COUNT; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.LUCENE; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.NUMBER; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.SPATIAL; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.STRING; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.TEMPORAL; @RunWith( Parameterized.class ) public class FusionIndexUpdaterTest @@ -113,7 +113,7 @@ private void initiateMocks() throw new RuntimeException(); } } - fusionIndexUpdater = new FusionIndexUpdater( updaters, fusionVersion.selector() ); + fusionIndexUpdater = new FusionIndexUpdater( fusionVersion.slotSelector(), new Selector( updaters ) ); } private void resetMocks() diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionVersion.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionVersion.java index 6d7d7a5b2477a..e15d010217231 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionVersion.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionVersion.java @@ -19,11 +19,11 @@ */ package org.neo4j.kernel.impl.index.schema.fusion; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.LUCENE; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.NUMBER; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.SPATIAL; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.STRING; -import static org.neo4j.kernel.impl.index.schema.fusion.FusionIndexBase.TEMPORAL; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.LUCENE; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.NUMBER; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.SPATIAL; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.STRING; +import static org.neo4j.kernel.impl.index.schema.fusion.SlotSelector.TEMPORAL; enum FusionVersion { @@ -36,7 +36,7 @@ int[] aliveSlots() } @Override - FusionIndexProvider.Selector selector() + SlotSelector slotSelector() { return new FusionSelector00(); } @@ -50,7 +50,7 @@ int[] aliveSlots() } @Override - FusionIndexProvider.Selector selector() + SlotSelector slotSelector() { return new FusionSelector10(); } @@ -64,7 +64,7 @@ int[] aliveSlots() } @Override - FusionIndexProvider.Selector selector() + SlotSelector slotSelector() { return new FusionSelector20(); } @@ -72,5 +72,5 @@ FusionIndexProvider.Selector selector() abstract int[] aliveSlots(); - abstract FusionIndexProvider.Selector selector(); + abstract SlotSelector slotSelector(); }