From a8c8acca6991841b98d434a7dd0bb802b3319c53 Mon Sep 17 00:00:00 2001 From: Anton Persson Date: Thu, 15 Mar 2018 16:49:26 +0100 Subject: [PATCH] Restore Fusion index to that of 3.3 Disable - Native string index - Native Temporal index - Native Spatial index Also make Fusion index multiversionable for future adaptation. --- .../neo4j/kernel/api/index/IndexAccessor.java | 2 + .../kernel/api/index/IndexPopulator.java | 2 + .../neo4j/kernel/api/index/IndexProvider.java | 8 +- .../schema/fusion/FusionIndexAccessor.java | 5 +- .../index/schema/fusion/FusionIndexBase.java | 20 +- .../schema/fusion/FusionIndexProvider.java | 58 +++--- .../schema/fusion/FusionIndexReader.java | 107 +---------- .../index/schema/fusion/FusionSelector00.java | 52 +++++ ...ionSelector.java => FusionSelector10.java} | 57 ++++-- .../index/schema/fusion/FusionSelector20.java | 125 ++++++++++++ .../fusion/FusionIndexAccessorTest.java | 174 +++++++++++------ .../fusion/FusionIndexPopulatorTest.java | 181 ++++++++++++------ .../fusion/FusionIndexProviderTest.java | 169 +++++++++++----- .../schema/fusion/FusionIndexReaderTest.java | 154 +++++++++++---- .../schema/fusion/FusionIndexUpdaterTest.java | 143 ++++++++++---- .../index/schema/fusion/FusionVersion.java | 76 ++++++++ .../command/HighIdTransactionApplierTest.java | 6 +- .../test/rule/NeoStoreDataSourceRule.java | 2 +- .../test/rule/RecordStorageEngineRule.java | 2 +- ...ativeLuceneFusionIndexProviderFactory.java | 23 +-- 20 files changed, 950 insertions(+), 416 deletions(-) create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionSelector00.java rename community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/{FusionSelector.java => FusionSelector10.java} (54%) create mode 100644 community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionSelector20.java create mode 100644 community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionVersion.java diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/index/IndexAccessor.java b/community/kernel/src/main/java/org/neo4j/kernel/api/index/IndexAccessor.java index 771c3598643ad..feada6eb7f725 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/api/index/IndexAccessor.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/index/IndexAccessor.java @@ -118,6 +118,8 @@ public interface IndexAccessor extends Closeable */ boolean isDirty(); + IndexAccessor EMPTY = new Adapter(); + class Adapter implements IndexAccessor { @Override diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/index/IndexPopulator.java b/community/kernel/src/main/java/org/neo4j/kernel/api/index/IndexPopulator.java index b77fa7cb62401..1f4944066fa2b 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/api/index/IndexPopulator.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/index/IndexPopulator.java @@ -142,6 +142,8 @@ void add( Collection> updates ) */ IndexSample sampleResult(); + IndexPopulator EMPTY = new Adapter(); + class Adapter implements IndexPopulator { @Override 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 43c84aa87a86a..f78996fba3558 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 @@ -114,11 +114,11 @@ public void recoveryCompleted( long indexId, SchemaIndexDescriptor schemaIndexDe void recoveryCompleted( long indexId, SchemaIndexDescriptor schemaIndexDescriptor, Map data ); } - public static final IndexProvider NO_INDEX_PROVIDER = + public static final IndexProvider EMPTY = new IndexProvider( new Descriptor( "no-index-provider", "1.0" ), -1, IndexDirectoryStructure.NONE ) { - private final IndexAccessor singleWriter = new IndexAccessor.Adapter(); - private final IndexPopulator singlePopulator = new IndexPopulator.Adapter(); + private final IndexAccessor singleWriter = IndexAccessor.EMPTY; + private final IndexPopulator singlePopulator = IndexPopulator.EMPTY; @Override public IndexAccessor getOnlineAccessor( long indexId, SchemaIndexDescriptor descriptor, @@ -137,7 +137,7 @@ public IndexPopulator getPopulator( long indexId, SchemaIndexDescriptor descript @Override public InternalIndexState getInitialState( long indexId, SchemaIndexDescriptor descriptor ) { - return InternalIndexState.POPULATING; + return InternalIndexState.ONLINE; } @Override 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 f92993391e07e..e7f6dd1bd0699 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 @@ -147,10 +147,7 @@ public Iterator iterator() public ResourceIterator snapshotFiles() throws IOException { List> snapshots = new ArrayList<>(); - for ( IndexAccessor accessor : instances ) - { - snapshots.add( accessor.snapshotFiles() ); - } + forAll( accessor -> snapshots.add( accessor.snapshotFiles() ), instances ); return concatResourceIterators( snapshots.iterator() ); } 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 3cd220cee11a4..521a2f44c2bae 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 @@ -20,9 +20,12 @@ package org.neo4j.kernel.impl.index.schema.fusion; import java.lang.reflect.Array; +import java.util.Arrays; +import org.neo4j.collection.primitive.PrimitiveIntCollections; import org.neo4j.function.ThrowingConsumer; import org.neo4j.function.ThrowingFunction; +import org.neo4j.kernel.api.index.IndexProvider; /** * Acting as a simplifier for the multiplexing that is going in inside a fusion index. A fusion index consists of multiple parts, @@ -33,7 +36,7 @@ */ public abstract class FusionIndexBase { - private static final int INSTANCE_COUNT = 5; + static final int INSTANCE_COUNT = 5; static final int STRING = 0; static final int NUMBER = 1; @@ -163,4 +166,19 @@ public static void forAll( ThrowingConsumer consum throw exception; } } + + static void validateSelectorInstances( Object[] instances, int... notNullIndex ) + { + for ( int i = 0; i < instances.length; i++ ) + { + boolean expected = PrimitiveIntCollections.contains( notNullIndex, i ); + boolean actual = instances[i] != IndexProvider.EMPTY; + if ( expected != actual ) + { + throw new IllegalArgumentException( + String.format( "Only indexes expected to be separated from IndexProvider.EMPTY are %s but was %s", + Arrays.toString( notNullIndex ), Arrays.toString( instances ) ) ); + } + } + } } 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 089ed569e7c8f..819b8f0fa2ee7 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 @@ -24,6 +24,7 @@ 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.fs.FileSystemAbstraction; import org.neo4j.io.pagecache.PageCache; @@ -33,18 +34,21 @@ import org.neo4j.kernel.api.index.IndexProvider; import org.neo4j.kernel.api.schema.index.SchemaIndexDescriptor; import org.neo4j.kernel.impl.api.index.sampling.IndexSamplingConfig; -import org.neo4j.kernel.impl.index.schema.NumberIndexProvider; -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.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.ValueGroup; -import static org.neo4j.helpers.collection.Iterators.array; 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; /** @@ -55,24 +59,28 @@ 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 )]; } + + IndexReader select( IndexReader[] instances, IndexQuery... predicates ); } - private final IndexProvider[] providers; + private final IndexProvider[] providers = new IndexProvider[INSTANCE_COUNT]; private final Selector selector; private final DropAction dropAction; public FusionIndexProvider( // good to be strict with specific providers here since this is dev facing - StringIndexProvider stringProvider, - NumberIndexProvider numberProvider, - SpatialIndexProvider spatialProvider, - TemporalIndexProvider temporalProvider, + IndexProvider stringProvider, + IndexProvider numberProvider, + IndexProvider spatialProvider, + IndexProvider temporalProvider, IndexProvider luceneProvider, Selector selector, Descriptor descriptor, @@ -81,11 +89,22 @@ public FusionIndexProvider( FileSystemAbstraction fs ) { super( descriptor, priority, directoryStructure ); - this.providers = array( stringProvider, numberProvider, spatialProvider, temporalProvider, luceneProvider ); + fillProvidersArray( stringProvider, numberProvider, spatialProvider, temporalProvider, luceneProvider ); + selector.validateSatisfied( providers ); this.selector = selector; this.dropAction = new FileSystemDropAction( fs, directoryStructure() ); } + private void fillProvidersArray( IndexProvider stringProvider, IndexProvider numberProvider, IndexProvider spatialProvider, IndexProvider temporalProvider, + IndexProvider luceneProvider ) + { + providers[STRING] = stringProvider; + providers[NUMBER] = numberProvider; + providers[SPATIAL] = spatialProvider; + providers[TEMPORAL] = temporalProvider; + providers[LUCENE] = luceneProvider; + } + @Override public IndexPopulator getPopulator( long indexId, SchemaIndexDescriptor descriptor, IndexSamplingConfig samplingConfig ) { @@ -106,10 +125,7 @@ public IndexAccessor getOnlineAccessor( long indexId, SchemaIndexDescriptor desc public String getPopulationFailure( long indexId, SchemaIndexDescriptor descriptor ) throws IllegalStateException { StringBuilder builder = new StringBuilder(); - for ( int i = 0; i < providers.length; i++ ) - { - writeFailure( nameOf( i ), builder, providers[i], indexId, descriptor ); - } + forAll( p -> writeFailure( p.getClass().getSimpleName(), builder, p, indexId, descriptor ), providers ); String failure = builder.toString(); if ( !failure.isEmpty() ) { @@ -118,15 +134,6 @@ public String getPopulationFailure( long indexId, SchemaIndexDescriptor descript throw new IllegalStateException( "None of the indexes were in a failed state" ); } - /** - * @param subProviderIndex the index into the providers array to get the name of. - * @return some name distinguishing the provider of this subProviderIndex from other providers. - */ - private String nameOf( int subProviderIndex ) - { - return providers[subProviderIndex].getClass().getSimpleName(); - } - private void writeFailure( String indexName, StringBuilder builder, IndexProvider provider, long indexId, SchemaIndexDescriptor descriptor ) { try @@ -145,8 +152,7 @@ private void writeFailure( String indexName, StringBuilder builder, IndexProvide @Override public InternalIndexState getInitialState( long indexId, SchemaIndexDescriptor descriptor ) { - InternalIndexState[] states = - Arrays.stream( providers ).map( provider -> provider.getInitialState( indexId, descriptor ) ).toArray( InternalIndexState[]::new ); + InternalIndexState[] states = FusionIndexBase.instancesAs( providers, InternalIndexState.class, 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 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 1e81086c05aeb..ebbbb4e2dd141 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 @@ -27,13 +27,11 @@ import org.neo4j.internal.kernel.api.IndexOrder; import org.neo4j.internal.kernel.api.IndexQuery; import org.neo4j.internal.kernel.api.IndexQuery.ExactPredicate; -import org.neo4j.internal.kernel.api.IndexQuery.ExistsPredicate; import org.neo4j.internal.kernel.api.IndexQuery.RangePredicate; 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; @@ -41,9 +39,6 @@ import org.neo4j.values.storable.ValueGroup; import static java.lang.String.format; -import static org.neo4j.internal.kernel.api.IndexQuery.StringContainsPredicate; -import static org.neo4j.internal.kernel.api.IndexQuery.StringPrefixPredicate; -import static org.neo4j.internal.kernel.api.IndexQuery.StringSuffixPredicate; class FusionIndexReader extends FusionIndexBase implements IndexReader { @@ -76,109 +71,28 @@ public IndexSampler createSampler() @Override public PrimitiveLongResourceIterator query( IndexQuery... predicates ) throws IndexNotApplicableKernelException { - if ( predicates.length > 1 ) + IndexReader instance = selector.select( instances, predicates ); + if ( instance != null ) { - return instances[LUCENE].query( predicates ); + return instance.query( predicates ); } - IndexQuery predicate = predicates[0]; - - if ( predicate instanceof ExactPredicate ) - { - ExactPredicate exactPredicate = (ExactPredicate) predicate; - return selector.select( instances, exactPredicate.value() ).query( predicates ); - } - - if ( predicate instanceof StringPrefixPredicate || - predicate instanceof StringSuffixPredicate || - predicate instanceof StringContainsPredicate ) - { - return instances[STRING].query( predicate ); - } - - if ( predicate instanceof RangePredicate ) - { - switch ( predicate.valueGroup() ) - { - case NUMBER: - return instances[NUMBER].query( predicates ); - case GEOMETRY: - return instances[SPATIAL].query( predicates ); - case TEXT: - return instances[STRING].query( predicates ); - case DATE: - case LOCAL_DATE_TIME: - case ZONED_DATE_TIME: - case LOCAL_TIME: - case ZONED_TIME: - case DURATION: - return instances[TEMPORAL].query( predicates ); - default: // fall through - } - } - - // todo: There will be no ordering of the node ids here. Is this a problem? - if ( predicate instanceof ExistsPredicate ) + else { PrimitiveLongResourceIterator[] converted = instancesAs( PrimitiveLongResourceIterator.class, reader -> reader.query( predicates ) ); return PrimitiveLongResourceCollections.concat( converted ); } - - return instances[LUCENE].query( predicates ); } @Override public void query( IndexProgressor.NodeValueClient cursor, IndexOrder indexOrder, IndexQuery... predicates ) throws IndexNotApplicableKernelException { - if ( predicates.length > 1 ) - { - instances[LUCENE].query( cursor, indexOrder, predicates ); - return; - } - IndexQuery predicate = predicates[0]; - - if ( predicate instanceof ExactPredicate ) - { - ExactPredicate exactPredicate = (ExactPredicate) predicate; - selector.select( instances, exactPredicate.value() ).query( cursor, indexOrder, predicate ); - return; - } - - if ( predicate instanceof StringPrefixPredicate || - predicate instanceof StringSuffixPredicate || - predicate instanceof StringContainsPredicate ) - { - instances[STRING].query( cursor, indexOrder, predicate ); - return; - } - - if ( predicate instanceof RangePredicate ) + IndexReader instance = selector.select( instances, predicates ); + if ( instance != null ) { - switch ( predicate.valueGroup() ) - { - case NUMBER: - instances[NUMBER].query( cursor, indexOrder, predicates ); - return; - case GEOMETRY: - instances[SPATIAL].query( cursor, indexOrder, predicates ); - return; - case TEXT: - instances[STRING].query( cursor, indexOrder, predicates ); - return; - case DATE: - case LOCAL_DATE_TIME: - case ZONED_DATE_TIME: - case LOCAL_TIME: - case ZONED_TIME: - case DURATION: - instances[TEMPORAL].query( cursor, indexOrder, predicates ); - return; - default: // fall through - } + instance.query( cursor, indexOrder, predicates ); } - - // todo: There will be no ordering of the node ids here. Is this a problem? - if ( predicate instanceof ExistsPredicate ) + else { if ( indexOrder != IndexOrder.NONE ) { @@ -191,12 +105,9 @@ public void query( IndexProgressor.NodeValueClient cursor, IndexOrder indexOrder cursor.initialize( descriptor, multiProgressor, predicates ); for ( IndexReader reader : instances ) { - reader.query( multiProgressor, indexOrder, predicate ); + reader.query( multiProgressor, indexOrder, predicates ); } - return; } - - instances[LUCENE].query( cursor, indexOrder, predicates ); } @Override 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 new file mode 100644 index 0000000000000..a502bac08e3d4 --- /dev/null +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionSelector00.java @@ -0,0 +1,52 @@ +/* + * 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 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; + +/** + * Selector for index provider "lucene-1.x". + * The version name "00" comes from lucene-1.x originally not being a fusion index. + * todo If spatial and temporal will live together with lucene-1.x, update this selector. Otherwise, remove it. + */ +public class FusionSelector00 implements FusionIndexProvider.Selector +{ + @Override + public void validateSatisfied( Object[] instances ) + { + FusionIndexBase.validateSelectorInstances( instances, LUCENE ); + } + + @Override + public int selectSlot( Value... values ) + { + return LUCENE; + } + + @Override + public IndexReader select( IndexReader[] instances, IndexQuery... predicates ) + { + return instances[LUCENE]; + } +} diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionSelector.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionSelector10.java similarity index 54% rename from community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionSelector.java rename to community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionSelector10.java index a07718818d133..d2a120aebceaa 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionSelector.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionSelector10.java @@ -19,18 +19,30 @@ */ package org.neo4j.kernel.impl.index.schema.fusion; +import org.neo4j.internal.kernel.api.IndexQuery; +import org.neo4j.internal.kernel.api.IndexQuery.ExistsPredicate; +import org.neo4j.storageengine.api.schema.IndexReader; import org.neo4j.values.storable.Value; import org.neo4j.values.storable.ValueGroup; -import org.neo4j.values.storable.Values; +import static org.neo4j.internal.kernel.api.IndexQuery.ExactPredicate; +import static org.neo4j.internal.kernel.api.IndexQuery.NumberRangePredicate; 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; -public class FusionSelector implements FusionIndexProvider.Selector + +/** + * Selector for "lucene+native-1.x". + * Separates numbers into native index. + */ +public class FusionSelector10 implements FusionIndexProvider.Selector { + @Override + public void validateSatisfied( Object[] instances ) + { + FusionIndexBase.validateSelectorInstances( instances, NUMBER, LUCENE ); + } + @Override public int selectSlot( Value... values ) { @@ -41,29 +53,40 @@ public int selectSlot( Value... values ) } Value singleValue = values[0]; - if ( singleValue.valueGroup() == ValueGroup.TEXT ) - { - // It's a string, the native string index can handle this - return STRING; - } if ( singleValue.valueGroup() == ValueGroup.NUMBER ) { - // It's a number, the native index can handle this return NUMBER; } - if ( Values.isGeometryValue( singleValue ) ) + return LUCENE; + } + + @Override + public IndexReader select( IndexReader[] instances, IndexQuery... predicates ) + { + if ( predicates.length > 1 ) + { + return instances[LUCENE]; + } + IndexQuery predicate = predicates[0]; + + if ( predicate instanceof ExactPredicate ) { - // It's a geometry, the spatial index can handle this - return SPATIAL; + ExactPredicate exactPredicate = (ExactPredicate) predicate; + return select( instances, exactPredicate.value() ); } - if ( Values.isTemporalValue( singleValue ) ) + if ( predicate instanceof NumberRangePredicate ) { - return TEMPORAL; + return instances[NUMBER]; } - return LUCENE; + if ( predicate instanceof ExistsPredicate ) + { + return null; + } + + return instances[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 new file mode 100644 index 0000000000000..362375dc323ad --- /dev/null +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/fusion/FusionSelector20.java @@ -0,0 +1,125 @@ +/* + * 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 org.neo4j.internal.kernel.api.IndexQuery; +import org.neo4j.storageengine.api.schema.IndexReader; +import org.neo4j.values.storable.Value; +import org.neo4j.values.storable.ValueGroup; +import org.neo4j.values.storable.Values; + +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, temoporal and spatial into native index. + */ +public class FusionSelector20 implements FusionIndexProvider.Selector +{ + @Override + public void validateSatisfied( Object[] instances ) + { + FusionIndexBase.validateSelectorInstances( instances, STRING, NUMBER, SPATIAL, TEMPORAL, LUCENE ); + } + + @Override + public int selectSlot( Value... values ) + { + if ( values.length > 1 ) + { + // Multiple values must be handled by lucene + return LUCENE; + } + + Value singleValue = values[0]; + if ( singleValue.valueGroup() == ValueGroup.TEXT ) + { + return STRING; + } + + if ( singleValue.valueGroup() == ValueGroup.NUMBER ) + { + return NUMBER; + } + + if ( Values.isGeometryValue( singleValue ) ) + { + return SPATIAL; + } + + if ( Values.isTemporalValue( singleValue ) ) + { + return TEMPORAL; + } + + return LUCENE; + } + + @Override + public IndexReader select( IndexReader[] instances, IndexQuery... predicates ) + { + if ( predicates.length > 1 ) + { + return instances[LUCENE]; + } + IndexQuery predicate = predicates[0]; + + if ( predicate instanceof IndexQuery.ExactPredicate ) + { + IndexQuery.ExactPredicate exactPredicate = (IndexQuery.ExactPredicate) predicate; + return select( instances, exactPredicate.value() ); + } + + if ( predicate instanceof IndexQuery.StringPredicate ) + { + return instances[STRING]; + } + + if ( predicate instanceof IndexQuery.RangePredicate ) + { + switch ( predicate.valueGroup() ) + { + case NUMBER: + return instances[NUMBER]; + case GEOMETRY: + return instances[SPATIAL]; + case TEXT: + return instances[STRING]; + case DATE: + case LOCAL_DATE_TIME: + case ZONED_DATE_TIME: + case LOCAL_TIME: + case ZONED_TIME: + case DURATION: + return instances[TEMPORAL]; + default: // fall through + } + } + if ( predicate instanceof IndexQuery.ExistsPredicate ) + { + return null; + } + throw new UnsupportedOperationException( "This selector does not have support for predicate " + predicate ); + } +} 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 569949f427386..cdace8a7c6505 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 @@ -22,10 +22,13 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -45,40 +48,94 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; 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.helpers.collection.Iterators.array; +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; +@RunWith( Parameterized.class ) public class FusionIndexAccessorTest { - private IndexAccessor stringAccessor; - private IndexAccessor numberAccessor; - private IndexAccessor spatialAccessor; - private IndexAccessor temporalAccessor; - private IndexAccessor luceneAccessor; private FusionIndexAccessor fusionIndexAccessor; private final long indexId = 10; private final DropAction dropAction = mock( DropAction.class ); - private IndexAccessor[] allAccessors; + private IndexAccessor[] accessors; + private IndexAccessor[] aliveAccessors; @Rule public RandomRule random = new RandomRule(); + @Parameterized.Parameters( name = "{0}" ) + public static FusionVersion[] versions() + { + return new FusionVersion[] + { + v00, v10, v20 + }; + } + + @Parameterized.Parameter + public static FusionVersion fusionVersion; + @Before - public void mockComponents() + public void setup() + { + initiateMocks(); + } + + private void initiateMocks() + { + int[] activeSlots = fusionVersion.aliveSlots(); + accessors = new IndexAccessor[INSTANCE_COUNT]; + Arrays.fill( accessors, IndexAccessor.EMPTY ); + aliveAccessors = new IndexAccessor[activeSlots.length]; + for ( int i = 0; i < activeSlots.length; i++ ) + { + IndexAccessor mock = mock( IndexAccessor.class ); + aliveAccessors[i] = mock; + switch ( activeSlots[i] ) + { + case STRING: + accessors[STRING] = mock; + break; + case NUMBER: + accessors[NUMBER] = mock; + break; + case SPATIAL: + accessors[SPATIAL] = mock; + break; + case TEMPORAL: + accessors[TEMPORAL] = mock; + break; + case LUCENE: + accessors[LUCENE] = mock; + break; + default: + throw new RuntimeException(); + } + } + fusionIndexAccessor = new FusionIndexAccessor( accessors, fusionVersion.selector(), indexId, mock( SchemaIndexDescriptor.class ), dropAction ); + } + + private void resetMocks() { - stringAccessor = mock( IndexAccessor.class ); - numberAccessor = mock( IndexAccessor.class ); - spatialAccessor = mock( IndexAccessor.class ); - temporalAccessor = mock( IndexAccessor.class ); - luceneAccessor = mock( IndexAccessor.class ); - allAccessors = array( stringAccessor, numberAccessor, spatialAccessor, temporalAccessor, luceneAccessor ); - fusionIndexAccessor = new FusionIndexAccessor( allAccessors, new FusionSelector(), indexId, mock( SchemaIndexDescriptor.class ), dropAction ); + for ( IndexAccessor accessor : aliveAccessors ) + { + reset( accessor ); + } } /* drop */ @@ -91,7 +148,7 @@ public void dropMustDropAll() throws Exception fusionIndexAccessor.drop(); // then - for ( IndexAccessor accessor : allAccessors ) + for ( IndexAccessor accessor : aliveAccessors ) { verify( accessor, times( 1 ) ).drop(); } @@ -101,7 +158,7 @@ public void dropMustDropAll() throws Exception @Test public void dropMustThrowIfDropAnyFail() throws Exception { - for ( IndexAccessor accessor : allAccessors ) + for ( IndexAccessor accessor : aliveAccessors ) { // when verifyFailOnSingleDropFailure( accessor, fusionIndexAccessor ); @@ -111,12 +168,12 @@ public void dropMustThrowIfDropAnyFail() throws Exception @Test public void fusionIndexIsDirtyWhenAnyIsDirty() { - for ( int i = 0; i < allAccessors.length; i++ ) + for ( IndexAccessor dirtyAccessor : aliveAccessors ) { // when - for ( int j = 0; j < allAccessors.length; j++ ) + for ( IndexAccessor aliveAccessor : aliveAccessors ) { - when( allAccessors[j].isDirty() ).thenReturn( j == i ); + when( aliveAccessor.isDirty() ).thenReturn( aliveAccessor == dirtyAccessor ); } // then @@ -146,7 +203,7 @@ public void dropMustThrowIfAllFail() throws Exception { // given List exceptions = new ArrayList<>(); - for ( IndexAccessor indexAccessor : allAccessors ) + for ( IndexAccessor indexAccessor : aliveAccessors ) { IOException exception = new IOException( indexAccessor.getClass().getSimpleName() + " fail" ); exceptions.add( exception ); @@ -176,7 +233,7 @@ public void closeMustCloseAll() throws Exception fusionIndexAccessor.close(); // then - for ( IndexAccessor accessor : allAccessors ) + for ( IndexAccessor accessor : aliveAccessors ) { verify( accessor, times( 1 ) ).close(); } @@ -185,29 +242,27 @@ public void closeMustCloseAll() throws Exception @Test public void closeMustThrowIfOneThrow() throws Exception { - for ( int i = 0; i < allAccessors.length; i++ ) + for ( IndexAccessor accessor : aliveAccessors ) { - verifyFusionCloseThrowOnSingleCloseThrow( allAccessors[i], fusionIndexAccessor ); - mockComponents(); + verifyFusionCloseThrowOnSingleCloseThrow( accessor, fusionIndexAccessor ); + resetMocks(); } } @Test public void closeMustCloseOthersIfOneThrow() throws Exception { - int count = allAccessors.length; - for ( int i = 0; i < count; i++ ) + for ( IndexAccessor accessor : aliveAccessors ) { - IndexAccessor accessor = allAccessors[i]; - verifyOtherIsClosedOnSingleThrow( accessor, fusionIndexAccessor, without( allAccessors, accessor ) ); - mockComponents(); + verifyOtherIsClosedOnSingleThrow( accessor, fusionIndexAccessor, without( aliveAccessors, accessor ) ); + resetMocks(); } } @Test public void closeMustThrowIfAllFail() throws Exception { - verifyFusionCloseThrowIfAllThrow( fusionIndexAccessor, allAccessors ); + verifyFusionCloseThrowIfAllThrow( fusionIndexAccessor, aliveAccessors ); } // newAllEntriesReader @@ -216,7 +271,7 @@ public void closeMustThrowIfAllFail() throws Exception public void allEntriesReaderMustCombineResultFromAll() { // given - List[] ids = new List[allAccessors.length]; + List[] ids = new List[aliveAccessors.length]; long lastId = 0; for ( int i = 0; i < ids.length; i++ ) { @@ -237,14 +292,14 @@ public void allEntriesReaderMustCombineResultFromAll() @Test public void allEntriesReaderMustCombineResultFromAllWithOneEmpty() { - for ( int i = 0; i < allAccessors.length; i++ ) + for ( int i = 0; i < accessors.length; i++ ) { // given - List[] ids = new List[allAccessors.length]; + List[] ids = new List[aliveAccessors.length]; long lastId = 0; for ( int j = 0; j < ids.length; j++ ) { - ids[j] = j == i ? Arrays.asList() : Arrays.asList( lastId++, lastId++ ); + ids[j] = j == i ? Collections.emptyList() : Arrays.asList( lastId++, lastId++ ); } mockAllEntriesReaders( ids ); @@ -263,10 +318,10 @@ public void allEntriesReaderMustCombineResultFromAllWithOneEmpty() public void allEntriesReaderMustCombineResultFromAllEmpty() { // given - List[] ids = new List[allAccessors.length]; + List[] ids = new List[aliveAccessors.length]; for ( int j = 0; j < ids.length; j++ ) { - ids[j] = Arrays.asList(); + ids[j] = Collections.emptyList(); } mockAllEntriesReaders( ids ); @@ -281,7 +336,7 @@ public void allEntriesReaderMustCombineResultFromAllEmpty() public void allEntriesReaderMustCombineResultFromAllAccessors() { // given - List[] parts = new List[allAccessors.length]; + List[] parts = new List[aliveAccessors.length]; for ( int i = 0; i < parts.length; i++ ) { parts[i] = new ArrayList<>(); @@ -306,7 +361,8 @@ public void allEntriesReaderMustCombineResultFromAllAccessors() public void allEntriesReaderMustCloseAll() throws Exception { // given - BoundedIterable[] allEntriesReaders = Arrays.stream( allAccessors ).map( accessor -> mockSingleAllEntriesReader( accessor, Arrays.asList() ) ) + BoundedIterable[] allEntriesReaders = Arrays.stream( aliveAccessors ) + .map( accessor -> mockSingleAllEntriesReader( accessor, Arrays.asList() ) ) .toArray( BoundedIterable[]::new ); // when @@ -322,57 +378,58 @@ public void allEntriesReaderMustCloseAll() throws Exception @Test public void allEntriesReaderMustCloseOthersIfOneThrow() throws Exception { - for ( int i = 0; i < allAccessors.length; i++ ) + for ( int i = 0; i < aliveAccessors.length; i++ ) { // given BoundedIterable[] allEntriesReaders = - Arrays.stream( allAccessors ).map( accessor -> mockSingleAllEntriesReader( accessor, Arrays.asList() ) ).toArray( BoundedIterable[]::new ); + Arrays.stream( aliveAccessors ) + .map( accessor -> mockSingleAllEntriesReader( accessor, Arrays.asList() ) ) + .toArray( BoundedIterable[]::new ); // then BoundedIterable fusionAllEntriesReader = fusionIndexAccessor.newAllEntriesReader(); verifyOtherIsClosedOnSingleThrow( allEntriesReaders[i], fusionAllEntriesReader, without( allEntriesReaders, allEntriesReaders[i] ) ); - mockComponents(); + resetMocks(); } } @Test public void allEntriesReaderMustThrowIfOneThrow() throws Exception { - for ( int i = 0; i < allAccessors.length; i++ ) + for ( IndexAccessor failingAccessor : aliveAccessors ) { - // given - BoundedIterable allEntriesReader = null; - for ( int j = 0; j < allAccessors.length; j++ ) + BoundedIterable failingReader = null; + for ( IndexAccessor aliveAccessor : aliveAccessors ) { - BoundedIterable reader = mockSingleAllEntriesReader( allAccessors[j], Arrays.asList() ); - if ( j == i ) + BoundedIterable reader = mockSingleAllEntriesReader( aliveAccessor, Collections.emptyList() ); + if ( aliveAccessor == failingAccessor ) { - allEntriesReader = reader; + failingReader = reader; } } // then BoundedIterable fusionAllEntriesReader = fusionIndexAccessor.newAllEntriesReader(); - FusionIndexTestHelp.verifyFusionCloseThrowOnSingleCloseThrow( allEntriesReader, fusionAllEntriesReader ); + FusionIndexTestHelp.verifyFusionCloseThrowOnSingleCloseThrow( failingReader, fusionAllEntriesReader ); } } @Test public void allEntriesReaderMustReportUnknownMaxCountIfAnyReportUnknownMaxCount() { - for ( int i = 0; i < allAccessors.length; i++ ) + for ( int i = 0; i < aliveAccessors.length; i++ ) { - for ( int j = 0; j < allAccessors.length; j++ ) + for ( int j = 0; j < aliveAccessors.length; j++ ) { // given if ( j == i ) { - mockSingleAllEntriesReaderWithUnknownMaxCount( allAccessors[j], Arrays.asList() ); + mockSingleAllEntriesReaderWithUnknownMaxCount( aliveAccessors[j], Collections.emptyList() ); } else { - mockSingleAllEntriesReader( allAccessors[j], Arrays.asList() ); + mockSingleAllEntriesReader( aliveAccessors[j], Collections.emptyList() ); } } @@ -386,7 +443,7 @@ public void allEntriesReaderMustReportUnknownMaxCountIfAnyReportUnknownMaxCount( public void allEntriesReaderMustReportFusionMaxCountOfAll() { long lastId = 0; - for ( IndexAccessor accessor : allAccessors ) + for ( IndexAccessor accessor : aliveAccessors ) { mockSingleAllEntriesReader( accessor, Arrays.asList( lastId++, lastId++ ) ); } @@ -440,12 +497,7 @@ private void mockAllEntriesReaders( List... entries ) { for ( int i = 0; i < entries.length; i++ ) { - mockSingleAllEntriesReader( allAccessors[i], entries[i] ); + mockSingleAllEntriesReader( aliveAccessors[i], entries[i] ); } } - - private List allAccessors() - { - return Arrays.asList( numberAccessor, stringAccessor, spatialAccessor, luceneAccessor ); - } } 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 6926e5b035a41..8a3d321de53df 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 @@ -21,11 +21,15 @@ import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.List; import org.neo4j.internal.kernel.api.schema.LabelSchemaDescriptor; import org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException; @@ -45,30 +49,85 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.neo4j.helpers.ArrayUtil.contains; -import static org.neo4j.helpers.ArrayUtil.without; -import static org.neo4j.helpers.collection.Iterators.array; +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; +@RunWith( Parameterized.class ) public class FusionIndexPopulatorTest { - private IndexPopulator lucenePopulator; - private IndexPopulator[] allPopulators; + private IndexPopulator[] alivePopulators; + private IndexPopulator[] populators; private FusionIndexPopulator fusionIndexPopulator; private final long indexId = 8; private final DropAction dropAction = mock( DropAction.class ); + @Parameterized.Parameters( name = "{0}" ) + public static FusionVersion[] versions() + { + return new FusionVersion[] + { + v00, v10, v20 + }; + } + + @Parameterized.Parameter + public static FusionVersion fusionVersion; + @Before - public void mockComponents() + public void setup() { - IndexPopulator stringPopulator = mock( IndexPopulator.class ); - IndexPopulator numberPopulator = mock( IndexPopulator.class ); - IndexPopulator spatialPopulator = mock( IndexPopulator.class ); - IndexPopulator temporalPopulator = mock( IndexPopulator.class ); - lucenePopulator = mock( IndexPopulator.class ); - allPopulators = array( stringPopulator, numberPopulator, spatialPopulator, temporalPopulator, lucenePopulator ); - fusionIndexPopulator = new FusionIndexPopulator( allPopulators, new FusionSelector(), indexId, dropAction ); + initiateMocks(); + } + + private void initiateMocks() + { + int[] aliveSlots = fusionVersion.aliveSlots(); + populators = new IndexPopulator[INSTANCE_COUNT]; + Arrays.fill( populators, IndexPopulator.EMPTY ); + alivePopulators = new IndexPopulator[aliveSlots.length]; + for ( int i = 0; i < aliveSlots.length; i++ ) + { + IndexPopulator mock = mock( IndexPopulator.class ); + alivePopulators[i] = mock; + switch ( aliveSlots[i] ) + { + case STRING: + populators[STRING] = mock; + break; + case NUMBER: + populators[NUMBER] = mock; + break; + case SPATIAL: + populators[SPATIAL] = mock; + break; + case TEMPORAL: + populators[TEMPORAL] = mock; + break; + case LUCENE: + populators[LUCENE] = mock; + break; + default: + throw new RuntimeException(); + } + } + fusionIndexPopulator = new FusionIndexPopulator( populators, fusionVersion.selector(), indexId, dropAction ); + } + + private void resetMocks() + { + for ( IndexPopulator alivePopulator : alivePopulators ) + { + reset( alivePopulator ); + } } /* create */ @@ -80,20 +139,20 @@ public void createMustCreateAll() throws Exception fusionIndexPopulator.create(); // then - for ( IndexPopulator populator : allPopulators ) + for ( IndexPopulator alivePopulator : alivePopulators ) { - verify( populator, times( 1 ) ).create(); + verify( alivePopulator, times( 1 ) ).create(); } } @Test public void createMustThrowIfAnyThrow() throws Exception { - for ( IndexPopulator populator : allPopulators ) + for ( IndexPopulator alivePopulator : alivePopulators ) { // given IOException failure = new IOException( "fail" ); - doThrow( failure ).when( populator ).create(); + doThrow( failure ).when( alivePopulator ).create(); verifyCallFail( failure, () -> { @@ -102,7 +161,7 @@ public void createMustThrowIfAnyThrow() throws Exception } ); // reset throw for testing of next populator - doAnswer( invocation -> null ).when( populator ).create(); + doAnswer( invocation -> null ).when( alivePopulator ).create(); } } @@ -115,9 +174,9 @@ public void dropMustDropAll() throws Exception fusionIndexPopulator.drop(); // then - for ( IndexPopulator populator : allPopulators ) + for ( IndexPopulator alivePopulator : alivePopulators ) { - verify( populator, times( 1 ) ).drop(); + verify( alivePopulator, times( 1 ) ).drop(); } verify( dropAction ).drop( indexId ); } @@ -125,11 +184,11 @@ public void dropMustDropAll() throws Exception @Test public void dropMustThrowIfAnyDropThrow() throws Exception { - for ( IndexPopulator populator : allPopulators ) + for ( IndexPopulator alivePopulator : alivePopulators ) { // given IOException failure = new IOException( "fail" ); - doThrow( failure ).when( populator ).drop(); + doThrow( failure ).when( alivePopulator ).drop(); verifyCallFail( failure, () -> { @@ -138,7 +197,7 @@ public void dropMustThrowIfAnyDropThrow() throws Exception } ); // reset throw for testing of next populator - doAnswer( invocation -> null ).when( populator ).drop(); + doAnswer( invocation -> null ).when( alivePopulator ).drop(); } } @@ -151,11 +210,11 @@ public void addMustSelectCorrectPopulator() throws Exception Value[][] values = FusionIndexTestHelp.valuesByGroup(); Value[] allValues = FusionIndexTestHelp.allValues(); - for ( int i = 0; i < allPopulators.length; i++ ) + for ( int i = 0; i < populators.length; i++ ) { for ( Value value : values[i] ) { - verifyAddWithCorrectPopulator( allPopulators[i], value ); + verifyAddWithCorrectPopulator( orLucene( populators[i] ), value ); } } @@ -164,7 +223,7 @@ public void addMustSelectCorrectPopulator() throws Exception { for ( Value secondValue : allValues ) { - verifyAddWithCorrectPopulator( lucenePopulator, firstValue, secondValue ); + verifyAddWithCorrectPopulator( populators[FusionIndexBase.LUCENE], firstValue, secondValue ); } } } @@ -175,11 +234,11 @@ private void verifyAddWithCorrectPopulator( IndexPopulator correctPopulator, Val Collection> update = Collections.singletonList( add( numberValues ) ); fusionIndexPopulator.add( update ); verify( correctPopulator, times( 1 ) ).add( update ); - for ( IndexPopulator populator : allPopulators ) + for ( IndexPopulator alivePopulator : alivePopulators ) { - if ( populator != correctPopulator ) + if ( alivePopulator != correctPopulator ) { - verify( populator, never() ).add( update ); + verify( alivePopulator, never() ).add( update ); } } } @@ -188,11 +247,11 @@ private void verifyAddWithCorrectPopulator( IndexPopulator correctPopulator, Val @Test public void verifyDeferredConstraintsMustThrowIfAnyThrow() throws Exception { - for ( IndexPopulator populator : allPopulators ) + for ( IndexPopulator alivePopulator : alivePopulators ) { // given IndexEntryConflictException failure = mock( IndexEntryConflictException.class ); - doThrow( failure ).when( populator ).verifyDeferredConstraints( any() ); + doThrow( failure ).when( alivePopulator ).verifyDeferredConstraints( any() ); verifyCallFail( failure, () -> { @@ -201,7 +260,7 @@ public void verifyDeferredConstraintsMustThrowIfAnyThrow() throws Exception } ); // reset throw for testing of next populator - doAnswer( invocation -> null ).when( populator ).verifyDeferredConstraints( any() ); + doAnswer( invocation -> null ).when( alivePopulator ).verifyDeferredConstraints( any() ); } } @@ -225,20 +284,20 @@ private void closeAndVerifyPropagation( boolean populationCompletedSuccessfully fusionIndexPopulator.close( populationCompletedSuccessfully ); // then - for ( IndexPopulator populator : allPopulators ) + for ( IndexPopulator alivePopulator : alivePopulators ) { - verify( populator, times( 1 ) ).close( populationCompletedSuccessfully ); + verify( alivePopulator, times( 1 ) ).close( populationCompletedSuccessfully ); } } @Test public void closeMustThrowIfCloseAnyThrow() throws Exception { - for ( IndexPopulator populator : allPopulators ) + for ( IndexPopulator alivePopulator : alivePopulators ) { // given IOException failure = new IOException( "fail" ); - doThrow( failure ).when( populator ).close( anyBoolean() ); + doThrow( failure ).when( alivePopulator ).close( anyBoolean() ); verifyCallFail( failure, () -> { @@ -247,12 +306,11 @@ public void closeMustThrowIfCloseAnyThrow() throws Exception } ); // reset throw for testing of next populator - doAnswer( invocation -> null ).when( populator ).close( anyBoolean() ); + doAnswer( invocation -> null ).when( alivePopulator ).close( anyBoolean() ); } } - private static void verifyOtherCloseOnThrow( IndexPopulator throwingPopulator, FusionIndexPopulator fusionPopulator, IndexPopulator... populators ) - throws Exception + private void verifyOtherCloseOnThrow( IndexPopulator throwingPopulator ) throws Exception { // given IOException failure = new IOException( "fail" ); @@ -261,7 +319,7 @@ private static void verifyOtherCloseOnThrow( IndexPopulator throwingPopulator, F // when try { - fusionPopulator.close( true ); + fusionIndexPopulator.close( true ); fail( "Should have failed" ); } catch ( IOException ignore ) @@ -269,20 +327,19 @@ private static void verifyOtherCloseOnThrow( IndexPopulator throwingPopulator, F } // then - for ( IndexPopulator populator : populators ) + for ( IndexPopulator alivePopulator : alivePopulators ) { - verify( populator, times( 1 ) ).close( true ); + verify( alivePopulator, times( 1 ) ).close( true ); } } @Test public void closeMustCloseOthersIfAnyThrow() throws Exception { - for ( int i = 0; i < allPopulators.length; i++ ) + for ( IndexPopulator throwingPopulator : alivePopulators ) { - IndexPopulator populator = allPopulators[i]; - verifyOtherCloseOnThrow( populator, fusionIndexPopulator, without( allPopulators, populator ) ); - mockComponents(); + verifyOtherCloseOnThrow( throwingPopulator ); + resetMocks(); } } @@ -290,11 +347,12 @@ public void closeMustCloseOthersIfAnyThrow() throws Exception public void closeMustThrowIfAllThrow() throws Exception { // given - IOException[] failures = new IOException[allPopulators.length]; - for ( int i = 0; i < allPopulators.length; i++ ) + List failures = new ArrayList<>(); + for ( IndexPopulator alivePopulator : alivePopulators ) { - failures[i] = new IOException( "FAILURE[" + i + "]" ); - doThrow( failures[i] ).when( allPopulators[i] ).close( anyBoolean() ); + IOException failure = new IOException( "FAILURE[" + alivePopulator + "]" ); + failures.add( failure ); + doThrow( failure ).when( alivePopulator ).close( anyBoolean() ); } try @@ -306,9 +364,9 @@ public void closeMustThrowIfAllThrow() throws Exception catch ( IOException e ) { // then - if ( !contains( failures, e ) ) + if ( !failures.contains( e ) ) { - fail( "Thrown exception didn't matchh any of the expected failures: " + Arrays.toString( failures ) ); + fail( "Thrown exception didn't match any of the expected failures: " + failures ); } } } @@ -322,20 +380,20 @@ public void markAsFailedMustMarkAll() throws Exception fusionIndexPopulator.markAsFailed( failureMessage ); // then - for ( IndexPopulator populator : allPopulators ) + for ( IndexPopulator alivePopulator : alivePopulators ) { - verify( populator, times( 1 ) ).markAsFailed( failureMessage ); + verify( alivePopulator, times( 1 ) ).markAsFailed( failureMessage ); } } @Test public void markAsFailedMustThrowIfAnyThrow() throws Exception { - for ( IndexPopulator populator : allPopulators ) + for ( IndexPopulator alivePopulator : alivePopulators ) { // given IOException failure = new IOException( "fail" ); - doThrow( failure ).when( populator ).markAsFailed( anyString() ); + doThrow( failure ).when( alivePopulator ).markAsFailed( anyString() ); // then verifyCallFail( failure, () -> @@ -345,7 +403,7 @@ public void markAsFailedMustThrowIfAnyThrow() throws Exception } ); // reset throw for testing of next populator - doAnswer( invocation -> null ).when( populator ).markAsFailed( anyString() ); + doAnswer( invocation -> null ).when( alivePopulator ).markAsFailed( anyString() ); } } @@ -355,9 +413,9 @@ public void shouldIncludeSampleOnCorrectPopulator() // given Value[][] values = FusionIndexTestHelp.valuesByGroup(); - for ( int i = 0; i < allPopulators.length; i++ ) + for ( int activeSlot : fusionVersion.aliveSlots() ) { - verifySampleToCorrectPopulator( values[i], allPopulators[i] ); + verifySampleToCorrectPopulator( values[activeSlot], populators[activeSlot] ); } } @@ -374,4 +432,9 @@ private void verifySampleToCorrectPopulator( Value[] values, IndexPopulator popu reset( populator ); } } + + private IndexPopulator orLucene( IndexPopulator populator ) + { + return populator != IndexPopulator.EMPTY ? populator : populators[LUCENE]; + } } 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 6446e156d29ac..09d4cd5ca6429 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 @@ -22,6 +22,12 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import org.neo4j.internal.kernel.api.InternalIndexState; import org.neo4j.io.fs.FileSystemAbstraction; @@ -49,32 +55,43 @@ 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.FusionVersion.v00; +import static org.neo4j.kernel.impl.index.schema.fusion.FusionVersion.v10; +import static org.neo4j.kernel.impl.index.schema.fusion.FusionVersion.v20; + +@RunWith( Parameterized.class ) public class FusionIndexProviderTest { private static final IndexProvider.Descriptor DESCRIPTOR = new IndexProvider.Descriptor( "test-fusion", "1" ); - private StringIndexProvider stringProvider; - private NumberIndexProvider numberProvider; - private SpatialIndexProvider spatialProvider; - private TemporalIndexProvider temporalProvider; - private IndexProvider luceneProvider; private IndexProvider[] providers; + private IndexProvider[] aliveProviders; + private IndexProvider fusionIndexProvider; + private Selector selector; + + @Parameterized.Parameters( name = "{0}" ) + public static FusionVersion[] versions() + { + return new FusionVersion[] + { + v00, v10, v20 + }; + } + + @Parameterized.Parameter + public static FusionVersion fusionVersion; @Before public void setup() { - stringProvider = mock( StringIndexProvider.class ); - numberProvider = mock( NumberIndexProvider.class ); - spatialProvider = mock( SpatialIndexProvider.class ); - temporalProvider = mock( TemporalIndexProvider.class ); - luceneProvider = mock( IndexProvider.class ); - when( stringProvider.getProviderDescriptor() ).thenReturn( new IndexProvider.Descriptor( "string", "1" ) ); - when( numberProvider.getProviderDescriptor() ).thenReturn( new IndexProvider.Descriptor( "number", "1" ) ); - when( spatialProvider.getProviderDescriptor() ).thenReturn( new IndexProvider.Descriptor( "spatial", "1" ) ); - when( temporalProvider.getProviderDescriptor() ).thenReturn( new IndexProvider.Descriptor( "temporal", "1" ) ); - when( luceneProvider.getProviderDescriptor() ).thenReturn( new IndexProvider.Descriptor( "lucene", "1" ) ); - providers = array( stringProvider, numberProvider, spatialProvider, temporalProvider, luceneProvider ); + selector = fusionVersion.selector(); + setupMocks(); } @Rule @@ -86,7 +103,6 @@ public void mustSelectCorrectTargetForAllGivenValueCombinations() // given Value[][] values = FusionIndexTestHelp.valuesByGroup(); Value[] allValues = FusionIndexTestHelp.allValues(); - Selector selector = new FusionSelector(); for ( int i = 0; i < values.length; i++ ) { @@ -97,7 +113,7 @@ public void mustSelectCorrectTargetForAllGivenValueCombinations() IndexProvider selected = selector.select( providers, value ); // then - assertSame( providers[i], selected ); + assertSame( orLucene( providers[i] ), selected ); } } @@ -110,7 +126,7 @@ public void mustSelectCorrectTargetForAllGivenValueCombinations() IndexProvider selected = selector.select( providers, firstValue, secondValue ); // then - assertSame( luceneProvider, selected ); + assertSame( providers[LUCENE], selected ); } } } @@ -146,13 +162,10 @@ public void mustCombineSamples() @Test public void getPopulationFailureMustThrowIfNoFailure() { - // given - FusionIndexProvider fusionIndexProvider = fusionProvider(); - // when // ... no failure IllegalStateException failure = new IllegalStateException( "not failed" ); - for ( IndexProvider provider : providers ) + for ( IndexProvider provider : aliveProviders ) { when( provider.getPopulationFailure( anyLong(), any( SchemaIndexDescriptor.class ) ) ).thenThrow( failure ); } @@ -171,48 +184,45 @@ public void getPopulationFailureMustThrowIfNoFailure() @Test public void getPopulationFailureMustReportFailureWhenAnyFailed() { - for ( int i = 0; i < providers.length; i++ ) + for ( IndexProvider failingProvider : aliveProviders ) { - FusionIndexProvider fusionSchemaIndexProvider = fusionProvider(); - // when String failure = "failure"; IllegalStateException exception = new IllegalStateException( "not failed" ); - for ( int j = 0; j < providers.length; j++ ) + for ( IndexProvider provider : aliveProviders ) { - if ( j == i ) + if ( provider == failingProvider ) { - when( providers[j].getPopulationFailure( anyLong(), any( SchemaIndexDescriptor.class ) ) ).thenReturn( failure ); + when( provider.getPopulationFailure( anyLong(), any( SchemaIndexDescriptor.class ) ) ).thenReturn( failure ); } else { - when( providers[j].getPopulationFailure( anyLong(), any( SchemaIndexDescriptor.class ) ) ).thenThrow( exception ); + when( provider.getPopulationFailure( anyLong(), any( SchemaIndexDescriptor.class ) ) ).thenThrow( exception ); } } // then - assertThat( fusionSchemaIndexProvider.getPopulationFailure( 0, forLabel( 0, 0 ) ), containsString( failure ) ); + assertThat( fusionIndexProvider.getPopulationFailure( 0, forLabel( 0, 0 ) ), containsString( failure ) ); } } @Test public void getPopulationFailureMustReportFailureWhenMultipleFail() { - FusionIndexProvider fusionSchemaIndexProvider = fusionProvider(); - // when - String[] failures = new String[providers.length]; - for ( int i = 0; i < providers.length; i++ ) + List failureMessages = new ArrayList<>(); + for ( IndexProvider aliveProvider : aliveProviders ) { - failures[i] = "FAILURE[" + i + "]"; - when( providers[i].getPopulationFailure( anyLong(), any( SchemaIndexDescriptor.class ) ) ).thenReturn( failures[i] ); + String failureMessage = "FAILURE[" + aliveProvider + "]"; + failureMessages.add( failureMessage ); + when( aliveProvider.getPopulationFailure( anyLong(), any( SchemaIndexDescriptor.class ) ) ).thenReturn( failureMessage ); } // then - String populationFailure = fusionSchemaIndexProvider.getPopulationFailure( 0, forLabel( 0, 0 ) ); - for ( String failure : failures ) + String populationFailure = fusionIndexProvider.getPopulationFailure( 0, forLabel( 0, 0 ) ); + for ( String failureMessage : failureMessages ) { - assertThat( populationFailure, containsString( failure ) ); + assertThat( populationFailure, containsString( failureMessage ) ); } } @@ -220,17 +230,17 @@ public void getPopulationFailureMustReportFailureWhenMultipleFail() public void shouldReportFailedIfAnyIsFailed() { // given - IndexProvider provider = fusionProvider(); + IndexProvider provider = fusionIndexProvider; SchemaIndexDescriptor schemaIndexDescriptor = SchemaIndexDescriptorFactory.forLabel( 1, 1 ); for ( InternalIndexState state : InternalIndexState.values() ) { - for ( int i = 0; i < providers.length; i++ ) + for ( IndexProvider failedProvider : aliveProviders ) { // when - for ( int j = 0; j < providers.length; j++ ) + for ( IndexProvider aliveProvider : aliveProviders ) { - setInitialState( providers[j], i == j ? InternalIndexState.FAILED : state ); + setInitialState( aliveProvider, failedProvider == aliveProvider ? InternalIndexState.FAILED : state ); } InternalIndexState initialState = provider.getInitialState( 0, schemaIndexDescriptor ); @@ -244,19 +254,18 @@ public void shouldReportFailedIfAnyIsFailed() public void shouldReportPopulatingIfAnyIsPopulating() { // given - IndexProvider provider = fusionProvider(); SchemaIndexDescriptor schemaIndexDescriptor = SchemaIndexDescriptorFactory.forLabel( 1, 1 ); for ( InternalIndexState state : array( InternalIndexState.ONLINE, InternalIndexState.POPULATING ) ) { - for ( int i = 0; i < providers.length; i++ ) + for ( IndexProvider populatingProvider : aliveProviders ) { // when - for ( int j = 0; j < providers.length; j++ ) + for ( IndexProvider aliveProvider : aliveProviders ) { - setInitialState( providers[j], i == j ? InternalIndexState.POPULATING : state ); + setInitialState( aliveProvider, populatingProvider == aliveProvider ? InternalIndexState.POPULATING : state ); } - InternalIndexState initialState = provider.getInitialState( 0, schemaIndexDescriptor ); + InternalIndexState initialState = fusionIndexProvider.getInitialState( 0, schemaIndexDescriptor ); // then assertEquals( InternalIndexState.POPULATING, initialState ); @@ -264,14 +273,68 @@ public void shouldReportPopulatingIfAnyIsPopulating() } } - private FusionIndexProvider fusionProvider() + private void setupMocks() { - return new FusionIndexProvider( stringProvider, numberProvider, spatialProvider, temporalProvider, luceneProvider, new FusionSelector(), - DESCRIPTOR, 10, NONE, mock( FileSystemAbstraction.class ) ); + int[] aliveSlots = fusionVersion.aliveSlots(); + aliveProviders = new IndexProvider[aliveSlots.length]; + providers = new IndexProvider[INSTANCE_COUNT]; + Arrays.fill( providers, IndexProvider.EMPTY ); + for ( int i = 0; i < aliveSlots.length; i++ ) + { + switch ( aliveSlots[i] ) + { + case STRING: + IndexProvider string = mockProvider( StringIndexProvider.class, "string" ); + providers[STRING] = string; + aliveProviders[i] = string; + break; + case NUMBER: + IndexProvider number = mockProvider( NumberIndexProvider.class, "number" ); + providers[NUMBER] = number; + aliveProviders[i] = number; + break; + case SPATIAL: + IndexProvider spatial = mockProvider( SpatialIndexProvider.class, "spatial" ); + providers[SPATIAL] = spatial; + aliveProviders[i] = spatial; + break; + case TEMPORAL: + IndexProvider temporal = mockProvider( TemporalIndexProvider.class, "temporal" ); + providers[TEMPORAL] = temporal; + aliveProviders[i] = temporal; + break; + case LUCENE: + IndexProvider lucene = mockProvider( IndexProvider.class, "lucene" ); + providers[LUCENE] = lucene; + aliveProviders[i] = lucene; + break; + default: + throw new RuntimeException(); + } + } + fusionIndexProvider = new FusionIndexProvider( + providers[STRING], + providers[NUMBER], + providers[SPATIAL], + providers[TEMPORAL], + providers[LUCENE], + fusionVersion.selector(), DESCRIPTOR, 10, NONE, mock( FileSystemAbstraction.class ) ); + } + + private IndexProvider mockProvider( Class providerClass, String name ) + { + IndexProvider mock = mock( providerClass ); + when( mock.getProviderDescriptor() ).thenReturn( new IndexProvider.Descriptor( name, "1" ) ); + return mock; } private void setInitialState( IndexProvider mockedProvider, InternalIndexState state ) { when( mockedProvider.getInitialState( anyLong(), any( SchemaIndexDescriptor.class ) ) ).thenReturn( state ); } + + private IndexProvider orLucene( IndexProvider provider ) + { + return provider != IndexProvider.EMPTY ? provider : providers[LUCENE]; + } } 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 f50726ef34c66..4158e904e28ee 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 @@ -21,6 +21,10 @@ import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; import org.neo4j.collection.primitive.PrimitiveLongCollections; import org.neo4j.collection.primitive.PrimitiveLongIterator; @@ -41,6 +45,7 @@ import org.neo4j.values.storable.Values; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -48,30 +53,75 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import static org.neo4j.helpers.collection.Iterators.array; - +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; + +@RunWith( Parameterized.class ) public class FusionIndexReaderTest { - private IndexReader stringReader; - private IndexReader numberReader; - private IndexReader spatialReader; - private IndexReader luceneReader; - private IndexReader temporalReader; - private IndexReader[] allReaders; + private IndexReader[] aliveReaders; + private IndexReader[] readers; private FusionIndexReader fusionIndexReader; private static final int PROP_KEY = 1; private static final int LABEL_KEY = 11; + @Parameterized.Parameters( name = "{0}" ) + public static FusionVersion[] versions() + { + return new FusionVersion[] + { + v00, v10, v20 + }; + } + + @Parameterized.Parameter + public static FusionVersion fusionVersion; + @Before public void setup() { - stringReader = mock( IndexReader.class ); - numberReader = mock( IndexReader.class ); - spatialReader = mock( IndexReader.class ); - temporalReader = mock( IndexReader.class ); - luceneReader = mock( IndexReader.class ); - allReaders = array( stringReader, numberReader, spatialReader, temporalReader, luceneReader ); - fusionIndexReader = new FusionIndexReader( allReaders, new FusionSelector(), SchemaIndexDescriptorFactory.forLabel( LABEL_KEY, PROP_KEY ) ); + initiateMocks(); + } + + private void initiateMocks() + { + int[] activeSlots = fusionVersion.aliveSlots(); + readers = new IndexReader[INSTANCE_COUNT]; + Arrays.fill( readers, IndexReader.EMPTY ); + aliveReaders = new IndexReader[activeSlots.length]; + for ( int i = 0; i < activeSlots.length; i++ ) + { + IndexReader mock = mock( IndexReader.class ); + aliveReaders[i] = mock; + switch ( activeSlots[i] ) + { + case STRING: + readers[STRING] = mock; + break; + case NUMBER: + readers[NUMBER] = mock; + break; + case SPATIAL: + readers[SPATIAL] = mock; + break; + case TEMPORAL: + readers[TEMPORAL] = mock; + break; + case LUCENE: + readers[LUCENE] = mock; + break; + default: + throw new RuntimeException(); + } + } + fusionIndexReader = new FusionIndexReader( readers, fusionVersion.selector(), SchemaIndexDescriptorFactory.forLabel( LABEL_KEY, PROP_KEY ) ); } /* close */ @@ -83,7 +133,7 @@ public void closeMustCloseBothNativeAndLucene() fusionIndexReader.close(); // then - for ( IndexReader reader : allReaders ) + for ( IndexReader reader : aliveReaders ) { verify( reader, times( 1 ) ).close(); } @@ -95,11 +145,11 @@ public void closeMustCloseBothNativeAndLucene() public void closeIteratorMustCloseAll() throws Exception { // given - PrimitiveLongResourceIterator[] iterators = new PrimitiveLongResourceIterator[allReaders.length]; - for ( int i = 0; i < allReaders.length; i++ ) + PrimitiveLongResourceIterator[] iterators = new PrimitiveLongResourceIterator[aliveReaders.length]; + for ( int i = 0; i < aliveReaders.length; i++ ) { PrimitiveLongResourceIterator iterator = mock( PrimitiveLongResourceIterator.class ); - when( allReaders[i].query( any( IndexQuery.class ) ) ).thenReturn( iterator ); + when( aliveReaders[i].query( any( IndexQuery.class ) ) ).thenReturn( iterator ); iterators[i] = iterator; } @@ -122,11 +172,11 @@ public void countIndexedNodesMustSelectCorrectReader() Value[][] values = FusionIndexTestHelp.valuesByGroup(); Value[] allValues = FusionIndexTestHelp.allValues(); - for ( int i = 0; i < allReaders.length; i++ ) + for ( int i = 0; i < readers.length; i++ ) { for ( Value value : values[i] ) { - verifyCountIndexedNodesWithCorrectReader( allReaders[i], value ); + verifyCountIndexedNodesWithCorrectReader( orLucene( readers[i] ), value ); } } @@ -135,7 +185,7 @@ public void countIndexedNodesMustSelectCorrectReader() { for ( Value secondValue : allValues ) { - verifyCountIndexedNodesWithCorrectReader( luceneReader, firstValue, secondValue ); + verifyCountIndexedNodesWithCorrectReader( readers[LUCENE], firstValue, secondValue ); } } } @@ -144,7 +194,7 @@ private void verifyCountIndexedNodesWithCorrectReader( IndexReader correct, Valu { fusionIndexReader.countIndexedNodes( 0, nativeValue ); verify( correct, times( 1 ) ).countIndexedNodes( 0, nativeValue ); - for ( IndexReader reader : allReaders ) + for ( IndexReader reader : aliveReaders ) { if ( reader != correct ) { @@ -159,7 +209,7 @@ private void verifyCountIndexedNodesWithCorrectReader( IndexReader correct, Valu public void mustSelectLuceneForCompositePredicate() throws Exception { // then - verifyQueryWithCorrectReader( luceneReader, any( IndexQuery.class ), any( IndexQuery.class ) ); + verifyQueryWithCorrectReader( readers[LUCENE], any( IndexQuery.class ), any( IndexQuery.class ) ); } @Test @@ -171,7 +221,7 @@ public void mustSelectStringForExactPredicateWithNumberValue() throws Exception IndexQuery indexQuery = IndexQuery.exact( PROP_KEY, value ); // then - verifyQueryWithCorrectReader( stringReader, indexQuery ); + verifyQueryWithCorrectReader( expectedForStrings(), indexQuery ); } } @@ -184,7 +234,7 @@ public void mustSelectNumberForExactPredicateWithNumberValue() throws Exception IndexQuery indexQuery = IndexQuery.exact( PROP_KEY, value ); // then - verifyQueryWithCorrectReader( numberReader, indexQuery ); + verifyQueryWithCorrectReader( expectedForNumbers(), indexQuery ); } } @@ -192,12 +242,13 @@ public void mustSelectNumberForExactPredicateWithNumberValue() throws Exception public void mustSelectSpatialForExactPredicateWithSpatialValue() throws Exception { // given + assumeTrue( hasSpatialSupport() ); for ( Object value : FusionIndexTestHelp.valuesSupportedBySpatial() ) { IndexQuery indexQuery = IndexQuery.exact( PROP_KEY, value ); // then - verifyQueryWithCorrectReader( spatialReader, indexQuery ); + verifyQueryWithCorrectReader( readers[SPATIAL], indexQuery ); } } @@ -205,12 +256,13 @@ public void mustSelectSpatialForExactPredicateWithSpatialValue() throws Exceptio public void mustSelectTemporalForExactPredicateWithTemporalValue() throws Exception { // given + assumeTrue( hasTemporalSupport() ); for ( Object temporalValue : FusionIndexTestHelp.valuesSupportedByTemporal() ) { IndexQuery indexQuery = IndexQuery.exact( PROP_KEY, temporalValue ); // then - verifyQueryWithCorrectReader( temporalReader, indexQuery ); + verifyQueryWithCorrectReader( readers[TEMPORAL], indexQuery ); } } @@ -223,7 +275,7 @@ public void mustSelectLuceneForExactPredicateWithOtherValue() throws Exception IndexQuery indexQuery = IndexQuery.exact( PROP_KEY, value ); // then - verifyQueryWithCorrectReader( luceneReader, indexQuery ); + verifyQueryWithCorrectReader( readers[LUCENE], indexQuery ); } } @@ -234,7 +286,7 @@ public void mustSelectStringForRangeStringPredicate() throws Exception RangePredicate stringRange = IndexQuery.range( PROP_KEY, "abc", true, "def", false ); // then - verifyQueryWithCorrectReader( stringReader, stringRange ); + verifyQueryWithCorrectReader( expectedForStrings(), stringRange ); } @Test @@ -244,19 +296,20 @@ public void mustSelectNumberForRangeNumericPredicate() throws Exception RangePredicate numberRange = IndexQuery.range( PROP_KEY, 0, true, 1, false ); // then - verifyQueryWithCorrectReader( numberReader, numberRange ); + verifyQueryWithCorrectReader( expectedForNumbers(), numberRange ); } @Test public void mustSelectSpatialForRangeGeometricPredicate() throws Exception { // given + assumeTrue( hasSpatialSupport() ); PointValue from = Values.pointValue( CoordinateReferenceSystem.Cartesian, 1.0, 1.0); PointValue to = Values.pointValue( CoordinateReferenceSystem.Cartesian, 2.0, 2.0); RangePredicate geometryRange = IndexQuery.range( PROP_KEY, from, true, to, false ); // then - verifyQueryWithCorrectReader( spatialReader, geometryRange ); + verifyQueryWithCorrectReader( readers[SPATIAL], geometryRange ); } @Test @@ -266,7 +319,7 @@ public void mustSelectStringForStringPrefixPredicate() throws Exception StringPrefixPredicate stringPrefix = IndexQuery.stringPrefix( PROP_KEY, "abc" ); // then - verifyQueryWithCorrectReader( stringReader, stringPrefix ); + verifyQueryWithCorrectReader( expectedForStrings(), stringPrefix ); } @Test @@ -276,7 +329,7 @@ public void mustSelectStringForStringSuffixPredicate() throws Exception StringSuffixPredicate stringPrefix = IndexQuery.stringSuffix( PROP_KEY, "abc" ); // then - verifyQueryWithCorrectReader( stringReader, stringPrefix ); + verifyQueryWithCorrectReader( expectedForStrings(), stringPrefix ); } @Test @@ -286,7 +339,7 @@ public void mustSelectStringForStringContainsPredicate() throws Exception StringContainsPredicate stringContains = IndexQuery.stringContains( PROP_KEY, "abc" ); // then - verifyQueryWithCorrectReader( stringReader, stringContains ); + verifyQueryWithCorrectReader( expectedForStrings(), stringContains ); } @Test @@ -295,9 +348,9 @@ public void mustCombineResultFromExistsPredicate() throws Exception // given IndexQuery.ExistsPredicate exists = IndexQuery.exists( PROP_KEY ); long lastId = 0; - for ( int i = 0; i < allReaders.length; i++ ) + for ( IndexReader aliveReader : aliveReaders ) { - when( allReaders[i].query( exists ) ).thenReturn( PrimitiveLongResourceCollections.iterator( null, lastId++, lastId++ ) ); + when( aliveReader.query( exists ) ).thenReturn( PrimitiveLongResourceCollections.iterator( null, lastId++, lastId++ ) ); } // when @@ -319,7 +372,7 @@ private void verifyQueryWithCorrectReader( IndexReader expectedReader, IndexQuer // then verify( expectedReader, times( 1 ) ).query( indexQuery ); - for ( IndexReader reader : allReaders ) + for ( IndexReader reader : aliveReaders ) { if ( reader != expectedReader ) { @@ -327,4 +380,29 @@ private void verifyQueryWithCorrectReader( IndexReader expectedReader, IndexQuer } } } + + private IndexReader expectedForStrings() + { + return orLucene( readers[STRING] ); + } + + private IndexReader expectedForNumbers() + { + return orLucene( readers[NUMBER] ); + } + + private boolean hasSpatialSupport() + { + return readers[SPATIAL] != IndexReader.EMPTY; + } + + private boolean hasTemporalSupport() + { + return readers[TEMPORAL] != IndexReader.EMPTY; + } + + private IndexReader orLucene( IndexReader reader ) + { + return reader != IndexReader.EMPTY ? reader : readers[LUCENE]; + } } 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 f4cc32f5b32ab..0f5ad1a8105f1 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 @@ -22,45 +22,106 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import java.io.IOException; +import java.util.Arrays; import org.neo4j.internal.kernel.api.schema.LabelSchemaDescriptor; 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.api.index.updater.SwallowingIndexUpdater; import org.neo4j.test.rule.RandomRule; import org.neo4j.values.storable.Value; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.neo4j.helpers.ArrayUtil.without; -import static org.neo4j.helpers.collection.Iterators.array; +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; +@RunWith( Parameterized.class ) public class FusionIndexUpdaterTest { - private IndexUpdater luceneUpdater; - private IndexUpdater[] allUpdaters; + private IndexUpdater[] aliveUpdaters; + private IndexUpdater[] updaters; private FusionIndexUpdater fusionIndexUpdater; @Rule public RandomRule random = new RandomRule(); + @Parameterized.Parameters( name = "{0}" ) + public static FusionVersion[] versions() + { + return new FusionVersion[] + { + v00, v10, v20 + }; + } + + @Parameterized.Parameter + public static FusionVersion fusionVersion; @Before - public void mockComponents() + public void setup() + { + initiateMocks(); + } + + private void initiateMocks() + { + int[] activeSlots = fusionVersion.aliveSlots(); + updaters = new IndexUpdater[INSTANCE_COUNT]; + Arrays.fill( updaters, SwallowingIndexUpdater.INSTANCE ); + aliveUpdaters = new IndexUpdater[activeSlots.length]; + for ( int i = 0; i < activeSlots.length; i++ ) + { + IndexUpdater mock = mock( IndexUpdater.class ); + aliveUpdaters[i] = mock; + switch ( activeSlots[i] ) + { + case STRING: + updaters[STRING] = mock; + break; + case NUMBER: + updaters[NUMBER] = mock; + break; + case SPATIAL: + updaters[SPATIAL] = mock; + break; + case TEMPORAL: + updaters[TEMPORAL] = mock; + break; + case LUCENE: + updaters[LUCENE] = mock; + break; + default: + throw new RuntimeException(); + } + } + fusionIndexUpdater = new FusionIndexUpdater( updaters, fusionVersion.selector() ); + } + + private void resetMocks() { - IndexUpdater stringUpdater = mock( IndexUpdater.class ); - IndexUpdater numberUpdater = mock( IndexUpdater.class ); - IndexUpdater spatialUpdater = mock( IndexUpdater.class ); - IndexUpdater temporalUpdater = mock( IndexUpdater.class ); - luceneUpdater = mock( IndexUpdater.class ); - allUpdaters = array( stringUpdater, numberUpdater, spatialUpdater, temporalUpdater, luceneUpdater ); - fusionIndexUpdater = new FusionIndexUpdater( allUpdaters, new FusionSelector() ); + for ( IndexUpdater updater : aliveUpdaters ) + { + reset( updater ); + } } /* process */ @@ -72,12 +133,12 @@ public void processMustSelectCorrectForAdd() throws Exception Value[][] values = FusionIndexTestHelp.valuesByGroup(); Value[] allValues = FusionIndexTestHelp.allValues(); - for ( int i = 0; i < allUpdaters.length; i++ ) + for ( int i = 0; i < updaters.length; i++ ) { for ( Value value : values[i] ) { // then - verifyAddWithCorrectUpdater( allUpdaters[i], value ); + verifyAddWithCorrectUpdater( orLucene( updaters[i] ), value ); } } @@ -86,7 +147,7 @@ public void processMustSelectCorrectForAdd() throws Exception { for ( Value secondValue : allValues ) { - verifyAddWithCorrectUpdater( luceneUpdater, firstValue, secondValue ); + verifyAddWithCorrectUpdater( updaters[LUCENE], firstValue, secondValue ); } } } @@ -98,12 +159,12 @@ public void processMustSelectCorrectForRemove() throws Exception Value[][] values = FusionIndexTestHelp.valuesByGroup(); Value[] allValues = FusionIndexTestHelp.allValues(); - for ( int i = 0; i < allUpdaters.length; i++ ) + for ( int i = 0; i < updaters.length; i++ ) { for ( Value value : values[i] ) { // then - verifyRemoveWithCorrectUpdater( allUpdaters[i], value ); + verifyRemoveWithCorrectUpdater( orLucene( updaters[i] ), value ); } } @@ -112,7 +173,7 @@ public void processMustSelectCorrectForRemove() throws Exception { for ( Value secondValue : allValues ) { - verifyRemoveWithCorrectUpdater( luceneUpdater, firstValue, secondValue ); + verifyRemoveWithCorrectUpdater( updaters[LUCENE], firstValue, secondValue ); } } } @@ -124,13 +185,13 @@ public void processMustSelectCorrectForChange() throws Exception Value[][] values = FusionIndexTestHelp.valuesByGroup(); // when - for ( int i = 0; i < allUpdaters.length; i++ ) + for ( int i = 0; i < updaters.length; i++ ) { for ( Value before : values[i] ) { for ( Value after : values[i] ) { - verifyChangeWithCorrectUpdaterNotMixed( allUpdaters[i], before, after ); + verifyChangeWithCorrectUpdaterNotMixed( orLucene( updaters[i] ), before, after ); } } } @@ -148,24 +209,29 @@ public void processMustSelectCorrectForChangeFromOneGroupToAnother() throws Exce if ( f != t ) { // when - verifyChangeWithCorrectUpdaterMixed( allUpdaters[f], allUpdaters[t], values[f], values[t] ); + verifyChangeWithCorrectUpdaterMixed( orLucene( updaters[f] ), orLucene( updaters[t] ), values[f], values[t] ); } else { - verifyChangeWithCorrectUpdaterNotMixed( allUpdaters[f], values[f] ); + verifyChangeWithCorrectUpdaterNotMixed( orLucene( updaters[f] ), values[f] ); } - mockComponents(); + resetMocks(); } } } + private IndexUpdater orLucene( IndexUpdater updater ) + { + return updater != SwallowingIndexUpdater.INSTANCE ? updater : updaters[LUCENE]; + } + private void verifyAddWithCorrectUpdater( IndexUpdater correctPopulator, Value... numberValues ) throws IndexEntryConflictException, IOException { IndexEntryUpdate update = add( numberValues ); fusionIndexUpdater.process( update ); verify( correctPopulator, times( 1 ) ).process( update ); - for ( IndexUpdater populator : allUpdaters ) + for ( IndexUpdater populator : aliveUpdaters ) { if ( populator != correctPopulator ) { @@ -180,7 +246,7 @@ private void verifyRemoveWithCorrectUpdater( IndexUpdater correctPopulator, Valu IndexEntryUpdate update = FusionIndexTestHelp.remove( numberValues ); fusionIndexUpdater.process( update ); verify( correctPopulator, times( 1 ) ).process( update ); - for ( IndexUpdater populator : allUpdaters ) + for ( IndexUpdater populator : aliveUpdaters ) { if ( populator != correctPopulator ) { @@ -195,7 +261,7 @@ private void verifyChangeWithCorrectUpdaterNotMixed( IndexUpdater correctPopulat IndexEntryUpdate update = FusionIndexTestHelp.change( before, after ); fusionIndexUpdater.process( update ); verify( correctPopulator, times( 1 ) ).process( update ); - for ( IndexUpdater populator : allUpdaters ) + for ( IndexUpdater populator : aliveUpdaters ) { if ( populator != correctPopulator ) { @@ -228,8 +294,15 @@ private void verifyChangeWithCorrectUpdaterMixed( IndexUpdater expectRemoveFrom, IndexEntryUpdate change = change( before, after ); fusionIndexUpdater.process( change ); - verify( expectRemoveFrom, times( afterIndex + 1 ) ).process( remove( before ) ); - verify( expectAddTo, times( beforeIndex + 1 ) ).process( add( after ) ); + if ( expectRemoveFrom != expectAddTo ) + { + verify( expectRemoveFrom, times( afterIndex + 1 ) ).process( remove( before ) ); + verify( expectAddTo, times( beforeIndex + 1 ) ).process( add( after ) ); + } + else + { + verify( expectRemoveFrom, times( 1 ) ).process( change( before, after ) ); + } } } } @@ -243,7 +316,7 @@ public void closeMustCloseAll() throws Exception fusionIndexUpdater.close(); // then - for ( IndexUpdater updater : allUpdaters ) + for ( IndexUpdater updater : aliveUpdaters ) { verify( updater, times( 1 ) ).close(); } @@ -252,28 +325,26 @@ public void closeMustCloseAll() throws Exception @Test public void closeMustThrowIfAnyThrow() throws Exception { - for ( int i = 0; i < allUpdaters.length; i++ ) + for ( IndexUpdater updater : aliveUpdaters ) { - IndexUpdater updater = allUpdaters[i]; FusionIndexTestHelp.verifyFusionCloseThrowOnSingleCloseThrow( updater, fusionIndexUpdater ); - mockComponents(); + resetMocks(); } } @Test public void closeMustCloseOthersIfAnyThrow() throws Exception { - for ( int i = 0; i < allUpdaters.length; i++ ) + for ( IndexUpdater updater : aliveUpdaters ) { - IndexUpdater updater = allUpdaters[i]; - FusionIndexTestHelp.verifyOtherIsClosedOnSingleThrow( updater, fusionIndexUpdater, without( allUpdaters, updater ) ); - mockComponents(); + FusionIndexTestHelp.verifyOtherIsClosedOnSingleThrow( updater, fusionIndexUpdater, without( aliveUpdaters, updater ) ); + resetMocks(); } } @Test public void closeMustThrowIfAllThrow() throws Exception { - FusionIndexTestHelp.verifyFusionCloseThrowIfAllThrow( fusionIndexUpdater, allUpdaters ); + FusionIndexTestHelp.verifyFusionCloseThrowIfAllThrow( fusionIndexUpdater, aliveUpdaters ); } } 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 new file mode 100644 index 0000000000000..52d1a2d9ccb1d --- /dev/null +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/fusion/FusionVersion.java @@ -0,0 +1,76 @@ +/* + * 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 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; + +enum FusionVersion +{ + v00 + { + @Override + int[] aliveSlots() + { + return new int[]{LUCENE}; + } + + @Override + FusionIndexProvider.Selector selector() + { + return new FusionSelector00(); + } + }, + v10 + { + @Override + int[] aliveSlots() + { + return new int[]{NUMBER, LUCENE}; + } + + @Override + FusionIndexProvider.Selector selector() + { + return new FusionSelector10(); + } + }, + v20 + { + @Override + int[] aliveSlots() + { + return new int[]{STRING, NUMBER, SPATIAL, TEMPORAL, LUCENE}; + } + + @Override + FusionIndexProvider.Selector selector() + { + return new FusionSelector20(); + } + }; + + abstract int[] aliveSlots(); + + abstract FusionIndexProvider.Selector selector(); +} diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/command/HighIdTransactionApplierTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/command/HighIdTransactionApplierTest.java index f29f6ec90d249..4cb38fe879070 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/command/HighIdTransactionApplierTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/command/HighIdTransactionApplierTest.java @@ -34,7 +34,7 @@ import org.neo4j.test.rule.NeoStoresRule; import static org.junit.Assert.assertEquals; -import static org.neo4j.kernel.api.index.IndexProvider.NO_INDEX_PROVIDER; +import static org.neo4j.kernel.api.index.IndexProvider.EMPTY; public class HighIdTransactionApplierTest { @@ -75,9 +75,9 @@ public void shouldUpdateHighIdsOnExternalTransaction() throws Exception // Schema rules tracker.visitSchemaRuleCommand( Commands.createIndexRule( - NO_INDEX_PROVIDER.getProviderDescriptor(), 10, SchemaDescriptorFactory.forLabel( 0, 1 ) ) ); + EMPTY.getProviderDescriptor(), 10, SchemaDescriptorFactory.forLabel( 0, 1 ) ) ); tracker.visitSchemaRuleCommand( Commands.createIndexRule( - NO_INDEX_PROVIDER.getProviderDescriptor(), 20, SchemaDescriptorFactory.forLabel( 1, 2 ) ) ); + EMPTY.getProviderDescriptor(), 20, SchemaDescriptorFactory.forLabel( 1, 2 ) ) ); // Properties tracker.visitPropertyCommand( Commands.createProperty( 10, PropertyType.STRING, 0, 6, 7 ) ); diff --git a/community/kernel/src/test/java/org/neo4j/test/rule/NeoStoreDataSourceRule.java b/community/kernel/src/test/java/org/neo4j/test/rule/NeoStoreDataSourceRule.java index 49209bffa35a5..a460338940c13 100644 --- a/community/kernel/src/test/java/org/neo4j/test/rule/NeoStoreDataSourceRule.java +++ b/community/kernel/src/test/java/org/neo4j/test/rule/NeoStoreDataSourceRule.java @@ -180,7 +180,7 @@ public T resolveDependency( Class type, SelectionStrategy selector ) thro { if ( IndexProvider.class.isAssignableFrom( type ) ) { - return type.cast( IndexProvider.NO_INDEX_PROVIDER ); + return type.cast( IndexProvider.EMPTY ); } throw new IllegalArgumentException( type.toString() ); } diff --git a/community/kernel/src/test/java/org/neo4j/test/rule/RecordStorageEngineRule.java b/community/kernel/src/test/java/org/neo4j/test/rule/RecordStorageEngineRule.java index 82a53617124ef..8aa8786ab0c89 100644 --- a/community/kernel/src/test/java/org/neo4j/test/rule/RecordStorageEngineRule.java +++ b/community/kernel/src/test/java/org/neo4j/test/rule/RecordStorageEngineRule.java @@ -140,7 +140,7 @@ public class Builder private File storeDirectory = new File( "/graph.db" ); private Function transactionApplierTransformer = applierFacade -> applierFacade; - private IndexProvider indexProvider = IndexProvider.NO_INDEX_PROVIDER; + private IndexProvider indexProvider = IndexProvider.EMPTY; private Monitors monitors = new Monitors(); public Builder( FileSystemAbstraction fs, PageCache pageCache ) diff --git a/community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/schema/NativeLuceneFusionIndexProviderFactory.java b/community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/schema/NativeLuceneFusionIndexProviderFactory.java index bf9a638d30965..21a4bed402352 100644 --- a/community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/schema/NativeLuceneFusionIndexProviderFactory.java +++ b/community/lucene-index/src/main/java/org/neo4j/kernel/api/impl/schema/NativeLuceneFusionIndexProviderFactory.java @@ -33,17 +33,15 @@ import org.neo4j.kernel.extension.KernelExtensionFactory; import org.neo4j.kernel.impl.factory.OperationalMode; import org.neo4j.kernel.impl.index.schema.NumberIndexProvider; -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; -import org.neo4j.kernel.impl.index.schema.fusion.FusionSelector; +import org.neo4j.kernel.impl.index.schema.fusion.FusionSelector10; import org.neo4j.kernel.impl.spi.KernelContext; import org.neo4j.kernel.monitoring.Monitors; import org.neo4j.logging.Log; import static org.neo4j.kernel.api.index.IndexDirectoryStructure.directoriesByProvider; import static org.neo4j.kernel.api.index.IndexDirectoryStructure.directoriesBySubProvider; +import static org.neo4j.kernel.api.index.IndexProvider.EMPTY; @Service.Implementation( KernelExtensionFactory.class ) public class NativeLuceneFusionIndexProviderFactory @@ -88,20 +86,17 @@ public static FusionIndexProvider newInstance( PageCache pageCache, File storeDi { IndexDirectoryStructure.Factory childDirectoryStructure = subProviderDirectoryStructure( storeDir ); boolean readOnly = isReadOnly( config, operationalMode ); - StringIndexProvider stringProvider = - new StringIndexProvider( pageCache, fs, childDirectoryStructure, monitor, recoveryCleanupWorkCollector, readOnly ); + + // This is the v1.0 of the lucene+native fusion setup where there's only number+lucene indexes NumberIndexProvider numberProvider = new NumberIndexProvider( pageCache, fs, childDirectoryStructure, monitor, recoveryCleanupWorkCollector, readOnly ); - SpatialIndexProvider spatialProvider = - new SpatialIndexProvider( pageCache, fs, childDirectoryStructure, monitor, recoveryCleanupWorkCollector, readOnly, config ); - TemporalIndexProvider temporalProvider = - new TemporalIndexProvider( pageCache, fs, childDirectoryStructure, monitor, recoveryCleanupWorkCollector, readOnly ); - LuceneIndexProvider luceneProvider = LuceneIndexProviderFactory.create( fs, childDirectoryStructure, monitor, config, - operationalMode ); + + LuceneIndexProvider luceneProvider = LuceneIndexProviderFactory.create( fs, childDirectoryStructure, monitor, config, operationalMode ); + boolean useNativeIndex = config.get( GraphDatabaseSettings.enable_native_schema_index ); int priority = useNativeIndex ? PRIORITY : 0; - return new FusionIndexProvider( stringProvider, numberProvider, - spatialProvider, temporalProvider, luceneProvider, new FusionSelector(), DESCRIPTOR, priority, directoriesByProvider( storeDir ), fs ); + return new FusionIndexProvider( EMPTY, numberProvider, + EMPTY, EMPTY, luceneProvider, new FusionSelector10(), DESCRIPTOR, priority, directoriesByProvider( storeDir ), fs ); } public static IndexDirectoryStructure.Factory subProviderDirectoryStructure( File storeDir )