From 440c39d402251b7b331138980072ad4e87f4629c Mon Sep 17 00:00:00 2001 From: Ragnar Mellbin Date: Tue, 28 Nov 2017 15:59:13 +0100 Subject: [PATCH] Introduce null fultext provider In order for the procedures to successfully load when the bloom extension is disabled. --- .../api/impl/fulltext/FulltextProvider.java | 357 ++++-------------- .../impl/fulltext/FulltextProviderImpl.java | 349 +++++++++++++++++ .../FulltextTransactionEventUpdater.java | 4 +- .../bloom/BloomKernelExtension.java | 9 +- .../fulltext/LuceneFulltextTestSupport.java | 4 +- .../fulltext/LuceneFulltextUpdaterTest.java | 2 +- .../fulltext/integrations/bloom/BloomIT.java | 19 + 7 files changed, 443 insertions(+), 301 deletions(-) create mode 100644 enterprise/fulltext-addon/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextProviderImpl.java diff --git a/enterprise/fulltext-addon/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextProvider.java b/enterprise/fulltext-addon/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextProvider.java index b4ab2d26ecca9..aaab7fee48b60 100644 --- a/enterprise/fulltext-addon/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextProvider.java +++ b/enterprise/fulltext-addon/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextProvider.java @@ -19,201 +19,96 @@ */ package org.neo4j.kernel.api.impl.fulltext; -import java.io.File; import java.io.IOException; -import java.io.UncheckedIOException; -import java.util.Collection; -import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.function.Consumer; -import java.util.function.Function; -import org.neo4j.graphdb.GraphDatabaseService; -import org.neo4j.io.fs.FileSystemAbstraction; -import org.neo4j.kernel.AvailabilityGuard; import org.neo4j.kernel.api.exceptions.InvalidArgumentsException; import org.neo4j.kernel.api.index.InternalIndexState; -import org.neo4j.kernel.impl.transaction.log.TransactionIdStore; -import org.neo4j.logging.Log; -import org.neo4j.scheduler.JobScheduler; -/** - * Provider class that manages and provides fulltext indices. This is the main entry point for the fulltext addon. - */ -public class FulltextProvider implements AutoCloseable +public interface FulltextProvider extends AutoCloseable { - public static final String LUCENE_FULLTEXT_ADDON_PREFIX = "__lucene__fulltext__addon__"; - public static final String FIELD_ENTITY_ID = LUCENE_FULLTEXT_ADDON_PREFIX + "internal__id__"; - - private final GraphDatabaseService db; - private final Log log; - private final TransactionIdStore transactionIdStore; - private final FulltextTransactionEventUpdater fulltextTransactionEventUpdater; - private final Set nodeProperties; - private final Set relationshipProperties; - private final Map writableNodeIndices; - private final Map writableRelationshipIndices; - private final FulltextUpdateApplier applier; - private final FulltextFactory factory; - private final ReadWriteLock configurationLock; - - /** - * Creates a provider of fulltext indices for the given database. This is the entry point for all fulltext index - * operations. - * - * @param db Database that this provider should work with. - * @param log For logging errors. - * @param availabilityGuard Used for waiting with populating the index until the database is available. - * @param scheduler For background work. - * @param transactionIdStore Used for checking if the store has had transactions applied to it, while the fulltext - * @param fileSystem The filesystem to use. - * @param storeDir Store directory of the database. - * @param analyzerClassName The Lucene analyzer to use for the {@link LuceneFulltext} created by this factory. - */ - public FulltextProvider( GraphDatabaseService db, Log log, AvailabilityGuard availabilityGuard, - JobScheduler scheduler, TransactionIdStore transactionIdStore, - FileSystemAbstraction fileSystem, File storeDir, - String analyzerClassName ) throws IOException - { - this.db = db; - this.log = log; - this.transactionIdStore = transactionIdStore; - applier = new FulltextUpdateApplier( log, availabilityGuard, scheduler ); - applier.start(); - factory = new FulltextFactory( fileSystem, storeDir, analyzerClassName ); - fulltextTransactionEventUpdater = new FulltextTransactionEventUpdater( this, log, applier ); - nodeProperties = ConcurrentHashMap.newKeySet(); - relationshipProperties = ConcurrentHashMap.newKeySet(); - writableNodeIndices = new ConcurrentHashMap<>(); - writableRelationshipIndices = new ConcurrentHashMap<>(); - configurationLock = new ReentrantReadWriteLock( true ); - } - - public void registerTransactionEventHandler() throws IOException + FulltextProvider NULL_PROVIDER = new FulltextProvider() { - db.registerTransactionEventHandler( fulltextTransactionEventUpdater ); - } + @Override + public void registerTransactionEventHandler() throws IOException + { + throw noProvider(); + } - private boolean matchesConfiguration( WritableFulltext index ) throws IOException - { - long txId = transactionIdStore.getLastCommittedTransactionId(); - FulltextIndexConfiguration currentConfig = - new FulltextIndexConfiguration( index.getAnalyzerName(), index.getProperties(), txId ); + @Override + public void awaitPopulation() + { + throw noProvider(); + } - FulltextIndexConfiguration storedConfig; - try ( ReadOnlyFulltext indexReader = index.getIndexReader() ) + @Override + public void openIndex( String identifier, FulltextIndexType type ) throws IOException { - storedConfig = indexReader.getConfigurationDocument(); + throw noProvider(); } - return storedConfig == null && index.getProperties().isEmpty() || - storedConfig != null && storedConfig.equals( currentConfig ); - } - /** - * Wait for the asynchronous background population, if one is on-going, to complete. - *

- * Such population, where the entire store is scanned for data to write to the index, will be started if the index - * needs to recover after an unclean shut-down, or a configuration change. - * - * @throws RuntimeException If it was not possible to wait for the population to finish, for some reason. - */ - public void awaitPopulation() - { - try + @Override + public void createIndex( String identifier, FulltextIndexType type, List properties ) throws IOException { - applier.writeBarrier().awaitCompletion(); + throw noProvider(); } - catch ( ExecutionException e ) + + @Override + public ReadOnlyFulltext getReader( String identifier, FulltextIndexType type ) throws IOException { - throw new AssertionError( "The writeBarrier operation should never throw an exception", e ); + throw noProvider(); } - catch ( IOException e ) + + @Override + public Set getProperties( String identifier, FulltextIndexType type ) { - throw new UncheckedIOException( e ); + throw noProvider(); } - } - public void openIndex( String identifier, FulltextIndexType type ) throws IOException - { - LuceneFulltext index = factory.openFulltextIndex( identifier, type ); - register( index ); - } + @Override + public InternalIndexState getState( String identifier, FulltextIndexType type ) + { + throw noProvider(); + } - public void createIndex( String identifier, FulltextIndexType type, List properties ) - throws IOException - { - LuceneFulltext index = factory.createFulltextIndex( identifier, type, properties ); - register( index ); - } + @Override + public void changeIndexedProperties( String identifier, FulltextIndexType type, List propertyKeys ) + throws IOException, InvalidArgumentsException + { + throw noProvider(); + } - private void register( LuceneFulltext fulltextIndex ) throws IOException - { - configurationLock.writeLock().lock(); - try + @Override + public void close() throws Exception { - WritableFulltext writableFulltext = new WritableFulltext( fulltextIndex ); - writableFulltext.open(); - if ( fulltextIndex.getType() == FulltextIndexType.NODES ) - { - if ( !matchesConfiguration( writableFulltext ) ) - { - writableFulltext.drop(); - writableFulltext.open(); - if ( !writableFulltext.getProperties().isEmpty() ) - { - applier.populateNodes( writableFulltext, db ); - } - } - writableNodeIndices.put( fulltextIndex.getIdentifier(), writableFulltext ); - nodeProperties.addAll( fulltextIndex.getProperties() ); - } - else - { - if ( !matchesConfiguration( writableFulltext ) ) - { - writableFulltext.drop(); - writableFulltext.open(); - if ( !writableFulltext.getProperties().isEmpty() ) - { - applier.populateRelationships( writableFulltext, db ); - } - } - writableRelationshipIndices.put( fulltextIndex.getIdentifier(), writableFulltext ); - relationshipProperties.addAll( fulltextIndex.getProperties() ); - } + throw noProvider(); } - finally + + private RuntimeException noProvider() { - configurationLock.writeLock().unlock(); + return new UnsupportedOperationException( + "There is no fulltext provider for this database. Make sure that the feature you are tyring to use is enabled" ); } - } + }; + String LUCENE_FULLTEXT_ADDON_PREFIX = "__lucene__fulltext__addon__"; + String FIELD_ENTITY_ID = LUCENE_FULLTEXT_ADDON_PREFIX + "internal__id__"; - String[] getNodeProperties() - { - return nodeProperties.toArray( new String[0] ); - } + void registerTransactionEventHandler() throws IOException; - String[] getRelationshipProperties() - { - return relationshipProperties.toArray( new String[0] ); - } + /** + * Wait for the asynchronous background population, if one is on-going, to complete. + *

+ * Such population, where the entire store is scanned for data to write to the index, will be started if the index + * needs to recover after an unclean shut-down, or a configuration change. + * + * @throws RuntimeException If it was not possible to wait for the population to finish, for some reason. + */ + void awaitPopulation(); - Collection writableNodeIndices() - { - return Collections.unmodifiableCollection( writableNodeIndices.values() ); - } + void openIndex( String identifier, FulltextIndexType type ) throws IOException; - Collection writableRelationshipIndices() - { - return Collections.unmodifiableCollection( writableRelationshipIndices.values() ); - } + void createIndex( String identifier, FulltextIndexType type, List properties ) throws IOException; /** * Returns a reader for the specified index. @@ -223,137 +118,11 @@ Collection writableRelationshipIndices() * @return A {@link ReadOnlyFulltext} for the index, or null if no such index is found. * @throws IOException */ - public ReadOnlyFulltext getReader( String identifier, FulltextIndexType type ) throws IOException - { - WritableFulltext writableFulltext = getIndexMap( type ).get( identifier ); - if ( writableFulltext == null ) - { - throw new IllegalArgumentException( "No such " + type + " index '" + identifier + "'." ); - } - return writableFulltext.getIndexReader(); - } - - private Map getIndexMap( FulltextIndexType type ) - { - switch ( type ) - { - case NODES: - return writableNodeIndices; - case RELATIONSHIPS: - return writableRelationshipIndices; - default: - throw new IllegalArgumentException( "No such fulltext index type: " + type ); - } - } - - public Set getProperties( String identifier, FulltextIndexType type ) - { - return applyToMatchingIndex( identifier, type, WritableFulltext::getProperties ); - } - - private E applyToMatchingIndex( - String identifier, FulltextIndexType type, Function function ) - { - if ( type == FulltextIndexType.NODES ) - { - return function.apply( writableNodeIndices.get( identifier ) ); - } - else - { - return function.apply( writableRelationshipIndices.get( identifier ) ); - } - } - - public InternalIndexState getState( String identifier, FulltextIndexType type ) - { - return applyToMatchingIndex( identifier, type, WritableFulltext::getState ); - } - - void drop( String identifier, FulltextIndexType type ) throws IOException - { - configurationLock.writeLock().lock(); - try - { - // Wait for the queue of updates to drain, before deleting an index. - awaitPopulation(); - if ( type == FulltextIndexType.NODES ) - { - writableNodeIndices.remove( identifier ).drop(); - } - else - { - writableRelationshipIndices.remove( identifier ).drop(); - } - rebuildProperties(); - } - finally - { - configurationLock.writeLock().unlock(); - } - } - - private void rebuildProperties() - { - nodeProperties.clear(); - relationshipProperties.clear(); - writableNodeIndices.forEach( ( s, index ) -> nodeProperties.addAll( index.getProperties() ) ); - writableRelationshipIndices.forEach( ( s, index ) -> relationshipProperties.addAll( index.getProperties() ) ); - } - - Lock readLockIndexConfiguration() - { - Lock lock = configurationLock.readLock(); - lock.lock(); - return lock; - } + ReadOnlyFulltext getReader( String identifier, FulltextIndexType type ) throws IOException; - public void changeIndexedProperties( String identifier, FulltextIndexType type, List propertyKeys ) - throws IOException, InvalidArgumentsException - { - configurationLock.writeLock().lock(); - try - { - if ( propertyKeys.stream().anyMatch( s -> s.startsWith( FulltextProvider.LUCENE_FULLTEXT_ADDON_PREFIX ) ) ) - { - throw new InvalidArgumentsException( - "It is not possible to index property keys starting with " + - FulltextProvider.LUCENE_FULLTEXT_ADDON_PREFIX ); - } - Set currentProperties = getProperties( identifier, type ); - if ( !currentProperties.containsAll( propertyKeys ) || !propertyKeys.containsAll( currentProperties ) ) - { - drop( identifier, type ); - createIndex( identifier, type, propertyKeys ); - } - } - finally - { - configurationLock.writeLock().unlock(); - } - } + Set getProperties( String identifier, FulltextIndexType type ); - /** - * Closes the provider and all associated resources. - */ - @Override - public void close() - { - db.unregisterTransactionEventHandler( fulltextTransactionEventUpdater ); - applier.stop(); - Consumer fulltextCloser = luceneFulltextIndex -> - { - try - { - luceneFulltextIndex.saveConfiguration( transactionIdStore.getLastCommittedTransactionId() ); - luceneFulltextIndex.close(); - } - catch ( IOException e ) - { - log.error( "Unable to close fulltext index.", e ); - } - }; - writableNodeIndices.values().forEach( fulltextCloser ); - writableRelationshipIndices.values().forEach( fulltextCloser ); - } + InternalIndexState getState( String identifier, FulltextIndexType type ); + void changeIndexedProperties( String identifier, FulltextIndexType type, List propertyKeys ) throws IOException, InvalidArgumentsException; } diff --git a/enterprise/fulltext-addon/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextProviderImpl.java b/enterprise/fulltext-addon/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextProviderImpl.java new file mode 100644 index 0000000000000..e01edea6094fa --- /dev/null +++ b/enterprise/fulltext-addon/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextProviderImpl.java @@ -0,0 +1,349 @@ +/* + * 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 Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.neo4j.kernel.api.impl.fulltext; + +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Consumer; +import java.util.function.Function; + +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.io.fs.FileSystemAbstraction; +import org.neo4j.kernel.AvailabilityGuard; +import org.neo4j.kernel.api.exceptions.InvalidArgumentsException; +import org.neo4j.kernel.api.index.InternalIndexState; +import org.neo4j.kernel.impl.transaction.log.TransactionIdStore; +import org.neo4j.logging.Log; +import org.neo4j.scheduler.JobScheduler; + +/** + * Provider class that manages and provides fulltext indices. This is the main entry point for the fulltext addon. + */ +public class FulltextProviderImpl implements FulltextProvider +{ + + private final GraphDatabaseService db; + private final Log log; + private final TransactionIdStore transactionIdStore; + private final FulltextTransactionEventUpdater fulltextTransactionEventUpdater; + private final Set nodeProperties; + private final Set relationshipProperties; + private final Map writableNodeIndices; + private final Map writableRelationshipIndices; + private final FulltextUpdateApplier applier; + private final FulltextFactory factory; + private final ReadWriteLock configurationLock; + + /** + * Creates a provider of fulltext indices for the given database. This is the entry point for all fulltext index + * operations. + * + * @param db Database that this provider should work with. + * @param log For logging errors. + * @param availabilityGuard Used for waiting with populating the index until the database is available. + * @param scheduler For background work. + * @param transactionIdStore Used for checking if the store has had transactions applied to it, while the fulltext + * @param fileSystem The filesystem to use. + * @param storeDir Store directory of the database. + * @param analyzerClassName The Lucene analyzer to use for the {@link LuceneFulltext} created by this factory. + */ + public FulltextProviderImpl( GraphDatabaseService db, Log log, AvailabilityGuard availabilityGuard, + JobScheduler scheduler, TransactionIdStore transactionIdStore, + FileSystemAbstraction fileSystem, File storeDir, + String analyzerClassName ) throws IOException + { + this.db = db; + this.log = log; + this.transactionIdStore = transactionIdStore; + applier = new FulltextUpdateApplier( log, availabilityGuard, scheduler ); + applier.start(); + factory = new FulltextFactory( fileSystem, storeDir, analyzerClassName ); + fulltextTransactionEventUpdater = new FulltextTransactionEventUpdater( this, log, applier ); + nodeProperties = ConcurrentHashMap.newKeySet(); + relationshipProperties = ConcurrentHashMap.newKeySet(); + writableNodeIndices = new ConcurrentHashMap<>(); + writableRelationshipIndices = new ConcurrentHashMap<>(); + configurationLock = new ReentrantReadWriteLock( true ); + } + + @Override + public void registerTransactionEventHandler() throws IOException + { + db.registerTransactionEventHandler( fulltextTransactionEventUpdater ); + } + + private boolean matchesConfiguration( WritableFulltext index ) throws IOException + { + long txId = transactionIdStore.getLastCommittedTransactionId(); + FulltextIndexConfiguration currentConfig = + new FulltextIndexConfiguration( index.getAnalyzerName(), index.getProperties(), txId ); + + FulltextIndexConfiguration storedConfig; + try ( ReadOnlyFulltext indexReader = index.getIndexReader() ) + { + storedConfig = indexReader.getConfigurationDocument(); + } + return storedConfig == null && index.getProperties().isEmpty() || + storedConfig != null && storedConfig.equals( currentConfig ); + } + + @Override + public void awaitPopulation() + { + try + { + applier.writeBarrier().awaitCompletion(); + } + catch ( ExecutionException e ) + { + throw new AssertionError( "The writeBarrier operation should never throw an exception", e ); + } + catch ( IOException e ) + { + throw new UncheckedIOException( e ); + } + } + + @Override + public void openIndex( String identifier, FulltextIndexType type ) throws IOException + { + LuceneFulltext index = factory.openFulltextIndex( identifier, type ); + register( index ); + } + + @Override + public void createIndex( String identifier, FulltextIndexType type, List properties ) + throws IOException + { + LuceneFulltext index = factory.createFulltextIndex( identifier, type, properties ); + register( index ); + } + + private void register( LuceneFulltext fulltextIndex ) throws IOException + { + configurationLock.writeLock().lock(); + try + { + WritableFulltext writableFulltext = new WritableFulltext( fulltextIndex ); + writableFulltext.open(); + if ( fulltextIndex.getType() == FulltextIndexType.NODES ) + { + if ( !matchesConfiguration( writableFulltext ) ) + { + writableFulltext.drop(); + writableFulltext.open(); + if ( !writableFulltext.getProperties().isEmpty() ) + { + applier.populateNodes( writableFulltext, db ); + } + } + writableNodeIndices.put( fulltextIndex.getIdentifier(), writableFulltext ); + nodeProperties.addAll( fulltextIndex.getProperties() ); + } + else + { + if ( !matchesConfiguration( writableFulltext ) ) + { + writableFulltext.drop(); + writableFulltext.open(); + if ( !writableFulltext.getProperties().isEmpty() ) + { + applier.populateRelationships( writableFulltext, db ); + } + } + writableRelationshipIndices.put( fulltextIndex.getIdentifier(), writableFulltext ); + relationshipProperties.addAll( fulltextIndex.getProperties() ); + } + } + finally + { + configurationLock.writeLock().unlock(); + } + } + + String[] getNodeProperties() + { + return nodeProperties.toArray( new String[0] ); + } + + String[] getRelationshipProperties() + { + return relationshipProperties.toArray( new String[0] ); + } + + Collection writableNodeIndices() + { + return Collections.unmodifiableCollection( writableNodeIndices.values() ); + } + + Collection writableRelationshipIndices() + { + return Collections.unmodifiableCollection( writableRelationshipIndices.values() ); + } + + @Override + public ReadOnlyFulltext getReader( String identifier, FulltextIndexType type ) throws IOException + { + WritableFulltext writableFulltext = getIndexMap( type ).get( identifier ); + if ( writableFulltext == null ) + { + throw new IllegalArgumentException( "No such " + type + " index '" + identifier + "'." ); + } + return writableFulltext.getIndexReader(); + } + + private Map getIndexMap( FulltextIndexType type ) + { + switch ( type ) + { + case NODES: + return writableNodeIndices; + case RELATIONSHIPS: + return writableRelationshipIndices; + default: + throw new IllegalArgumentException( "No such fulltext index type: " + type ); + } + } + + @Override + public Set getProperties( String identifier, FulltextIndexType type ) + { + return applyToMatchingIndex( identifier, type, WritableFulltext::getProperties ); + } + + private E applyToMatchingIndex( + String identifier, FulltextIndexType type, Function function ) + { + if ( type == FulltextIndexType.NODES ) + { + return function.apply( writableNodeIndices.get( identifier ) ); + } + else + { + return function.apply( writableRelationshipIndices.get( identifier ) ); + } + } + + @Override + public InternalIndexState getState( String identifier, FulltextIndexType type ) + { + return applyToMatchingIndex( identifier, type, WritableFulltext::getState ); + } + + void drop( String identifier, FulltextIndexType type ) throws IOException + { + configurationLock.writeLock().lock(); + try + { + // Wait for the queue of updates to drain, before deleting an index. + awaitPopulation(); + if ( type == FulltextIndexType.NODES ) + { + writableNodeIndices.remove( identifier ).drop(); + } + else + { + writableRelationshipIndices.remove( identifier ).drop(); + } + rebuildProperties(); + } + finally + { + configurationLock.writeLock().unlock(); + } + } + + private void rebuildProperties() + { + nodeProperties.clear(); + relationshipProperties.clear(); + writableNodeIndices.forEach( ( s, index ) -> nodeProperties.addAll( index.getProperties() ) ); + writableRelationshipIndices.forEach( ( s, index ) -> relationshipProperties.addAll( index.getProperties() ) ); + } + + Lock readLockIndexConfiguration() + { + Lock lock = configurationLock.readLock(); + lock.lock(); + return lock; + } + + @Override + public void changeIndexedProperties( String identifier, FulltextIndexType type, List propertyKeys ) + throws IOException, InvalidArgumentsException + { + configurationLock.writeLock().lock(); + try + { + if ( propertyKeys.stream().anyMatch( s -> s.startsWith( FulltextProvider.LUCENE_FULLTEXT_ADDON_PREFIX ) ) ) + { + throw new InvalidArgumentsException( + "It is not possible to index property keys starting with " + + FulltextProvider.LUCENE_FULLTEXT_ADDON_PREFIX ); + } + Set currentProperties = getProperties( identifier, type ); + if ( !currentProperties.containsAll( propertyKeys ) || !propertyKeys.containsAll( currentProperties ) ) + { + drop( identifier, type ); + createIndex( identifier, type, propertyKeys ); + } + } + finally + { + configurationLock.writeLock().unlock(); + } + } + + /** + * Closes the provider and all associated resources. + */ + @Override + public void close() + { + db.unregisterTransactionEventHandler( fulltextTransactionEventUpdater ); + applier.stop(); + Consumer fulltextCloser = luceneFulltextIndex -> + { + try + { + luceneFulltextIndex.saveConfiguration( transactionIdStore.getLastCommittedTransactionId() ); + luceneFulltextIndex.close(); + } + catch ( IOException e ) + { + log.error( "Unable to close fulltext index.", e ); + } + }; + writableNodeIndices.values().forEach( fulltextCloser ); + writableRelationshipIndices.values().forEach( fulltextCloser ); + } + +} diff --git a/enterprise/fulltext-addon/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextTransactionEventUpdater.java b/enterprise/fulltext-addon/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextTransactionEventUpdater.java index 39390ee4b6e44..055431cf6dd40 100644 --- a/enterprise/fulltext-addon/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextTransactionEventUpdater.java +++ b/enterprise/fulltext-addon/src/main/java/org/neo4j/kernel/api/impl/fulltext/FulltextTransactionEventUpdater.java @@ -34,11 +34,11 @@ class FulltextTransactionEventUpdater implements TransactionEventHandler { - private final FulltextProvider fulltextProvider; + private final FulltextProviderImpl fulltextProvider; private final Log log; private final FulltextUpdateApplier applier; - FulltextTransactionEventUpdater( FulltextProvider fulltextProvider, Log log, + FulltextTransactionEventUpdater( FulltextProviderImpl fulltextProvider, Log log, FulltextUpdateApplier applier ) { this.fulltextProvider = fulltextProvider; diff --git a/enterprise/fulltext-addon/src/main/java/org/neo4j/kernel/api/impl/fulltext/integrations/bloom/BloomKernelExtension.java b/enterprise/fulltext-addon/src/main/java/org/neo4j/kernel/api/impl/fulltext/integrations/bloom/BloomKernelExtension.java index f193d66bc64e2..5b66ee8fd26c6 100644 --- a/enterprise/fulltext-addon/src/main/java/org/neo4j/kernel/api/impl/fulltext/integrations/bloom/BloomKernelExtension.java +++ b/enterprise/fulltext-addon/src/main/java/org/neo4j/kernel/api/impl/fulltext/integrations/bloom/BloomKernelExtension.java @@ -28,6 +28,7 @@ import org.neo4j.kernel.AvailabilityGuard; import org.neo4j.kernel.api.exceptions.KernelException; import org.neo4j.kernel.api.impl.fulltext.FulltextProvider; +import org.neo4j.kernel.api.impl.fulltext.FulltextProviderImpl; import org.neo4j.kernel.configuration.Config; import org.neo4j.kernel.impl.logging.LogService; import org.neo4j.kernel.impl.proc.Procedures; @@ -78,8 +79,8 @@ public void start() throws IOException, KernelException { String analyzer = config.get( BloomFulltextConfig.bloom_default_analyzer ); - Log log = logService.getInternalLog( FulltextProvider.class ); - provider = new FulltextProvider( db, log, availabilityGuard, scheduler, transactionIdStore.get(), + Log log = logService.getInternalLog( FulltextProviderImpl.class ); + provider = new FulltextProviderImpl( db, log, availabilityGuard, scheduler, transactionIdStore.get(), fileSystem, storeDir, analyzer ); provider.openIndex( BLOOM_NODES, NODES ); provider.openIndex( BLOOM_RELATIONSHIPS, RELATIONSHIPS ); @@ -87,6 +88,10 @@ public void start() throws IOException, KernelException procedures.registerComponent( FulltextProvider.class, context -> provider, true ); } + else + { + procedures.registerComponent( FulltextProvider.class, context -> FulltextProvider.NULL_PROVIDER, true ); + } } @Override diff --git a/enterprise/fulltext-addon/src/test/java/org/neo4j/kernel/api/impl/fulltext/LuceneFulltextTestSupport.java b/enterprise/fulltext-addon/src/test/java/org/neo4j/kernel/api/impl/fulltext/LuceneFulltextTestSupport.java index 1872525295a96..1fc64353b2fcc 100644 --- a/enterprise/fulltext-addon/src/test/java/org/neo4j/kernel/api/impl/fulltext/LuceneFulltextTestSupport.java +++ b/enterprise/fulltext-addon/src/test/java/org/neo4j/kernel/api/impl/fulltext/LuceneFulltextTestSupport.java @@ -77,9 +77,9 @@ public void setUp() throws Throwable transactionIdStore = dbRule.resolveDependency( TransactionIdStore.class ); } - protected FulltextProvider createProvider() throws IOException + protected FulltextProviderImpl createProvider() throws IOException { - return new FulltextProvider( db, LOG, availabilityGuard, scheduler, transactionIdStore, + return new FulltextProviderImpl( db, LOG, availabilityGuard, scheduler, transactionIdStore, fs, storeDir, analyzer ); } diff --git a/enterprise/fulltext-addon/src/test/java/org/neo4j/kernel/api/impl/fulltext/LuceneFulltextUpdaterTest.java b/enterprise/fulltext-addon/src/test/java/org/neo4j/kernel/api/impl/fulltext/LuceneFulltextUpdaterTest.java index a017757dd858d..3440e2661bfd8 100644 --- a/enterprise/fulltext-addon/src/test/java/org/neo4j/kernel/api/impl/fulltext/LuceneFulltextUpdaterTest.java +++ b/enterprise/fulltext-addon/src/test/java/org/neo4j/kernel/api/impl/fulltext/LuceneFulltextUpdaterTest.java @@ -722,7 +722,7 @@ public void shouldBeAbleToUpdateAndQueryAfterIndexChange() throws Exception @Test public void shouldBeAbleToDropAndReaddIndex() throws Exception { - try ( FulltextProvider provider = createProvider() ) + try ( FulltextProviderImpl provider = createProvider() ) { provider.createIndex( "nodes", NODES, singletonList( "prop" ) ); provider.registerTransactionEventHandler(); diff --git a/enterprise/fulltext-addon/src/test/java/org/neo4j/kernel/api/impl/fulltext/integrations/bloom/BloomIT.java b/enterprise/fulltext-addon/src/test/java/org/neo4j/kernel/api/impl/fulltext/integrations/bloom/BloomIT.java index 36e420bdf0506..0be733a4ff20f 100644 --- a/enterprise/fulltext-addon/src/test/java/org/neo4j/kernel/api/impl/fulltext/integrations/bloom/BloomIT.java +++ b/enterprise/fulltext-addon/src/test/java/org/neo4j/kernel/api/impl/fulltext/integrations/bloom/BloomIT.java @@ -35,6 +35,7 @@ import org.neo4j.consistency.checking.full.CheckConsistencyConfig; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.QueryExecutionException; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.RelationshipType; import org.neo4j.graphdb.Result; @@ -605,6 +606,24 @@ public void onlineIndexShouldBeReportedAsOnline() throws Exception assertFalse( result.hasNext() ); } + @Test + public void databaseShouldBeAbleToStartWithBloomPresentButDisabled() throws Exception + { + builder.setConfig( bloom_enabled, "false" ); + db = getDb(); + //all good. + } + + @Test + public void shouldThrowSomewhatHelpfulMessageIfCalledWhenDisabled() throws Exception + { + builder.setConfig( bloom_enabled, "false" ); + db = getDb(); + expectedException.expect( QueryExecutionException.class ); + expectedException.expectMessage( "enabled" ); + db.execute( AWAIT_POPULATION ); + } + @Test public void failureToStartUpMustNotPreventShutDown() throws Exception {