/**
* Copyright (c) 2002-2014 "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.index.impl.lucene;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterAccessor;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.factory.GraphDatabaseSettings;
import org.neo4j.graphdb.index.IndexManager;
import org.neo4j.helpers.collection.MapUtil;
import org.neo4j.kernel.DefaultFileSystemAbstraction;
import org.neo4j.kernel.configuration.Config;
import org.neo4j.kernel.configuration.ConfigurationDefaults;
import org.neo4j.kernel.impl.index.IndexStore;
import org.neo4j.kernel.impl.nioneo.store.FileSystemAbstraction;
import org.neo4j.kernel.impl.transaction.PlaceboTm;
import org.neo4j.kernel.impl.transaction.xaframework.DefaultLogBufferFactory;
import org.neo4j.kernel.impl.transaction.xaframework.LogPruneStrategies;
import org.neo4j.kernel.impl.transaction.xaframework.RecoveryVerifier;
import org.neo4j.kernel.impl.transaction.xaframework.TxIdGenerator;
import org.neo4j.kernel.impl.transaction.xaframework.XaFactory;
import org.neo4j.kernel.impl.util.FileUtils;
import org.neo4j.kernel.impl.util.StringLogger;
public class TestLuceneDataSource
{
private final FileSystemAbstraction fileSystem = new DefaultFileSystemAbstraction();
private IndexStore indexStore;
private File datasourceDirectory;
private LuceneDataSource dataSource;
String dbPath = getDbPath();
private String getDbPath()
{
return "target/var/datasource"+System.currentTimeMillis();
}
@Before
public void setup()
{
datasourceDirectory = new File( dbPath );
datasourceDirectory.mkdirs();
indexStore = new IndexStore( dbPath, new DefaultFileSystemAbstraction() );
addIndex( "foo" );
}
private void addIndex(String name)
{
indexStore.set( Node.class, name, MapUtil.stringMap( IndexManager.PROVIDER, "lucene", "type", "fulltext" ) );
}
private IndexIdentifier identifier(String name)
{
return new IndexIdentifier( LuceneCommand.NODE, dataSource.nodeEntityType, name);
}
@After
public void teardown() throws IOException
{
dataSource.stop();
FileUtils.deleteRecursively( datasourceDirectory );
}
@Test
public void testShouldReturnIndexWriterFromLRUCache() throws InstantiationException
{
dataSource = new LuceneDataSource(new Config( new ConfigurationDefaults(GraphDatabaseSettings.class ).apply(config() )), indexStore, new DefaultFileSystemAbstraction(),
new XaFactory(new Config( new ConfigurationDefaults( GraphDatabaseSettings.class ).apply( config() )), TxIdGenerator.DEFAULT, new PlaceboTm(), new DefaultLogBufferFactory(), new DefaultFileSystemAbstraction(), StringLogger.DEV_NULL, RecoveryVerifier.ALWAYS_VALID, LogPruneStrategies.NO_PRUNING ) );
dataSource.start();
IndexIdentifier identifier = identifier( "foo" );
IndexWriter writer = dataSource.getIndexSearcher( identifier ).getWriter();
assertSame( writer, dataSource.getIndexSearcher( identifier ).getWriter() );
}
@Test
public void testShouldReturnIndexSearcherFromLRUCache() throws InstantiationException, IOException
{
Config config = new Config( new ConfigurationDefaults(GraphDatabaseSettings.class ).apply( config()) );
dataSource = new LuceneDataSource( config, indexStore, new DefaultFileSystemAbstraction(),
new XaFactory( config, TxIdGenerator.DEFAULT, new PlaceboTm(), new DefaultLogBufferFactory(), new DefaultFileSystemAbstraction(), StringLogger.DEV_NULL, RecoveryVerifier.ALWAYS_VALID, LogPruneStrategies.NO_PRUNING ) );
dataSource.start();
IndexIdentifier identifier = identifier( "foo" );
IndexReference searcher = dataSource.getIndexSearcher( identifier );
assertSame( searcher, dataSource.getIndexSearcher( identifier ) );
searcher.close();
}
@Test
public void testClosesOldestIndexWriterWhenCacheSizeIsExceeded() throws InstantiationException
{
addIndex( "bar" );
addIndex( "baz" );
Map<String,String> config = config();
config.put( GraphDatabaseSettings.lucene_searcher_cache_size.name(), "2");
Config config1 = new Config( new ConfigurationDefaults(GraphDatabaseSettings.class ).apply( config) );
dataSource = new LuceneDataSource( config1, indexStore, new DefaultFileSystemAbstraction(),
new XaFactory(config1, TxIdGenerator.DEFAULT, new PlaceboTm(), new DefaultLogBufferFactory(), new DefaultFileSystemAbstraction(), StringLogger.DEV_NULL, RecoveryVerifier.ALWAYS_VALID, LogPruneStrategies.NO_PRUNING ) );
dataSource.start();
IndexIdentifier fooIdentifier = identifier( "foo" );
IndexIdentifier barIdentifier = identifier( "bar" );
IndexIdentifier bazIdentifier = identifier( "baz" );
IndexWriter fooIndexWriter = dataSource.getIndexSearcher( fooIdentifier ).getWriter();
dataSource.getIndexSearcher( barIdentifier );
assertFalse( IndexWriterAccessor.isClosed( fooIndexWriter ) );
dataSource.getIndexSearcher( bazIdentifier );
assertTrue( IndexWriterAccessor.isClosed( fooIndexWriter ) );
}
@Test
public void testClosesOldestIndexSearcherWhenCacheSizeIsExceeded() throws InstantiationException, IOException
{
addIndex( "bar" );
addIndex( "baz" );
Map<String,String> config = config();
config.put( GraphDatabaseSettings.lucene_searcher_cache_size.name(), "2");
Config config1 = new Config( new ConfigurationDefaults(GraphDatabaseSettings.class ).apply( config) );
dataSource = new LuceneDataSource( config1, indexStore, new DefaultFileSystemAbstraction(),
new XaFactory( config1, TxIdGenerator.DEFAULT, new PlaceboTm(), new DefaultLogBufferFactory(), fileSystem, StringLogger.DEV_NULL, RecoveryVerifier.ALWAYS_VALID, LogPruneStrategies.NO_PRUNING ) );
dataSource.start();
IndexIdentifier fooIdentifier = identifier( "foo" );
IndexIdentifier barIdentifier = identifier( "bar" );
IndexIdentifier bazIdentifier = identifier( "baz" );
IndexReference fooSearcher = dataSource.getIndexSearcher( fooIdentifier );
IndexReference barSearcher = dataSource.getIndexSearcher( barIdentifier );
assertFalse( fooSearcher.isClosed() );
IndexReference bazSearcher = dataSource.getIndexSearcher( bazIdentifier );
assertTrue( fooSearcher.isClosed() );
barSearcher.close();
bazSearcher.close();
}
@Test
public void testRecreatesSearcherWhenRequestedAgain() throws InstantiationException, IOException
{
addIndex( "bar" );
addIndex( "baz" );
Map<String,String> config = config();
config.put( GraphDatabaseSettings.lucene_searcher_cache_size.name(), "2");
Config config1 = new Config( new ConfigurationDefaults(GraphDatabaseSettings.class ).apply( config) );
dataSource = new LuceneDataSource( config1, indexStore, new DefaultFileSystemAbstraction(),
new XaFactory( config1, TxIdGenerator.DEFAULT, new PlaceboTm(), new DefaultLogBufferFactory(), new DefaultFileSystemAbstraction(), StringLogger.DEV_NULL, RecoveryVerifier.ALWAYS_VALID, LogPruneStrategies.NO_PRUNING ) );
dataSource.start();
IndexIdentifier fooIdentifier = identifier( "foo" );
IndexIdentifier barIdentifier = identifier( "bar" );
IndexIdentifier bazIdentifier = identifier( "baz" );
IndexReference oldFooSearcher = dataSource.getIndexSearcher( fooIdentifier );
IndexReference barSearcher = dataSource.getIndexSearcher( barIdentifier );
IndexReference bazSearcher = dataSource.getIndexSearcher( bazIdentifier );
IndexReference newFooSearcher = dataSource.getIndexSearcher( bazIdentifier );
assertNotSame( oldFooSearcher, newFooSearcher );
assertFalse( newFooSearcher.isClosed() );
oldFooSearcher.close();
barSearcher.close();
bazSearcher.close();
newFooSearcher.close();
}
@Test
public void testRecreatesWriterWhenRequestedAgainAfterCacheEviction() throws InstantiationException
{
addIndex( "bar" );
addIndex( "baz" );
Map<String,String> config = config();
config.put( GraphDatabaseSettings.lucene_searcher_cache_size.name(), "2");
Config config1 = new Config( new ConfigurationDefaults(GraphDatabaseSettings.class ).apply( config) );
dataSource = new LuceneDataSource( config1, indexStore, new DefaultFileSystemAbstraction(),
new XaFactory( config1, TxIdGenerator.DEFAULT, new PlaceboTm(), new DefaultLogBufferFactory(), new DefaultFileSystemAbstraction(), StringLogger.DEV_NULL, RecoveryVerifier.ALWAYS_VALID, LogPruneStrategies.NO_PRUNING ) );
dataSource.start();
IndexIdentifier fooIdentifier = identifier( "foo" );
IndexIdentifier barIdentifier = identifier( "bar" );
IndexIdentifier bazIdentifier = identifier( "baz" );
IndexWriter oldFooIndexWriter = dataSource.getIndexSearcher( fooIdentifier ).getWriter();
dataSource.getIndexSearcher( barIdentifier );
dataSource.getIndexSearcher( bazIdentifier );
IndexWriter newFooIndexWriter = dataSource.getIndexSearcher( fooIdentifier ).getWriter();
assertNotSame( oldFooIndexWriter, newFooIndexWriter );
assertFalse( IndexWriterAccessor.isClosed( newFooIndexWriter ) );
}
@Ignore( "No longer valid since Lucene 3.5" )
@Test
public void testInvalidatingSearcherCreatesANewOne() throws InstantiationException, IOException
{
Config config = new Config( new ConfigurationDefaults(GraphDatabaseSettings.class ).apply( config()) );
dataSource = new LuceneDataSource( config, indexStore, new DefaultFileSystemAbstraction(),
new XaFactory( config, TxIdGenerator.DEFAULT, new PlaceboTm(), new DefaultLogBufferFactory(), new DefaultFileSystemAbstraction(), StringLogger.DEV_NULL, RecoveryVerifier.ALWAYS_VALID, LogPruneStrategies.NO_PRUNING ) );
dataSource.start();
IndexIdentifier identifier = new IndexIdentifier( LuceneCommand.NODE, dataSource.nodeEntityType, "foo" );
IndexReference oldSearcher = dataSource.getIndexSearcher( identifier );
dataSource.invalidateIndexSearcher( identifier );
IndexReference newSearcher = dataSource.getIndexSearcher( identifier );
assertNotSame( oldSearcher, newSearcher );
assertTrue( oldSearcher.isClosed() );
assertFalse( newSearcher.isClosed() );
assertNotSame( oldSearcher.getSearcher(), newSearcher.getSearcher() );
newSearcher.close();
}
private Map<String,String> config()
{
return MapUtil.stringMap(
"store_dir", getDbPath());
}
}