/* * Copyright (c) 2002-2009 "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.kernel.impl.nioneo.store; 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.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.transaction.xa.XAResource; import javax.transaction.xa.Xid; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.RelationshipType; import org.neo4j.kernel.impl.AbstractNeo4jTestCase; import org.neo4j.kernel.impl.core.LockReleaser; import org.neo4j.kernel.impl.core.PropertyIndex; import org.neo4j.kernel.impl.nioneo.xa.NeoStoreXaConnection; import org.neo4j.kernel.impl.nioneo.xa.NeoStoreXaDataSource; import org.neo4j.kernel.impl.transaction.LockManager; import org.neo4j.kernel.impl.transaction.XidImpl; public class TestXa extends AbstractNeo4jTestCase { private NeoStoreXaDataSource ds; private NeoStoreXaConnection xaCon; private Logger log; private Level level; private static class MyPropertyIndex extends org.neo4j.kernel.impl.core.PropertyIndex { private static Map<String,PropertyIndex> stringToIndex = new HashMap<String,PropertyIndex>(); private static Map<Integer,PropertyIndex> intToIndex = new HashMap<Integer,PropertyIndex>(); protected MyPropertyIndex( String key, int keyId ) { super( key, keyId ); } public static Iterable<PropertyIndex> index( String key ) { if ( stringToIndex.containsKey( key ) ) { return Arrays.asList( new PropertyIndex[] { stringToIndex .get( key ) } ); } return Collections.emptyList(); } // public static PropertyIndex getIndexFor( int index ) // { // return intToIndex.get( index ); // } public static void add( MyPropertyIndex index ) { // TODO Auto-generated method stub stringToIndex.put( index.getKey(), index ); intToIndex.put( index.getKeyId(), index ); } } @Override protected boolean restartGraphDbBetweenTests() { return true; } private PropertyIndex createDummyIndex( int id, String key ) { MyPropertyIndex index = new MyPropertyIndex( key, id ); MyPropertyIndex.add( index ); return index; } private LockManager lockManager; private LockReleaser lockReleaser; private String path() { String path = getStorePath( "xatest" ); new File( path ).mkdirs(); return path; } private String file( String name ) { return path() + File.separator + name; } @Before public void setUpNeoStore() throws Exception { log = Logger .getLogger( "org.neo4j.kernel.impl.transaction.xaframework.XaLogicalLog/" + "nioneo_logical.log" ); level = log.getLevel(); log.setLevel( Level.OFF ); log = Logger .getLogger( "org.neo4j.kernel.impl.nioneo.xa.NeoStoreXaDataSource" ); log.setLevel( Level.OFF ); NeoStore.createStore( file( "neo" ), Collections.EMPTY_MAP ); lockManager = getEmbeddedGraphDb().getConfig().getLockManager(); lockReleaser = getEmbeddedGraphDb().getConfig().getLockReleaser(); ds = new NeoStoreXaDataSource( file( "neo" ), file( "nioneo_logical.log" ), lockManager, lockReleaser ); xaCon = (NeoStoreXaConnection) ds.getXaConnection(); } @After public void tearDownNeoStore() { ds.close(); log.setLevel( level ); log = Logger .getLogger( "org.neo4j.kernel.impl.transaction.xaframework.XaLogicalLog/" + "nioneo_logical.log" ); log.setLevel( level ); log = Logger .getLogger( "org.neo4j.kernel.impl.nioneo.xa.NeoStoreXaDataSource" ); log.setLevel( level ); File file = new File( file( "neo" ) ); if ( file.exists() ) { assertTrue( file.delete() ); } file = new File( file( "neo.id" ) ); if ( file.exists() ) { assertTrue( file.delete() ); } file = new File( file( "neo.nodestore.db" ) ); if ( file.exists() ) { assertTrue( file.delete() ); } file = new File( file( "neo.nodestore.db.id" ) ); if ( file.exists() ) { assertTrue( file.delete() ); } file = new File( file( "neo.propertystore.db" ) ); if ( file.exists() ) { assertTrue( file.delete() ); } file = new File( file( "neo.propertystore.db.id" ) ); if ( file.exists() ) { assertTrue( file.delete() ); } file = new File( file( "neo.propertystore.db.index" ) ); if ( file.exists() ) { assertTrue( file.delete() ); } file = new File( file( "neo.propertystore.db.index.id" ) ); if ( file.exists() ) { assertTrue( file.delete() ); } file = new File( file( "neo.propertystore.db.index.keys" ) ); if ( file.exists() ) { assertTrue( file.delete() ); } file = new File( file( "neo.propertystore.db.index.keys.id" ) ); if ( file.exists() ) { assertTrue( file.delete() ); } file = new File( file( "neo.propertystore.db.strings" ) ); if ( file.exists() ) { assertTrue( file.delete() ); } file = new File( file( "neo.propertystore.db.strings.id" ) ); if ( file.exists() ) { assertTrue( file.delete() ); } file = new File( file( "neo.propertystore.db.arrays" ) ); if ( file.exists() ) { assertTrue( file.delete() ); } file = new File( file( "neo.propertystore.db.arrays.id" ) ); if ( file.exists() ) { assertTrue( file.delete() ); } file = new File( file( "neo.relationshipstore.db" ) ); if ( file.exists() ) { assertTrue( file.delete() ); } file = new File( file( "neo.relationshipstore.db.id" ) ); if ( file.exists() ) { assertTrue( file.delete() ); } file = new File( file( "neo.relationshiptypestore.db" ) ); if ( file.exists() ) { assertTrue( file.delete() ); } file = new File( file( "neo.relationshiptypestore.db.id" ) ); if ( file.exists() ) { assertTrue( file.delete() ); } file = new File( file( "neo.relationshiptypestore.db.names" ) ); if ( file.exists() ) { assertTrue( file.delete() ); } file = new File( file( "neo.relationshiptypestore.db.names.id" ) ); if ( file.exists() ) { assertTrue( file.delete() ); } file = new File( path() ); for ( File nioFile : file.listFiles() ) { if ( nioFile.getName().startsWith( "nioneo_logical.log" ) ) { assertTrue( nioFile.delete() ); } } } private void deleteLogicalLogIfExist() { File file = new File( file( "nioneo_logical.log.1" ) ); if ( file.exists() ) { assertTrue( file.delete() ); } file = new File( file( "nioneo_logical.log.2" ) ); if ( file.exists() ) { assertTrue( file.delete() ); } file = new File( file( "nioneo_logical.log.active" ) ); assertTrue( file.delete() ); } private void renameCopiedLogicalLog() { File file = new File( file( "nioneo_logical.log.bak.1" ) ); if ( file.exists() ) { assertTrue( file.renameTo( new File( file( "nioneo_logical.log.1" ) ) ) ); } else { file = new File( file( "nioneo_logical.log.bak.2" ) ); assertTrue( file.renameTo( new File( file( "nioneo_logical.log.2" ) ) ) ); } file = new File( file( "nioneo_logical.log.bak.active" ) ); assertTrue( file.renameTo( new File( file( "nioneo_logical.log.active" ) ) ) ); } private void truncateLogicalLog( int size ) throws IOException { char active = '1'; FileChannel af = new RandomAccessFile( file( "nioneo_logical.log.active" ), "r" ).getChannel(); ByteBuffer buffer = ByteBuffer.allocate( 1024 ); af.read( buffer ); af.close(); buffer.flip(); active = buffer.asCharBuffer().get(); buffer.clear(); FileChannel fileChannel = new RandomAccessFile( file( "nioneo_logical.log." + active ), "rw" ).getChannel(); if ( fileChannel.size() > size ) { fileChannel.truncate( size ); } else { fileChannel.position( size ); ByteBuffer buf = ByteBuffer.allocate( 1 ); buf.put( (byte) 0 ).flip(); fileChannel.write( buf ); } fileChannel.force( false ); fileChannel.close(); } private void copyLogicalLog() throws IOException { char active = '1'; FileChannel af = new RandomAccessFile( file( "nioneo_logical.log.active" ), "r" ).getChannel(); ByteBuffer buffer = ByteBuffer.allocate( 1024 ); af.read( buffer ); buffer.flip(); FileChannel activeCopy = new RandomAccessFile( file( "nioneo_logical.log.bak.active" ), "rw" ).getChannel(); activeCopy.write( buffer ); activeCopy.close(); af.close(); buffer.flip(); active = buffer.asCharBuffer().get(); buffer.clear(); FileChannel source = new RandomAccessFile( file( "nioneo_logical.log." + active ), "r" ).getChannel(); FileChannel dest = new RandomAccessFile( file( "nioneo_logical.log.bak." + active ), "rw" ).getChannel(); int read = -1; do { read = source.read( buffer ); buffer.flip(); dest.write( buffer ); buffer.clear(); } while ( read == 1024 ); source.close(); dest.close(); } private PropertyIndex index( String key ) { Iterator<PropertyIndex> itr = MyPropertyIndex.index( key ).iterator(); if ( !itr.hasNext() ) { int id = ds.nextId( PropertyIndex.class ); PropertyIndex index = createDummyIndex( id, key ); xaCon.getPropertyIndexConsumer().createPropertyIndex( id, key ); return index; } return itr.next(); } @Test public void testLogicalLog() throws Exception { Xid xid = new XidImpl( new byte[1], new byte[1] ); XAResource xaRes = xaCon.getXaResource(); xaRes.start( xid, XAResource.TMNOFLAGS ); int node1 = ds.nextId( Node.class ); xaCon.getNodeConsumer().createNode( node1 ); int node2 = ds.nextId( Node.class ); xaCon.getNodeConsumer().createNode( node2 ); int n1prop1 = ds.nextId( PropertyStore.class ); xaCon.getNodeConsumer().addProperty( node1, n1prop1, index( "prop1" ), "string1" ); xaCon.getNodeConsumer().getProperties( node1, false ); int relType1 = ds.nextId( RelationshipType.class ); xaCon.getRelationshipTypeConsumer().addRelationshipType( relType1, "relationshiptype1" ); int rel1 = ds.nextId( Relationship.class ); xaCon.getRelationshipConsumer().createRelationship( rel1, node1, node2, relType1 ); int r1prop1 = ds.nextId( PropertyStore.class ); xaCon.getRelationshipConsumer().addProperty( rel1, r1prop1, index( "prop1" ), "string1" ); xaCon.getNodeConsumer().changeProperty( node1, n1prop1, "string2" ); xaCon.getRelationshipConsumer().changeProperty( rel1, r1prop1, "string2" ); xaCon.getNodeConsumer().removeProperty( node1, n1prop1 ); xaCon.getRelationshipConsumer().removeProperty( rel1, r1prop1 ); xaCon.getRelationshipConsumer().deleteRelationship( rel1 ); xaCon.getNodeConsumer().deleteNode( node1 ); xaCon.getNodeConsumer().deleteNode( node2 ); xaRes.end( xid, XAResource.TMSUCCESS ); xaRes.commit( xid, true ); copyLogicalLog(); xaCon.clearAllTransactions(); ds.close(); deleteLogicalLogIfExist(); renameCopiedLogicalLog(); ds = new NeoStoreXaDataSource( file( "neo" ), file( "nioneo_logical.log" ), lockManager, lockReleaser ); xaCon = (NeoStoreXaConnection) ds.getXaConnection(); xaRes = xaCon.getXaResource(); assertEquals( 0, xaRes.recover( XAResource.TMNOFLAGS ).length ); xaCon.clearAllTransactions(); } @Test public void testLogicalLogPrepared() throws Exception { Xid xid = new XidImpl( new byte[2], new byte[2] ); XAResource xaRes = xaCon.getXaResource(); xaRes.start( xid, XAResource.TMNOFLAGS ); int node1 = ds.nextId( Node.class ); xaCon.getNodeConsumer().createNode( node1 ); int node2 = ds.nextId( Node.class ); xaCon.getNodeConsumer().createNode( node2 ); int n1prop1 = ds.nextId( PropertyStore.class ); xaCon.getNodeConsumer().addProperty( node1, n1prop1, index( "prop1" ), "string1" ); int relType1 = ds.nextId( RelationshipType.class ); xaCon.getRelationshipTypeConsumer().addRelationshipType( relType1, "relationshiptype1" ); int rel1 = ds.nextId( Relationship.class ); xaCon.getRelationshipConsumer().createRelationship( rel1, node1, node2, relType1 ); int r1prop1 = ds.nextId( PropertyStore.class ); xaCon.getRelationshipConsumer().addProperty( rel1, r1prop1, index( "prop1" ), "string1" ); xaCon.getNodeConsumer().changeProperty( node1, n1prop1, "string2" ); xaCon.getRelationshipConsumer().changeProperty( rel1, r1prop1, "string2" ); xaRes.end( xid, XAResource.TMSUCCESS ); xaRes.prepare( xid ); copyLogicalLog(); xaCon.clearAllTransactions(); ds.close(); deleteLogicalLogIfExist(); renameCopiedLogicalLog(); ds = new NeoStoreXaDataSource( file( "neo" ), file( "nioneo_logical.log" ), lockManager, lockReleaser ); xaCon = (NeoStoreXaConnection) ds.getXaConnection(); xaRes = xaCon.getXaResource(); assertEquals( 1, xaRes.recover( XAResource.TMNOFLAGS ).length ); xaRes.commit( xid, true ); xaCon.clearAllTransactions(); } @Test public void testLogicalLogPrePrepared() throws Exception { Xid xid = new XidImpl( new byte[3], new byte[3] ); XAResource xaRes = xaCon.getXaResource(); xaRes.start( xid, XAResource.TMNOFLAGS ); int node1 = ds.nextId( Node.class ); xaCon.getNodeConsumer().createNode( node1 ); int node2 = ds.nextId( Node.class ); xaCon.getNodeConsumer().createNode( node2 ); int n1prop1 = ds.nextId( PropertyStore.class ); xaCon.getNodeConsumer().addProperty( node1, n1prop1, index( "prop1" ), "string1" ); int relType1 = ds.nextId( RelationshipType.class ); xaCon.getRelationshipTypeConsumer().addRelationshipType( relType1, "relationshiptype1" ); int rel1 = ds.nextId( Relationship.class ); xaCon.getRelationshipConsumer().createRelationship( rel1, node1, node2, relType1 ); int r1prop1 = ds.nextId( PropertyStore.class ); xaCon.getRelationshipConsumer().addProperty( rel1, r1prop1, index( "prop1" ), "string1" ); xaCon.getNodeConsumer().changeProperty( node1, n1prop1, "string2" ); xaCon.getRelationshipConsumer().changeProperty( rel1, r1prop1, "string2" ); xaRes.end( xid, XAResource.TMSUCCESS ); xaCon.clearAllTransactions(); copyLogicalLog(); ds.close(); deleteLogicalLogIfExist(); renameCopiedLogicalLog(); ds = new NeoStoreXaDataSource( file( "neo" ), file( "nioneo_logical.log" ), lockManager, lockReleaser ); xaCon = (NeoStoreXaConnection) ds.getXaConnection(); xaRes = xaCon.getXaResource(); assertEquals( 0, xaRes.recover( XAResource.TMNOFLAGS ).length ); } @Test public void testBrokenNodeCommand() throws Exception { Xid xid = new XidImpl( new byte[4], new byte[4] ); XAResource xaRes = xaCon.getXaResource(); xaRes.start( xid, XAResource.TMNOFLAGS ); int node1 = ds.nextId( Node.class ); xaCon.getNodeConsumer().createNode( node1 ); xaRes.end( xid, XAResource.TMSUCCESS ); xaRes.prepare( xid ); xaCon.clearAllTransactions(); copyLogicalLog(); xaCon.clearAllTransactions(); ds.close(); deleteLogicalLogIfExist(); renameCopiedLogicalLog(); truncateLogicalLog( 39 ); truncateLogicalLog( 40 ); ds = new NeoStoreXaDataSource( file( "neo" ), file( "nioneo_logical.log" ), lockManager, lockReleaser ); xaCon = (NeoStoreXaConnection) ds.getXaConnection(); xaRes = xaCon.getXaResource(); assertEquals( 0, xaRes.recover( XAResource.TMNOFLAGS ).length ); xaCon.clearAllTransactions(); } @Test public void testBrokenCommand() throws Exception { Xid xid = new XidImpl( new byte[4], new byte[4] ); XAResource xaRes = xaCon.getXaResource(); xaRes.start( xid, XAResource.TMNOFLAGS ); int node1 = ds.nextId( Node.class ); xaCon.getNodeConsumer().createNode( node1 ); xaRes.end( xid, XAResource.TMSUCCESS ); xaRes.prepare( xid ); xaCon.clearAllTransactions(); copyLogicalLog(); xaCon.clearAllTransactions(); ds.close(); deleteLogicalLogIfExist(); renameCopiedLogicalLog(); truncateLogicalLog( 32 ); truncateLogicalLog( 40 ); ds = new NeoStoreXaDataSource( file( "neo" ), file( "nioneo_logical.log" ), lockManager, lockReleaser ); xaCon = (NeoStoreXaConnection) ds.getXaConnection(); xaRes = xaCon.getXaResource(); assertEquals( 0, xaRes.recover( XAResource.TMNOFLAGS ).length ); xaCon.clearAllTransactions(); } @Test public void testBrokenPrepare() throws Exception { Xid xid = new XidImpl( new byte[4], new byte[4] ); XAResource xaRes = xaCon.getXaResource(); xaRes.start( xid, XAResource.TMNOFLAGS ); int node1 = ds.nextId( Node.class ); xaCon.getNodeConsumer().createNode( node1 ); int node2 = ds.nextId( Node.class ); xaCon.getNodeConsumer().createNode( node2 ); int n1prop1 = ds.nextId( PropertyStore.class ); xaCon.getNodeConsumer().addProperty( node1, n1prop1, index( "prop1" ), "string1" ); xaRes.end( xid, XAResource.TMSUCCESS ); xaRes.prepare( xid ); copyLogicalLog(); xaCon.clearAllTransactions(); ds.close(); deleteLogicalLogIfExist(); renameCopiedLogicalLog(); truncateLogicalLog( 141 ); ds = new NeoStoreXaDataSource( file( "neo" ), file( "nioneo_logical.log" ), lockManager, lockReleaser ); xaCon = (NeoStoreXaConnection) ds.getXaConnection(); xaRes = xaCon.getXaResource(); assertEquals( 0, xaRes.recover( XAResource.TMNOFLAGS ).length ); xaCon.clearAllTransactions(); } @Test public void testBrokenDone() throws Exception { Xid xid = new XidImpl( new byte[4], new byte[4] ); XAResource xaRes = xaCon.getXaResource(); xaRes.start( xid, XAResource.TMNOFLAGS ); int node1 = ds.nextId( Node.class ); xaCon.getNodeConsumer().createNode( node1 ); int node2 = ds.nextId( Node.class ); xaCon.getNodeConsumer().createNode( node2 ); int n1prop1 = ds.nextId( PropertyStore.class ); xaCon.getNodeConsumer().addProperty( node1, n1prop1, index( "prop1" ), "string1" ); xaRes.end( xid, XAResource.TMSUCCESS ); xaRes.prepare( xid ); xaRes.commit( xid, false ); copyLogicalLog(); ds.close(); deleteLogicalLogIfExist(); renameCopiedLogicalLog(); truncateLogicalLog( 145 ); ds = new NeoStoreXaDataSource( file( "neo" ), file( "nioneo_logical.log" ), lockManager, lockReleaser ); xaCon = (NeoStoreXaConnection) ds.getXaConnection(); xaRes = xaCon.getXaResource(); assertEquals( 1, xaRes.recover( XAResource.TMNOFLAGS ).length ); xaCon.clearAllTransactions(); } @Test public void testLogVersion() { long creationTime = ds.getCreationTime(); long randomIdentifier = ds.getRandomIdentifier(); long currentVersion = ds.getCurrentLogVersion(); assertEquals( currentVersion, ds.incrementAndGetLogVersion() ); assertEquals( currentVersion + 1, ds.incrementAndGetLogVersion() ); assertEquals( creationTime, ds.getCreationTime() ); assertEquals( randomIdentifier, ds.getRandomIdentifier() ); } @Test public void testLogicalLogRotation() throws Exception { ds.keepLogicalLogs( true ); Xid xid = new XidImpl( new byte[1], new byte[1] ); XAResource xaRes = xaCon.getXaResource(); xaRes.start( xid, XAResource.TMNOFLAGS ); int node1 = ds.nextId( Node.class ); xaCon.getNodeConsumer().createNode( node1 ); int node2 = ds.nextId( Node.class ); xaCon.getNodeConsumer().createNode( node2 ); int n1prop1 = ds.nextId( PropertyStore.class ); xaCon.getNodeConsumer().addProperty( node1, n1prop1, index( "prop1" ), "string1" ); xaCon.getNodeConsumer().getProperties( node1, false ); int relType1 = ds.nextId( RelationshipType.class ); xaCon.getRelationshipTypeConsumer().addRelationshipType( relType1, "relationshiptype1" ); int rel1 = ds.nextId( Relationship.class ); xaCon.getRelationshipConsumer().createRelationship( rel1, node1, node2, relType1 ); int r1prop1 = ds.nextId( PropertyStore.class ); xaCon.getRelationshipConsumer().addProperty( rel1, r1prop1, index( "prop1" ), "string1" ); xaCon.getNodeConsumer().changeProperty( node1, n1prop1, "string2" ); xaCon.getRelationshipConsumer().changeProperty( rel1, r1prop1, "string2" ); xaCon.getNodeConsumer().removeProperty( node1, n1prop1 ); xaCon.getRelationshipConsumer().removeProperty( rel1, r1prop1 ); xaCon.getRelationshipConsumer().deleteRelationship( rel1 ); xaCon.getNodeConsumer().deleteNode( node1 ); xaCon.getNodeConsumer().deleteNode( node2 ); xaRes.end( xid, XAResource.TMSUCCESS ); xaRes.commit( xid, true ); long currentVersion = ds.getCurrentLogVersion(); ds.rotateLogicalLog(); assertTrue( ds.getLogicalLog( currentVersion ) != null ); ds.rotateLogicalLog(); assertTrue( ds.getLogicalLog( currentVersion ) != null ); assertTrue( ds.getLogicalLog( currentVersion + 1 ) != null ); } @Test public void testApplyLogicalLog() throws Exception { ds.keepLogicalLogs( true ); Xid xid = new XidImpl( new byte[1], new byte[1] ); XAResource xaRes = xaCon.getXaResource(); xaRes.start( xid, XAResource.TMNOFLAGS ); int node1 = ds.nextId( Node.class ); xaCon.getNodeConsumer().createNode( node1 ); int node2 = ds.nextId( Node.class ); xaCon.getNodeConsumer().createNode( node2 ); int n1prop1 = ds.nextId( PropertyStore.class ); xaCon.getNodeConsumer().addProperty( node1, n1prop1, index( "prop1" ), "string1" ); xaCon.getNodeConsumer().getProperties( node1, false ); int relType1 = ds.nextId( RelationshipType.class ); xaCon.getRelationshipTypeConsumer().addRelationshipType( relType1, "relationshiptype1" ); int rel1 = ds.nextId( Relationship.class ); xaCon.getRelationshipConsumer().createRelationship( rel1, node1, node2, relType1 ); int r1prop1 = ds.nextId( PropertyStore.class ); xaCon.getRelationshipConsumer().addProperty( rel1, r1prop1, index( "prop1" ), "string1" ); xaCon.getNodeConsumer().changeProperty( node1, n1prop1, "string2" ); xaCon.getRelationshipConsumer().changeProperty( rel1, r1prop1, "string2" ); xaCon.getNodeConsumer().removeProperty( node1, n1prop1 ); xaCon.getRelationshipConsumer().removeProperty( rel1, r1prop1 ); xaCon.getRelationshipConsumer().deleteRelationship( rel1 ); xaCon.getNodeConsumer().deleteNode( node1 ); xaCon.getNodeConsumer().deleteNode( node2 ); xaRes.end( xid, XAResource.TMSUCCESS ); xaRes.commit( xid, true ); long currentVersion = ds.getCurrentLogVersion(); ds.keepLogicalLogs( true ); ds.rotateLogicalLog(); ds.rotateLogicalLog(); ds.rotateLogicalLog(); ds.setCurrentLogVersion( currentVersion ); ds.makeBackupSlave(); ds.applyLog( ds.getLogicalLog( currentVersion ) ); ds.applyLog( ds.getLogicalLog( currentVersion + 1 ) ); ds.applyLog( ds.getLogicalLog( currentVersion + 2 ) ); ds.keepLogicalLogs( false ); } }