/** * 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.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import java.io.File; import java.util.Map; import org.junit.Test; import org.neo4j.graphdb.DynamicRelationshipType; import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.RelationshipType; import org.neo4j.graphdb.factory.GraphDatabaseFactory; import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.graphdb.index.Index; import org.neo4j.helpers.collection.MapUtil; import org.neo4j.index.Neo4jTestCase; 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.StringLogger; import org.neo4j.test.ProcessStreamHandler; /** * 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 GraphDatabaseFactory().newEmbeddedDatabase( path ); } @Test public void testRecovery() throws Exception { GraphDatabaseService graphDb = newGraphDbService(); Index<Node> nodeIndex = graphDb.index().forNodes( "node-index" ); Index<Relationship> relIndex = graphDb.index().forRelationships( "rel-index" ); RelationshipType relType = DynamicRelationshipType.withName( "recovery" ); graphDb.beginTx(); Node node = graphDb.createNode(); Node otherNode = graphDb.createNode(); Relationship rel = node.createRelationshipTo( otherNode, relType ); nodeIndex.add( node, "key1", "string value" ); nodeIndex.add( node, "key2", 12345 ); relIndex.add( rel, "key1", "string value" ); relIndex.add( rel, "key2", 12345 ); graphDb.shutdown(); // Start up and let it recover final GraphDatabaseService newGraphDb = new GraphDatabaseFactory().newEmbeddedDatabase( getDbPath() ); newGraphDb.shutdown(); } @Test public void testAsLittleAsPossibleRecoveryScenario() throws Exception { GraphDatabaseService db = newGraphDbService(); Index<Node> index = db.index().forNodes( "my-index" ); db.beginTx(); Node node = db.createNode(); index.add( node, "key", "value" ); db.shutdown(); // This doesn't seem to trigger recovery... it really should new GraphDatabaseFactory().newEmbeddedDatabase( getDbPath() ).shutdown(); } @Test public void testIndexDeleteIssue() throws Exception { GraphDatabaseService db = newGraphDbService(); db.index().forNodes( "index" ); db.shutdown(); Process process = Runtime.getRuntime().exec( new String[]{ "java", "-cp", System.getProperty( "java.class.path" ), AddDeleteQuit.class.getName(), getDbPath() } ); assertEquals( 0, new ProcessStreamHandler( process, true ).waitForResult() ); new GraphDatabaseFactory().newEmbeddedDatabase( getDbPath() ).shutdown(); db.shutdown(); } @Test public void recoveryForRelationshipCommandsOnly() throws Exception { String path = getDbPath(); Neo4jTestCase.deleteFileOrDirectory( new File( path ) ); Process process = Runtime.getRuntime().exec( new String[]{ "java", "-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005", "-cp", System.getProperty( "java.class.path" ), AddRelToIndex.class.getName(), getDbPath() } ); assertEquals( 0, new ProcessStreamHandler( process, true ).waitForResult() ); // I would like to do this, but there's no exception propagated out from the constructor // if the recovery fails. // new EmbeddedGraphDatabase( getDbPath() ).shutdown(); // Instead I have to do this FileSystemAbstraction fileSystemAbstraction = new DefaultFileSystemAbstraction(); FileSystemAbstraction fileSystem = fileSystemAbstraction; Map<String, String> params = MapUtil.stringMap( "store_dir", getDbPath()); Config config = new Config( new ConfigurationDefaults(GraphDatabaseSettings.class ).apply(params )); LuceneDataSource ds = new LuceneDataSource( config, new IndexStore( getDbPath(), fileSystem ), fileSystem, new XaFactory( config, TxIdGenerator.DEFAULT, new PlaceboTm(), new DefaultLogBufferFactory(), fileSystemAbstraction, StringLogger.DEV_NULL, RecoveryVerifier.ALWAYS_VALID, LogPruneStrategies.NO_PRUNING )); ds.start(); ds.stop(); } @Test public void recoveryOnDeletedIndex() throws Exception { GraphDatabaseService db = newGraphDbService(); db.index().forNodes( "index" ); db.shutdown(); Process process = Runtime.getRuntime().exec( new String[]{ "java", "-cp", System.getProperty( "java.class.path" ), AddThenDeleteInAnotherTxAndQuit.class.getName(), getDbPath() } ); assertEquals( 0, new ProcessStreamHandler( process, true ).waitForResult() ); db = new GraphDatabaseFactory().newEmbeddedDatabase( getDbPath() ); assertFalse( db.index().existsForNodes( "index" ) ); assertNotNull( db.index().forNodes( "index2" ).get( "key", "value" ).getSingle() ); db.shutdown(); } }