/**
* Copyright (c) 2002-2010 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.index.lucene;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.junit.Test;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;
import org.neo4j.index.IndexService;
import org.neo4j.index.Neo4jTestCase;
import org.neo4j.kernel.EmbeddedGraphDatabase;
import org.neo4j.kernel.impl.transaction.XidImpl;
/**
* Don't extend Neo4jTestCase since these tests restarts the db in the tests.
*/
public class TestRecovery
{
private String getDbPath()
{
return "target/var/recovery";
}
private GraphDatabaseService newGraphDbService()
{
String path = getDbPath();
Neo4jTestCase.deleteFileOrDirectory( new File( path ) );
return new EmbeddedGraphDatabase( path );
}
@Test
public void testRecovery() throws Exception
{
final GraphDatabaseService graphDb = newGraphDbService();
final IndexService index = new LuceneIndexService( graphDb );
graphDb.beginTx();
Node node = graphDb.createNode();
Random random = new Random();
Thread stopper = new Thread()
{
@Override public void run()
{
sleepNice( 1000 );
index.shutdown();
graphDb.shutdown();
}
};
try
{
stopper.start();
for ( int i = 0; i < 500; i++ )
{
index.index( node, "" + random.nextInt(), random.nextInt() );
sleepNice( 10 );
}
}
catch ( Exception e )
{
// Ok
}
sleepNice( 1000 );
final GraphDatabaseService newGraphDb =
new EmbeddedGraphDatabase( getDbPath() );
final IndexService newIndexService = new LuceneIndexService( newGraphDb );
sleepNice( 1000 );
newIndexService.shutdown();
newGraphDb.shutdown();
}
private static void sleepNice( long time )
{
try
{
Thread.sleep( time );
}
catch ( InterruptedException e )
{
// Ok
}
}
@Test
public void testReCommit() throws Exception
{
GraphDatabaseService graphDb = newGraphDbService();
IndexService idx = new LuceneIndexService( graphDb );
Transaction tx = graphDb.beginTx();
assertEquals( null, idx.getSingleNode( "test", "1" ) );
Node refNode = graphDb.getReferenceNode();
tx.finish();
idx.shutdown();
Map<Object,Object> params = new HashMap<Object,Object>();
String luceneDir = getDbPath() + "/lucene";
params.put( "dir", luceneDir );
params.put( "store_dir", getDbPath() );
LuceneDataSource xaDs = new LuceneDataSource( params );
LuceneXaConnection xaC = (LuceneXaConnection) xaDs.getXaConnection();
XAResource xaR = xaC.getXaResource();
Xid xid = new XidImpl( new byte[1], new byte[1] );
xaR.start( xid, XAResource.TMNOFLAGS );
xaC.index( refNode, "test", "1" );
xaR.end( xid, XAResource.TMSUCCESS );
xaR.prepare( xid );
xaR.commit( xid, false );
copyLogicalLog( luceneDir + "/lucene.log.active",
luceneDir + "/lucene.log.active.bak" );
copyLogicalLog( luceneDir + "/lucene.log.1",
luceneDir + "/lucene.log.1.bak" );
// test recovery re-commit
idx = new LuceneIndexService( graphDb );
tx = graphDb.beginTx();
assertEquals( refNode, idx.getSingleNode( "test", "1" ) );
tx.finish();
idx.shutdown();
assertTrue( new File( luceneDir + "/lucene.log.active" ).delete() );
// test recovery again on same log and only still only get 1 node
copyLogicalLog( luceneDir + "/lucene.log.active.bak",
luceneDir + "/lucene.log.active" );
copyLogicalLog( luceneDir + "/lucene.log.1.bak",
luceneDir + "/lucene.log.1" );
idx = new LuceneIndexService( graphDb );
tx = graphDb.beginTx();
assertEquals( refNode, idx.getSingleNode( "test", "1" ) );
tx.finish();
idx.shutdown();
graphDb.shutdown();
}
private void copyLogicalLog( String name, String copy ) throws IOException
{
ByteBuffer buffer = ByteBuffer.allocate( 1024 );
assertTrue( new File( name ).exists() );
FileChannel source = new RandomAccessFile( name, "r" ).getChannel();
assertTrue( !new File( copy ).exists() );
FileChannel dest = new RandomAccessFile( copy, "rw" ).getChannel();
int read = -1;
do
{
read = source.read( buffer );
buffer.flip();
dest.write( buffer );
buffer.clear();
}
while ( read == 1024 );
source.close();
dest.close();
}
@Test
public void testRecoveryFulltextIndex()
{
GraphDatabaseService graphDb = new EmbeddedGraphDatabase(
"target/graphdb" );
LuceneFulltextIndexService idx = new LuceneFulltextIndexService(
graphDb );
Transaction tx = graphDb.beginTx();
try
{
Node node = graphDb.createNode();
idx.index( node, "test", "value" );
tx.success();
}
finally
{
// no tx commit
}
idx.shutdown();
graphDb.shutdown();
graphDb = new EmbeddedGraphDatabase( "target/graphdb" );
graphDb.shutdown();
}
}