Skip to content

Commit

Permalink
Changes schema index directory structure for fusion index
Browse files Browse the repository at this point in the history
The lucene schema index provider puts its indexes in:

  `<db>/schema/index/lucene/<indexId>/...`

this commit doesn't change this, but the fusion index provider,
i.e. the combination of lucene+native gets a new structure driven by the need
to one day be able to do efficient background migration of schema indexes
and for convenience in the wild. The new structure looks like this:

  `<db>/schema/index/<providerKey>/<providerVersion>/<indexId>/...`

Including the sub-provider directory and specifically for this fusion index (for indexId 5):

  `<db>/schema/index/lucene_native-1.0/5/lucene-1.0/...`
  `<db>/schema/index/lucene_native-1.0/5/native-1.0...`

This has two benefits:

- all index data related to one index lives under one directory
- indexes on disk are also versioned, just like providers are

Since the fusion index also uses a lucene schema index provider internally,
this structure has been made pluggable and given to a SchemaIndexProvider
at construction time. See IndexDirectoryStructure. This class has all knowledge
about the base schema root directory and various directory structure implementations
collected in one place.

NOTE This change does not require any migration, except for databases recently
created, where the fusion index is enabled by default. Although no releases
has had the fusion index in them so that is not a problem.
  • Loading branch information
tinwelint committed Sep 8, 2017
1 parent b073819 commit 0373d09
Show file tree
Hide file tree
Showing 42 changed files with 484 additions and 168 deletions.
Expand Up @@ -195,7 +195,7 @@ public long getIndexStoreSize()
// Add schema index
MutableLong schemaSize = new MutableLong();
schemaIndexProviderMap.accept( provider ->
schemaSize.add( FileUtils.size( fs, provider.getSchemaIndexStoreDirectory( storePath ) ) ) );
schemaSize.add( FileUtils.size( fs, provider.directoryStructure().rootDirectory() ) ) );
size += schemaSize.longValue();

// Add label index
Expand Down
Expand Up @@ -35,6 +35,7 @@
import org.neo4j.io.pagecache.PageCache;
import org.neo4j.jmx.StoreSize;
import org.neo4j.kernel.NeoStoreDataSource;
import org.neo4j.kernel.api.index.IndexDirectoryStructure;
import org.neo4j.kernel.api.index.SchemaIndexProvider;
import org.neo4j.kernel.api.index.SchemaIndexProvider.Descriptor;
import org.neo4j.kernel.api.labelscan.LabelScanStore;
Expand Down Expand Up @@ -253,12 +254,16 @@ public void shouldCountAllIndexFiles() throws Exception
{
File schemaIndex = new File( storeDir, "schemaIndex" );
createFileOfSize( schemaIndex, 2 );
when( schemaIndexProvider.getSchemaIndexStoreDirectory( any() ) ).thenReturn( schemaIndex );
IndexDirectoryStructure directoryStructure = mock( IndexDirectoryStructure.class );
when( directoryStructure.rootDirectory() ).thenReturn( schemaIndex );
when( schemaIndexProvider.directoryStructure() ).thenReturn( directoryStructure );
}
{
File schemaIndex = new File( storeDir, "schemaIndex2" );
createFileOfSize( schemaIndex, 3 );
when( schemaIndexProvider2.getSchemaIndexStoreDirectory( any() ) ).thenReturn( schemaIndex );
IndexDirectoryStructure directoryStructure = mock( IndexDirectoryStructure.class );
when( directoryStructure.rootDirectory() ).thenReturn( schemaIndex );
when( schemaIndexProvider2.directoryStructure() ).thenReturn( directoryStructure );
}

// Label scan store
Expand Down
@@ -0,0 +1,213 @@
/*
* Copyright (c) 2002-2017 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.kernel.api.index;

import java.io.File;

import org.neo4j.kernel.api.index.SchemaIndexProvider.Descriptor;

import static org.neo4j.io.fs.FileUtils.path;

/**
* Dictates how directory structure looks for a {@link SchemaIndexProvider} and its indexes. Generally there's a
* {@link #rootDirectory() root directory} which contains all index directories in some shape and form.
* For getting a directory (which must be a sub-directory to the root directory) for a particular index there's the
* {@link #directoryForIndex(long)} method.
*
* These instances are created from a {@link Factory} which typically gets passed into a {@link SchemaIndexProvider} constructor,
* which then a {@link IndexDirectoryStructure} given its {@link Descriptor}.
*/
public abstract class IndexDirectoryStructure
{
/**
* Creates an {@link IndexDirectoryStructure} for a {@link Descriptor} for a {@link SchemaIndexProvider}.
*/
public interface Factory
{
IndexDirectoryStructure forProvider( SchemaIndexProvider.Descriptor descriptor );
}

private static class SubDirectoryByIndexId extends IndexDirectoryStructure
{
private final File providerRootFolder;

private SubDirectoryByIndexId( File providerRootFolder )
{
this.providerRootFolder = providerRootFolder;
}

@Override
public File rootDirectory()
{
return providerRootFolder;
}

@Override
public File directoryForIndex( long indexId )
{
return path( providerRootFolder, String.valueOf( indexId ) );
}
}

/**
* Returns the base schema index directory, i.e.
*
* <pre>
* &lt;db&gt;/schema/index/
* </pre>
*
* @param databaseStoreDir database store directory, i.e. {@code db} in the example above, where e.g. {@code nodestore} lives.
* @return the base directory of schema indexing.
*/
static final File baseSchemaIndexFolder( File databaseStoreDir )
{
return path( databaseStoreDir, "schema", "index" );
}

/**
* @param databaseStoreDir store directory of database, i.e. {@code db} in the example above.
* @return {@link Factory} for creating {@link IndexDirectoryStructure} returning directories looking something like:
*
* <pre>
* &lt;db&gt;/schema/index/&lt;providerKey&gt;/&lt;indexId&gt;/
* </pre>
*/
public static Factory directoriesByProviderKey( File databaseStoreDir )
{
return descriptor -> new SubDirectoryByIndexId(
path( baseSchemaIndexFolder( databaseStoreDir ), fileNameFriendly( descriptor.getKey() ) ) );
}

/**
* @param databaseStoreDir store directory of database, i.e. {@code db} in the example above.
* @return {@link Factory} for creating {@link IndexDirectoryStructure} returning directories looking something like:
*
* <pre>
* &lt;db&gt;/schema/index/&lt;providerKey&gt;-&lt;providerVersion&gt;/&lt;indexId&gt;/
* </pre>
*/
public static Factory directoriesByProvider( File databaseStoreDir )
{
return descriptor -> new SubDirectoryByIndexId(
path( baseSchemaIndexFolder( databaseStoreDir ), fileNameFriendly( descriptor ) ) );
}

/**
* @param directoryStructure existing {@link IndexDirectoryStructure}.
* @return a {@link Factory} returning an already existing {@link IndexDirectoryStructure}.
*/
public static Factory given( IndexDirectoryStructure directoryStructure )
{
return descriptor -> directoryStructure;
}

/**
* Useful when combining multiple {@link SchemaIndexProvider} into one.
*
* @param parentStructure {@link IndexDirectoryStructure} of the parent.
* @return {@link Factory} creating {@link IndexDirectoryStructure} looking something like:
*
* <pre>
* &lt;db&gt;/schema/index/.../&lt;indexId&gt;/&lt;childProviderKey&gt;-&lt;childProviderVersion&gt;/
* </pre>
*/
public static Factory directoriesBySubProvider( IndexDirectoryStructure parentStructure )
{
return new Factory()
{
@Override
public IndexDirectoryStructure forProvider( Descriptor descriptor )
{
return new IndexDirectoryStructure()
{
@Override
public File rootDirectory()
{
return parentStructure.rootDirectory();
}

@Override
public File directoryForIndex( long indexId )
{
return path( parentStructure.directoryForIndex( indexId ), fileNameFriendly( descriptor ) );
}
};
}
};
}

private static String fileNameFriendly( String name )
{
return name.replaceAll( "\\+", "_" );
}

private static String fileNameFriendly( Descriptor descriptor )
{
return fileNameFriendly( descriptor.getKey() + "-" + descriptor.getVersion() );
}

private static final IndexDirectoryStructure NO_DIRECTORY_STRUCTURE = new IndexDirectoryStructure()
{
@Override
public File rootDirectory()
{
return null; // meaning there's no persistent storage
}

@Override
public File directoryForIndex( long indexId )
{
return null; // meaning there's no persistent storage
}
};

/**
* Useful for some in-memory index providers or similar.
*/
public static final Factory NONE = descriptor -> NO_DIRECTORY_STRUCTURE;

/**
* Returns root directory. Must be above all sub-directories returned from {@link #directoryForIndex(long)}.
* Returns something equivalent to:
*
* <pre>
* &lt;db&gt;/schema/index/&lt;provider&gt;/
* </pre>
*
* @return {@link File} denoting root directory for this provider.
* May return {@code null} if there's no root directory, i.e. no persistent storage at all.
*/
public abstract File rootDirectory();

/**
* Returns a sub-directory (somewhere under {@link #rootDirectory()}) for a specific index id, looking something equivalent to:
*
* <pre>
* &lt;db&gt;/schema/index/&lt;provider&gt;/&lt;indexId&gt;/
* </pre>
*
* I.e. the root of the schema indexes for this specific provider.
*
* @param indexId index id to return directory for.
* @return {@link File} denoting directory for the specific {@code indexId} for this provider.
* May return {@code null} if there's no root directory, i.e. no persistent storage at all.
*/
public abstract File directoryForIndex( long indexId );
}
Expand Up @@ -93,7 +93,7 @@
public abstract class SchemaIndexProvider extends LifecycleAdapter implements Comparable<SchemaIndexProvider>
{
public static final SchemaIndexProvider NO_INDEX_PROVIDER =
new SchemaIndexProvider( new Descriptor( "no-index-provider", "1.0" ), -1 )
new SchemaIndexProvider( new Descriptor( "no-index-provider", "1.0" ), -1, IndexDirectoryStructure.NONE )
{
private final IndexAccessor singleWriter = new IndexAccessor.Adapter();
private final IndexPopulator singlePopulator = new IndexPopulator.Adapter();
Expand Down Expand Up @@ -134,17 +134,22 @@ public String getPopulationFailure( long indexId ) throws IllegalStateException

protected final int priority;
private final Descriptor providerDescriptor;
private final IndexDirectoryStructure.Factory directoryStructureFactory;
private final IndexDirectoryStructure directoryStructure;

protected SchemaIndexProvider( SchemaIndexProvider copySource )
{
this( copySource.providerDescriptor, copySource.priority );
this( copySource.providerDescriptor, copySource.priority, copySource.directoryStructureFactory );
}

protected SchemaIndexProvider( Descriptor descriptor, int priority )
protected SchemaIndexProvider( Descriptor descriptor, int priority,
IndexDirectoryStructure.Factory directoryStructureFactory )
{
this.directoryStructureFactory = directoryStructureFactory;
assert descriptor != null;
this.priority = priority;
this.providerDescriptor = descriptor;
this.directoryStructure = directoryStructureFactory.forProvider( descriptor );
}

/**
Expand Down Expand Up @@ -215,26 +220,20 @@ public int hashCode()
}

/**
* Get schema index store root directory in specified store.
* @param storeDir store root directory
* @return schema index store root directory
* @return {@link IndexDirectoryStructure} for this schema index provider. From it can be retrieved directories
* for individual indexes.
*/
public File getSchemaIndexStoreDirectory( File storeDir )
public IndexDirectoryStructure directoryStructure()
{
return getSchemaIndexStoreDirectory( storeDir, getProviderDescriptor() );
}

public static File getSchemaIndexStoreDirectory( File storeDir, Descriptor descriptor )
{
return new File( new File( new File( storeDir, "schema" ), "index" ), descriptor.getKey() );
return directoryStructure;
}

public abstract StoreMigrationParticipant storeMigrationParticipant( FileSystemAbstraction fs, PageCache pageCache );

/**
* Provides a snapshot of meta files about this index provider, not the indexes themselves.
*
* @return {@link ResourceIterator<File>} over all meta files for this index provider.
* @return {@link ResourceIterator} over all meta files for this index provider.
*/
public ResourceIterator<File> snapshotMetaFiles()
{
Expand Down
Expand Up @@ -28,6 +28,7 @@
import org.neo4j.io.fs.FileSystemAbstraction;
import org.neo4j.io.pagecache.PageCache;
import org.neo4j.kernel.api.index.IndexAccessor;
import org.neo4j.kernel.api.index.IndexDirectoryStructure;
import org.neo4j.kernel.api.index.IndexPopulator;
import org.neo4j.kernel.api.index.InternalIndexState;
import org.neo4j.kernel.api.index.SchemaIndexProvider;
Expand All @@ -48,20 +49,27 @@ public class NativeSchemaNumberIndexProvider extends SchemaIndexProvider
{
public static final String KEY = "native";
public static final Descriptor NATIVE_PROVIDER_DESCRIPTOR = new Descriptor( KEY, "1.0" );
private static final String INDEX_FILE_NAME = "index";

private final PageCache pageCache;
private final FileSystemAbstraction fs;
private final File nativeSchemaIndexBaseDir;
private final Log log;
private final RecoveryCleanupWorkCollector recoveryCleanupWorkCollector;
private final boolean readOnly;

public NativeSchemaNumberIndexProvider( PageCache pageCache, FileSystemAbstraction fs, File storeDir, LogProvider logging,
RecoveryCleanupWorkCollector recoveryCleanupWorkCollector, boolean readOnly )
{
super( NATIVE_PROVIDER_DESCRIPTOR, 0 );
this( pageCache, fs, IndexDirectoryStructure.directoriesByProvider( storeDir ), logging,
recoveryCleanupWorkCollector, readOnly );
}

public NativeSchemaNumberIndexProvider( PageCache pageCache, FileSystemAbstraction fs, IndexDirectoryStructure.Factory directoryStructure,
LogProvider logging, RecoveryCleanupWorkCollector recoveryCleanupWorkCollector, boolean readOnly )
{
super( NATIVE_PROVIDER_DESCRIPTOR, 0, directoryStructure );
this.pageCache = pageCache;
this.fs = fs;
this.nativeSchemaIndexBaseDir = getSchemaIndexStoreDirectory( storeDir );
this.log = logging.getLog( getClass() );
this.recoveryCleanupWorkCollector = recoveryCleanupWorkCollector;
this.readOnly = readOnly;
Expand Down Expand Up @@ -170,7 +178,7 @@ public StoreMigrationParticipant storeMigrationParticipant( FileSystemAbstractio

private File nativeIndexFileFromIndexId( long indexId )
{
return new File( nativeSchemaIndexBaseDir, Long.toString( indexId ) );
return new File( directoryStructure().directoryForIndex( indexId ), INDEX_FILE_NAME );
}

private class ReadOnlyMetaNumberLayout extends Layout.ReadOnlyMetaLayout
Expand Down
Expand Up @@ -24,6 +24,7 @@
import org.neo4j.io.fs.FileSystemAbstraction;
import org.neo4j.io.pagecache.PageCache;
import org.neo4j.kernel.api.index.IndexAccessor;
import org.neo4j.kernel.api.index.IndexDirectoryStructure;
import org.neo4j.kernel.api.index.IndexPopulator;
import org.neo4j.kernel.api.index.InternalIndexState;
import org.neo4j.kernel.api.index.SchemaIndexProvider;
Expand All @@ -49,10 +50,11 @@ public interface Selector
private final SchemaIndexProvider luceneProvider;
private final Selector selector;

public FusionSchemaIndexProvider( SchemaIndexProvider nativeProvider, SchemaIndexProvider luceneProvider, Selector selector,
public FusionSchemaIndexProvider( IndexDirectoryStructure.Factory directoryStructure,
SchemaIndexProvider nativeProvider, SchemaIndexProvider luceneProvider, Selector selector,
SchemaIndexProvider.Descriptor descriptor, int priority )
{
super( descriptor, priority );
super( descriptor, priority, directoryStructure );
this.nativeProvider = nativeProvider;
this.luceneProvider = luceneProvider;
this.selector = selector;
Expand Down

0 comments on commit 0373d09

Please sign in to comment.