diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/BlockBasedIndexPopulator.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/BlockBasedIndexPopulator.java index e59a046162c0..229b7cf1eda9 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/BlockBasedIndexPopulator.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/index/schema/BlockBasedIndexPopulator.java @@ -52,6 +52,7 @@ import org.neo4j.kernel.impl.index.schema.config.IndexSpecificSpaceFillingCurveSettingsCache; import org.neo4j.kernel.impl.index.schema.config.SpaceFillingCurveSettingsWriter; import org.neo4j.memory.LocalMemoryTracker; +import org.neo4j.memory.MemoryAllocationTracker; import org.neo4j.storageengine.api.schema.PopulationProgress; import org.neo4j.storageengine.api.schema.StoreIndexDescriptor; import org.neo4j.util.FeatureToggles; @@ -62,6 +63,7 @@ import static org.neo4j.kernel.impl.index.schema.BlockStorage.Monitor.NO_MONITOR; import static org.neo4j.kernel.impl.index.schema.NativeIndexUpdater.initializeKeyFromUpdate; import static org.neo4j.kernel.impl.index.schema.NativeIndexes.deleteIndex; +import static org.neo4j.util.concurrent.Runnables.runAll; public abstract class BlockBasedIndexPopulator,VALUE extends NativeIndexValue> extends NativeIndexPopulator { @@ -95,7 +97,7 @@ public abstract class BlockBasedIndexPopulator,V // written to in a synchronized method when creating new thread-local instances, read from when population completes private final List allScanUpdates = new CopyOnWriteArrayList<>(); private final ThreadLocal scanUpdates; - private final ByteBufferFactory bufferFactory = new UnsafeDirectByteBufferFactory( new LocalMemoryTracker() /*plug in actual tracker when available*/ ); + private final ByteBufferFactory bufferFactory; private IndexUpdateStorage externalUpdates; // written in a synchronized method when creating new thread-local instances, read when processing external updates private volatile boolean scanCompleted; @@ -112,13 +114,13 @@ public abstract class BlockBasedIndexPopulator,V IndexDirectoryStructure directoryStructure, IndexDropAction dropAction, boolean archiveFailedIndex ) { this( pageCache, fs, file, layout, monitor, descriptor, spatialSettings, directoryStructure, dropAction, archiveFailedIndex, parseBlockSize(), - MERGE_FACTOR, NO_MONITOR ); + MERGE_FACTOR, NO_MONITOR, new LocalMemoryTracker() /*plug in actual tracker when available*/ ); } BlockBasedIndexPopulator( PageCache pageCache, FileSystemAbstraction fs, File file, IndexLayout layout, IndexProvider.Monitor monitor, StoreIndexDescriptor descriptor, IndexSpecificSpaceFillingCurveSettingsCache spatialSettings, IndexDirectoryStructure directoryStructure, IndexDropAction dropAction, boolean archiveFailedIndex, - int blockSize, int mergeFactor, BlockStorage.Monitor blockStorageMonitor ) + int blockSize, int mergeFactor, BlockStorage.Monitor blockStorageMonitor, MemoryAllocationTracker memoryTracker ) { super( pageCache, fs, file, layout, monitor, descriptor, new SpaceFillingCurveSettingsWriter( spatialSettings ) ); this.directoryStructure = directoryStructure; @@ -128,6 +130,7 @@ public abstract class BlockBasedIndexPopulator,V this.mergeFactor = mergeFactor; this.blockStorageMonitor = blockStorageMonitor; this.scanUpdates = ThreadLocal.withInitial( this::newThreadLocalBlockStorage ); + this.bufferFactory = new UnsafeDirectByteBufferFactory( memoryTracker ); } private synchronized ThreadLocalBlockStorage newThreadLocalBlockStorage() @@ -237,28 +240,7 @@ public void scanCompleted( PhaseTracker phaseTracker ) throws IndexEntryConflict phaseTracker.enterPhase( PhaseTracker.Phase.MERGE ); if ( !allScanUpdates.isEmpty() ) { - ExecutorService executorService = Executors.newFixedThreadPool( allScanUpdates.size() ); - List> mergeFutures = new ArrayList<>(); - for ( ThreadLocalBlockStorage part : allScanUpdates ) - { - BlockStorage scanUpdates = part.blockStorage; - mergeFutures.add( executorService.submit( () -> - { - scanUpdates.doneAdding(); - scanUpdates.merge( mergeFactor, cancellation ); - return null; - } ) ); - } - executorService.shutdown(); - while ( !executorService.awaitTermination( 1, TimeUnit.SECONDS ) ) - { - // just wait longer - } - // Let potential exceptions in the merge threads have a chance to propagate - for ( Future mergeFuture : mergeFutures ) - { - mergeFuture.get(); - } + mergeScanUpdates(); } externalUpdates.doneAdding(); @@ -297,6 +279,32 @@ public void scanCompleted( PhaseTracker phaseTracker ) throws IndexEntryConflict } } + private void mergeScanUpdates() throws InterruptedException, ExecutionException + { + ExecutorService executorService = Executors.newFixedThreadPool( allScanUpdates.size() ); + List> mergeFutures = new ArrayList<>(); + for ( ThreadLocalBlockStorage part : allScanUpdates ) + { + BlockStorage scanUpdates = part.blockStorage; + mergeFutures.add( executorService.submit( () -> + { + scanUpdates.doneAdding(); + scanUpdates.merge( mergeFactor, cancellation ); + return null; + } ) ); + } + executorService.shutdown(); + while ( !executorService.awaitTermination( 1, TimeUnit.SECONDS ) ) + { + // just wait longer + } + // Let potential exceptions in the merge threads have a chance to propagate + for ( Future mergeFuture : mergeFutures ) + { + mergeFuture.get(); + } + } + /** * We will loop over all external updates once to add them to the tree. This is done without checking any uniqueness. * If index is a uniqueness index we will then loop over external updates again and for each ADD or CHANGED update @@ -473,32 +481,22 @@ private void assertOpen() @Override public synchronized void drop() { - try - { - // Close internal resources - closeBlockStorage(); - } - finally - { - // Super drop will close inherited resources - super.drop(); - // Cleanup files - dropAction.drop( descriptor.getId(), archiveFailedIndex ); - } + runAll( "Failed while trying to drop index", + this::closeBlockStorage /* Close internal resources */, + bufferFactory::close /* Free all allocated byte buffers */, + super::drop /* Super drop will close inherited resources */, + () -> dropAction.drop( descriptor.getId(), archiveFailedIndex ) /* Cleanup files */ + ); } @Override public synchronized void close( boolean populationCompletedSuccessfully ) { - try - { - closeBlockStorage(); - } - finally - { - bufferFactory.close(); - super.close( populationCompletedSuccessfully ); - } + runAll( "Failed while trying to close index", + this::closeBlockStorage /* Close internal resources */, + bufferFactory::close /* Free all allocated byte buffers */, + () -> super.close( populationCompletedSuccessfully ) /* Super close will close inherited resources */ + ); } // Always called from synchronized method diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/BlockBasedIndexPopulatorTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/BlockBasedIndexPopulatorTest.java index 68f0c9682ea8..f9e37cbf1c3b 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/BlockBasedIndexPopulatorTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/BlockBasedIndexPopulatorTest.java @@ -33,12 +33,16 @@ import org.neo4j.graphdb.mockfs.EphemeralFileSystemAbstraction; 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.memory.LocalMemoryTracker; +import org.neo4j.memory.MemoryAllocationTracker; import org.neo4j.storageengine.api.schema.IndexDescriptorFactory; import org.neo4j.storageengine.api.schema.PopulationProgress; import org.neo4j.storageengine.api.schema.StoreIndexDescriptor; @@ -93,20 +97,33 @@ public void shouldAwaitMergeToBeFullyAbortedBeforeLeavingCloseMethod() throws Ex { // given TrappingMonitor monitor = new TrappingMonitor( false ); - BlockBasedIndexPopulator populator = instantiatePopulatorWithSomeData( monitor ); - - // when starting to merge (in a separate thread) - Future mergeFuture = t2.execute( command( () -> populator.scanCompleted( nullInstance ) ) ); - // and waiting for merge to get going - monitor.barrier.awaitUninterruptibly(); - // calling close here should wait for the merge future, so that checking the merge future for "done" immediately afterwards must say true - Future closeFuture = t3.execute( command( () -> populator.close( false ) ) ); - t3.get().waitUntilWaiting(); - monitor.barrier.release(); - closeFuture.get(); - - // then - assertTrue( mergeFuture.isDone() ); + BlockBasedIndexPopulator populator = instantiatePopulator( monitor, new LocalMemoryTracker() ); + boolean closed = false; + try + { + populator.add( batchOfUpdates() ); + + // when starting to merge (in a separate thread) + Future mergeFuture = t2.execute( command( () -> populator.scanCompleted( nullInstance ) ) ); + // and waiting for merge to get going + monitor.barrier.awaitUninterruptibly(); + // calling close here should wait for the merge future, so that checking the merge future for "done" immediately afterwards must say true + Future closeFuture = t3.execute( command( () -> populator.close( false ) ) ); + t3.get().waitUntilWaiting(); + monitor.barrier.release(); + closeFuture.get(); + closed = true; + + // then + assertTrue( mergeFuture.isDone() ); + } + finally + { + if ( !closed ) + { + populator.close( true ); + } + } } @Test @@ -114,9 +131,11 @@ public void shouldReportAccurateProgressThroughoutThePhases() throws Exception { // given TrappingMonitor monitor = new TrappingMonitor( true ); - BlockBasedIndexPopulator populator = instantiatePopulatorWithSomeData( monitor ); + BlockBasedIndexPopulator populator = instantiatePopulator( monitor, new LocalMemoryTracker() ); try { + populator.add( batchOfUpdates() ); + // when starting to merge (in a separate thread) Future mergeFuture = t2.execute( command( () -> populator.scanCompleted( nullInstance ) ) ); // and waiting for merge to get going @@ -140,17 +159,30 @@ public void shouldReportAccurateProgressThroughoutThePhases() throws Exception public void shouldCorrectlyDecideToAwaitMergeDependingOnProgress() throws Throwable { // given - BlockBasedIndexPopulator populator = instantiatePopulatorWithSomeData( NO_MONITOR ); + BlockBasedIndexPopulator populator = instantiatePopulator( NO_MONITOR, new LocalMemoryTracker() ); + boolean closed = false; + try + { + populator.add( batchOfUpdates() ); - // when - Race race = new Race(); - race.addContestant( throwing( () -> populator.scanCompleted( nullInstance ) ) ); - race.addContestant( throwing( () -> populator.close( false ) ) ); - race.go(); + // when + Race race = new Race(); + race.addContestant( throwing( () -> populator.scanCompleted( nullInstance ) ) ); + race.addContestant( throwing( () -> populator.close( false ) ) ); + race.go(); + closed = true; - // then regardless of who wins (close/merge) after close call returns no files should still be mapped - EphemeralFileSystemAbstraction ephemeralFileSystem = (EphemeralFileSystemAbstraction) fs; - ephemeralFileSystem.assertNoOpenFiles(); + // then regardless of who wins (close/merge) after close call returns no files should still be mapped + EphemeralFileSystemAbstraction ephemeralFileSystem = (EphemeralFileSystemAbstraction) fs; + ephemeralFileSystem.assertNoOpenFiles(); + } + finally + { + if ( !closed ) + { + populator.close( true ); + } + } } @Test @@ -158,28 +190,119 @@ public void shouldDeleteDirectoryOnDrop() throws Exception { // given TrappingMonitor monitor = new TrappingMonitor( false ); - BlockBasedIndexPopulator populator = instantiatePopulatorWithSomeData( monitor ); - - // when starting to merge (in a separate thread) - Future mergeFuture = t2.execute( command( () -> populator.scanCompleted( nullInstance ) ) ); - // and waiting for merge to get going - monitor.barrier.awaitUninterruptibly(); - // calling drop here should wait for the merge future and then delete index directory - assertTrue( fs.fileExists( indexDir ) ); - assertTrue( fs.isDirectory( indexDir ) ); - assertTrue( fs.listFiles( indexDir ).length > 0 ); - - Future dropFuture = t3.execute( command( populator::drop ) ); - t3.get().waitUntilWaiting(); - monitor.barrier.release(); - dropFuture.get(); - - // then - assertTrue( mergeFuture.isDone() ); - assertFalse( fs.fileExists( indexDir ) ); + BlockBasedIndexPopulator populator = instantiatePopulator( monitor, new LocalMemoryTracker() ); + boolean closed = false; + try + { + populator.add( batchOfUpdates() ); + + // when starting to merge (in a separate thread) + Future mergeFuture = t2.execute( command( () -> populator.scanCompleted( nullInstance ) ) ); + // and waiting for merge to get going + monitor.barrier.awaitUninterruptibly(); + // calling drop here should wait for the merge future and then delete index directory + assertTrue( fs.fileExists( indexDir ) ); + assertTrue( fs.isDirectory( indexDir ) ); + assertTrue( fs.listFiles( indexDir ).length > 0 ); + + Future dropFuture = t3.execute( command( populator::drop ) ); + t3.get().waitUntilWaiting(); + monitor.barrier.release(); + dropFuture.get(); + closed = true; + + // then + assertTrue( mergeFuture.isDone() ); + assertFalse( fs.fileExists( indexDir ) ); + } + finally + { + if ( !closed ) + { + populator.close( true ); + } + } } - private BlockBasedIndexPopulator instantiatePopulatorWithSomeData( BlockStorage.Monitor monitor ) + @Test + public void shouldDeallocateAllAllocatedMemoryOnClose() throws IndexEntryConflictException + { + // given + LocalMemoryTracker memoryTracker = new LocalMemoryTracker(); + BlockBasedIndexPopulator populator = instantiatePopulator( NO_MONITOR, memoryTracker ); + boolean closed = false; + try + { + // when + Collection> updates = batchOfUpdates(); + populator.add( updates ); + int nextId = updates.size(); + externalUpdates( populator, nextId, nextId + 10 ); + nextId = nextId + 10; + populator.scanCompleted( nullInstance ); + externalUpdates( populator, nextId, nextId + 10 ); + + // then + assertTrue( "expected some memory to be in use", memoryTracker.usedDirectMemory() > 0 ); + populator.close( true ); + assertEquals( "expected all allocated memory to have been freed on close", 0, memoryTracker.usedDirectMemory() ); + closed = true; + } + finally + { + if ( !closed ) + { + populator.close( true ); + } + } + } + + @Test + public void shouldDeallocateAllAllocatedMemoryOnDrop() throws IndexEntryConflictException + { + // given + LocalMemoryTracker memoryTracker = new LocalMemoryTracker(); + BlockBasedIndexPopulator populator = instantiatePopulator( NO_MONITOR, memoryTracker ); + boolean closed = false; + try + { + // when + Collection> updates = batchOfUpdates(); + populator.add( updates ); + int nextId = updates.size(); + externalUpdates( populator, nextId, nextId + 10 ); + nextId = nextId + 10; + populator.scanCompleted( nullInstance ); + externalUpdates( populator, nextId, nextId + 10 ); + + // then + assertTrue( "expected some memory to be in use", memoryTracker.usedDirectMemory() > 0 ); + populator.drop(); + closed = true; + assertEquals( "expected all allocated memory to have been freed on drop", 0, memoryTracker.usedDirectMemory() ); + } + finally + { + if ( !closed ) + { + populator.close( true ); + } + } + } + + private void externalUpdates( BlockBasedIndexPopulator populator, int firstId, int lastId ) + throws IndexEntryConflictException + { + try ( IndexUpdater updater = populator.newPopulatingUpdater() ) + { + for ( int i = firstId; i < lastId; i++ ) + { + updater.process( add( i ) ); + } + } + } + + private BlockBasedIndexPopulator instantiatePopulator( BlockStorage.Monitor monitor, MemoryAllocationTracker memoryTracker ) { Config config = Config.defaults(); ConfiguredSpaceFillingCurveSettingsCache settingsCache = new ConfiguredSpaceFillingCurveSettingsCache( config ); @@ -187,7 +310,7 @@ private BlockBasedIndexPopulator instantiatePopulat GenericLayout layout = new GenericLayout( 1, spatialSettings ); BlockBasedIndexPopulator populator = new BlockBasedIndexPopulator( storage.pageCache(), fs, indexFile, layout, EMPTY, - INDEX_DESCRIPTOR, spatialSettings, directoryStructure, dropAction, false, 100, 2, monitor ) + INDEX_DESCRIPTOR, spatialSettings, directoryStructure, dropAction, false, 100, 2, monitor, memoryTracker ) { @Override NativeIndexReader newReader() @@ -196,7 +319,6 @@ NativeIndexReader newReader() } }; populator.create(); - populator.add( batchOfUpdates() ); return populator; } @@ -205,11 +327,16 @@ private static Collection> batchOfUpdates() List> updates = new ArrayList<>(); for ( int i = 0; i < 50; i++ ) { - updates.add( IndexEntryUpdate.add( i, INDEX_DESCRIPTOR, stringValue( "Value" + i ) ) ); + updates.add( add( i ) ); } return updates; } + private static IndexEntryUpdate add( int i ) + { + return IndexEntryUpdate.add( i, INDEX_DESCRIPTOR, stringValue( "Value" + i ) ); + } + private static class TrappingMonitor extends BlockStorage.Monitor.Adapter { private final Barrier.Control barrier = new Barrier.Control(); diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/GenericBlockBasedIndexPopulatorTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/GenericBlockBasedIndexPopulatorTest.java new file mode 100644 index 000000000000..c8faadeecb53 --- /dev/null +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/index/schema/GenericBlockBasedIndexPopulatorTest.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2002-2019 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.impl.index.schema; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.io.File; +import java.util.HashMap; + +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.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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +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.impl.api.index.PhaseTracker.nullInstance; +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 IndexDirectoryStructure directoryStructure; + private File indexFile; + private FileSystemAbstraction fs; + private IndexDropAction dropAction; + + @Before + public void setup() + { + IndexProviderDescriptor providerDescriptor = new IndexProviderDescriptor( "test", "v1" ); + directoryStructure = directoriesByProvider( storage.directory().databaseDir() ).forProvider( providerDescriptor ); + File indexDir = directoryStructure.directoryForIndex( INDEX_DESCRIPTOR.getId() ); + indexFile = new File( indexDir, "index" ); + fs = storage.fileSystem(); + dropAction = new FileSystemIndexDropAction( fs, directoryStructure ); + } + + @Rule + public final PageCacheAndDependenciesRule storage = new PageCacheAndDependenciesRule(); + + @Test + public void shouldSeeExternalUpdateBothBeforeAndAfterScanCompleted() throws IndexEntryConflictException + { + // given + BlockBasedIndexPopulator populator = instantiatePopulator(); + try + { + // when + TextValue hakuna = stringValue( "hakuna" ); + TextValue matata = stringValue( "matata" ); + int hakunaId = 1; + int matataId = 2; + externalUpdate( populator, hakuna, hakunaId ); + populator.scanCompleted( nullInstance ); + externalUpdate( populator, matata, matataId ); + + // then + assertMatch( populator, hakuna, hakunaId ); + assertMatch( populator, matata, matataId ); + } + finally + { + populator.close( true ); + } + } + + private void externalUpdate( BlockBasedIndexPopulator populator, TextValue matata, int matataId ) + throws IndexEntryConflictException + { + try ( IndexUpdater indexUpdater = populator.newPopulatingUpdater() ) + { + // After scanCompleted + indexUpdater.process( add( matataId, INDEX_DESCRIPTOR, matata ) ); + } + } + + private void assertMatch( BlockBasedIndexPopulator populator, Value value, long id ) + { + try ( NativeIndexReader reader = populator.newReader() ) + { + SimpleNodeValueClient cursor = new SimpleNodeValueClient(); + reader.query( cursor, IndexOrder.NONE, true, IndexQuery.exact( INDEX_DESCRIPTOR.properties()[0], value ) ); + assertTrue( cursor.next() ); + assertEquals( id, cursor.reference ); + assertEquals( value, cursor.values[0] ); + assertFalse( cursor.next() ); + } + } + + private GenericBlockBasedIndexPopulator instantiatePopulator() + { + Config config = Config.defaults(); + ConfiguredSpaceFillingCurveSettingsCache settingsCache = new ConfiguredSpaceFillingCurveSettingsCache( config ); + IndexSpecificSpaceFillingCurveSettingsCache spatialSettings = new IndexSpecificSpaceFillingCurveSettingsCache( settingsCache, new HashMap<>() ); + GenericLayout layout = new GenericLayout( 1, spatialSettings ); + SpaceFillingCurveConfiguration configuration = + SpaceFillingCurveSettingsFactory.getConfiguredSpaceFillingCurveConfiguration( config ); + GenericBlockBasedIndexPopulator populator = + new GenericBlockBasedIndexPopulator( storage.pageCache(), fs, indexFile, layout, EMPTY, INDEX_DESCRIPTOR, spatialSettings, directoryStructure, + configuration, dropAction, false ); + populator.create(); + return populator; + } +}