Skip to content

Commit

Permalink
Acceptance test, BlockBasedIndexPopulator don't throw on false negatives
Browse files Browse the repository at this point in the history
When BlockBasedIndexPopulator run into duplicate values during scan that
are fixed by external updates, it should not throw violation exception.
It should only throw if it really ends up with duplicate values after
tree is built.
  • Loading branch information
burqen committed Mar 28, 2019
1 parent 0cc84f1 commit 149e602
Showing 1 changed file with 167 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,38 +26,45 @@
import java.io.File;
import java.util.HashMap;

import org.neo4j.collection.PrimitiveLongResourceIterator;
import org.neo4j.gis.spatial.index.curves.SpaceFillingCurveConfiguration;
import org.neo4j.internal.kernel.api.IndexOrder;
import org.neo4j.internal.kernel.api.IndexQuery;
import org.neo4j.internal.kernel.api.schema.IndexProviderDescriptor;
import org.neo4j.io.fs.FileSystemAbstraction;
import org.neo4j.kernel.api.exceptions.index.IndexEntryConflictException;
import org.neo4j.kernel.api.index.IndexDirectoryStructure;
import org.neo4j.kernel.api.index.IndexEntryUpdate;
import org.neo4j.kernel.api.index.IndexUpdater;
import org.neo4j.kernel.api.schema.SchemaDescriptorFactory;
import org.neo4j.kernel.configuration.Config;
import org.neo4j.kernel.impl.index.schema.config.ConfiguredSpaceFillingCurveSettingsCache;
import org.neo4j.kernel.impl.index.schema.config.IndexSpecificSpaceFillingCurveSettingsCache;
import org.neo4j.kernel.impl.index.schema.config.SpaceFillingCurveSettingsFactory;
import org.neo4j.storageengine.api.schema.IndexDescriptorFactory;
import org.neo4j.storageengine.api.schema.SimpleNodeValueClient;
import org.neo4j.storageengine.api.schema.StoreIndexDescriptor;
import org.neo4j.test.rule.PageCacheAndDependenciesRule;
import org.neo4j.values.storable.TextValue;
import org.neo4j.values.storable.Value;
import org.neo4j.values.storable.Values;

import static java.util.Collections.singleton;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.neo4j.kernel.api.index.IndexDirectoryStructure.directoriesByProvider;
import static org.neo4j.kernel.api.index.IndexEntryUpdate.add;
import static org.neo4j.kernel.api.index.IndexProvider.Monitor.EMPTY;
import static org.neo4j.kernel.api.schema.SchemaDescriptorFactory.forLabel;
import static org.neo4j.kernel.impl.api.index.PhaseTracker.nullInstance;
import static org.neo4j.storageengine.api.schema.IndexDescriptorFactory.forSchema;
import static org.neo4j.storageengine.api.schema.IndexDescriptorFactory.uniqueForSchema;
import static org.neo4j.values.storable.Values.stringValue;

public class GenericBlockBasedIndexPopulatorTest
{
private static final StoreIndexDescriptor INDEX_DESCRIPTOR = IndexDescriptorFactory.forSchema( SchemaDescriptorFactory.forLabel( 1, 1 ) ).withId( 1 );
private static final StoreIndexDescriptor INDEX_DESCRIPTOR = forSchema( forLabel( 1, 1 ) ).withId( 1 );
private static final StoreIndexDescriptor UNIQUE_INDEX_DESCRIPTOR = uniqueForSchema( forLabel( 1, 1 ) ).withId( 1 );

private IndexDirectoryStructure directoryStructure;
private File indexFile;
Expand All @@ -82,7 +89,7 @@ public void setup()
public void shouldSeeExternalUpdateBothBeforeAndAfterScanCompleted() throws IndexEntryConflictException
{
// given
BlockBasedIndexPopulator<GenericKey,NativeIndexValue> populator = instantiatePopulator();
BlockBasedIndexPopulator<GenericKey,NativeIndexValue> populator = instantiatePopulator( INDEX_DESCRIPTOR );
try
{
// when
Expand All @@ -104,6 +111,160 @@ public void shouldSeeExternalUpdateBothBeforeAndAfterScanCompleted() throws Inde
}
}

@Test
public void shouldThrowOnDuplicatedValuesFromScan()
{
// given
BlockBasedIndexPopulator<GenericKey,NativeIndexValue> populator = instantiatePopulator( UNIQUE_INDEX_DESCRIPTOR );
boolean closed = false;
try
{
// when
Value duplicate = Values.of( "duplicate" );
IndexEntryUpdate<?> firstScanUpdate = IndexEntryUpdate.add( 1, INDEX_DESCRIPTOR, duplicate );
IndexEntryUpdate<?> secondScanUpdate = IndexEntryUpdate.add( 2, INDEX_DESCRIPTOR, duplicate );
try
{
populator.add( singleton( firstScanUpdate ) );
populator.add( singleton( secondScanUpdate ) );
populator.scanCompleted( nullInstance );

fail( "Expected to throw" );
}
catch ( IndexEntryConflictException e )
{
// then
}
}
finally
{
if ( !closed )
{
populator.close( true );
}
}
}

@Test
public void shouldThrowOnDuplicatedValuesFromExternalUpdates()
{
// given
BlockBasedIndexPopulator<GenericKey,NativeIndexValue> populator = instantiatePopulator( UNIQUE_INDEX_DESCRIPTOR );
boolean closed = false;
try
{
// when
Value duplicate = Values.of( "duplicate" );
IndexEntryUpdate<?> firstExternalUpdate = IndexEntryUpdate.add( 1, INDEX_DESCRIPTOR, duplicate );
IndexEntryUpdate<?> secondExternalUpdate = IndexEntryUpdate.add( 2, INDEX_DESCRIPTOR, duplicate );
try
{
try ( IndexUpdater updater = populator.newPopulatingUpdater() )
{
updater.process( firstExternalUpdate );
updater.process( secondExternalUpdate );
}
populator.scanCompleted( nullInstance );

fail( "Expected to throw" );
}
catch ( IndexEntryConflictException e )
{
// then
}
}
finally
{
if ( !closed )
{
populator.close( true );
}
}
}

@Test
public void shouldThrowOnDuplicatedValuesFromScanAndExternalUpdates()
{
// given
BlockBasedIndexPopulator<GenericKey,NativeIndexValue> populator = instantiatePopulator( UNIQUE_INDEX_DESCRIPTOR );
boolean closed = false;
try
{
// when
Value duplicate = Values.of( "duplicate" );
IndexEntryUpdate<?> externalUpdate = IndexEntryUpdate.add( 1, INDEX_DESCRIPTOR, duplicate );
IndexEntryUpdate<?> scanUpdate = IndexEntryUpdate.add( 2, INDEX_DESCRIPTOR, duplicate );
try
{
try ( IndexUpdater updater = populator.newPopulatingUpdater() )
{
updater.process( externalUpdate );
}
populator.add( singleton( scanUpdate ) );
populator.scanCompleted( nullInstance );

fail( "Expected to throw" );
}
catch ( IndexEntryConflictException e )
{
// then
}
}
finally
{
if ( !closed )
{
populator.close( true );
}
}
}

@Test
public void shouldNotThrowOnDuplicationsLaterFixedByExternalUpdates() throws IndexEntryConflictException
{
// given
BlockBasedIndexPopulator<GenericKey,NativeIndexValue> populator = instantiatePopulator( UNIQUE_INDEX_DESCRIPTOR );
boolean closed = false;
try
{
// when
Value duplicate = Values.of( "duplicate" );
Value unique = Values.of( "unique" );
IndexEntryUpdate<?> firstScanUpdate = IndexEntryUpdate.add( 1, INDEX_DESCRIPTOR, duplicate );
IndexEntryUpdate<?> secondScanUpdate = IndexEntryUpdate.add( 2, INDEX_DESCRIPTOR, duplicate );
IndexEntryUpdate<?> externalUpdate = IndexEntryUpdate.change( 1, INDEX_DESCRIPTOR, duplicate, unique );
populator.add( singleton( firstScanUpdate ) );
try ( IndexUpdater updater = populator.newPopulatingUpdater() )
{
updater.process( externalUpdate );
}
populator.add( singleton( secondScanUpdate ) );
populator.scanCompleted( nullInstance );

// then
assertHasEntry( populator, unique, 1 );
assertHasEntry( populator, duplicate, 2 );
}
finally
{
if ( !closed )
{
populator.close( true );
}
}
}

private void assertHasEntry( BlockBasedIndexPopulator<GenericKey,NativeIndexValue> populator, Value duplicate, int expectedId )
{
try ( NativeIndexReader<GenericKey,NativeIndexValue> reader = populator.newReader() )
{
PrimitiveLongResourceIterator query = reader.query( IndexQuery.exact( INDEX_DESCRIPTOR.properties()[0], duplicate ) );
assertTrue( query.hasNext() );
long id = query.next();
assertEquals( expectedId, id );
}
}

private void externalUpdate( BlockBasedIndexPopulator<GenericKey,NativeIndexValue> populator, TextValue matata, int matataId )
throws IndexEntryConflictException
{
Expand All @@ -127,7 +288,7 @@ private void assertMatch( BlockBasedIndexPopulator<GenericKey,NativeIndexValue>
}
}

private GenericBlockBasedIndexPopulator instantiatePopulator()
private GenericBlockBasedIndexPopulator instantiatePopulator( StoreIndexDescriptor indexDescriptor )
{
Config config = Config.defaults();
ConfiguredSpaceFillingCurveSettingsCache settingsCache = new ConfiguredSpaceFillingCurveSettingsCache( config );
Expand All @@ -136,7 +297,7 @@ private GenericBlockBasedIndexPopulator instantiatePopulator()
SpaceFillingCurveConfiguration configuration =
SpaceFillingCurveSettingsFactory.getConfiguredSpaceFillingCurveConfiguration( config );
GenericBlockBasedIndexPopulator populator =
new GenericBlockBasedIndexPopulator( storage.pageCache(), fs, indexFile, layout, EMPTY, INDEX_DESCRIPTOR, spatialSettings, directoryStructure,
new GenericBlockBasedIndexPopulator( storage.pageCache(), fs, indexFile, layout, EMPTY, indexDescriptor, spatialSettings, directoryStructure,
configuration, dropAction, false );
populator.create();
return populator;
Expand Down

0 comments on commit 149e602

Please sign in to comment.