/**
* Copyright (c) 2002-2011 "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 slavetest;
import java.io.Serializable;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.Map;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.DynamicRelationshipType;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.NotFoundException;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.index.Index;
import org.neo4j.ha.StandaloneDatabase;
import org.neo4j.kernel.Config;
import org.neo4j.kernel.DeadlockDetectedException;
import org.neo4j.kernel.IdType;
import org.neo4j.kernel.ha.LockableNode;
import org.neo4j.kernel.impl.core.LockReleaser;
import org.neo4j.kernel.impl.nioneo.store.IdGenerator;
import org.neo4j.kernel.impl.transaction.LockManager;
import org.neo4j.kernel.impl.transaction.LockType;
public abstract class CommonJobs
{
public static final RelationshipType REL_TYPE = DynamicRelationshipType.withName( "HA_TEST" );
public static final RelationshipType KNOWS = DynamicRelationshipType.withName( "KNOWS" );
public static abstract class AbstractJob<T> implements Job<T>
{
protected Config getConfig( GraphDatabaseService db )
{
Config config = null;
try
{
return (Config) db.getClass().getDeclaredMethod( "getConfig" ).invoke( db );
}
catch ( Exception e )
{
// Won't happen
throw new RuntimeException( e );
}
}
}
public static abstract class TransactionalJob<T> extends AbstractJob<T>
{
public final T execute( GraphDatabaseService db ) throws RemoteException
{
Transaction tx = db.beginTx();
try
{
return executeInTransaction( db, tx );
}
catch ( RuntimeException e )
{
e.printStackTrace( System.out );
throw e;
}
finally
{
beforeFinish();
tx.finish();
}
}
protected void beforeFinish()
{
}
protected abstract T executeInTransaction( GraphDatabaseService db, Transaction tx );
}
public static class CreateSubRefNodeJob extends TransactionalJob<Long>
{
private final String type;
private final String key;
private final Object value;
public CreateSubRefNodeJob( String type, String key, Object value )
{
this.type = type;
this.key = key;
this.value = value;
}
@Override
protected Long executeInTransaction( GraphDatabaseService db, Transaction tx )
{
Node node = db.createNode();
Relationship rel = db.getReferenceNode().createRelationshipTo( node,
DynamicRelationshipType.withName( type ) );
rel.setProperty( "something else", "Somewhat different" );
if ( value != null )
{
node.setProperty( key, value );
}
tx.success();
return node.getId();
}
}
public static class CreateSubRefNodeWithRelCountJob extends TransactionalJob<Integer>
{
private final String type;
private final String[] typesToAsk;
public CreateSubRefNodeWithRelCountJob( String type,
String... typesToAsk )
{
this.type = type;
this.typesToAsk = typesToAsk;
}
@Override
protected Integer executeInTransaction( GraphDatabaseService db, Transaction tx )
{
Node node = db.createNode();
db.getReferenceNode().createRelationshipTo( node,
DynamicRelationshipType.withName( type ) );
int counter = 0;
for ( Relationship rel : db.getReferenceNode().getRelationships(
toRelationshipTypes( typesToAsk ) ) )
{
counter++;
}
tx.success();
return counter;
}
}
public static class CreateSubRefNodeMasterFailJob implements Job<Serializable[]>
{
private final CommonJobs.ShutdownDispatcher shutdownDispatcher;
public CreateSubRefNodeMasterFailJob( CommonJobs.ShutdownDispatcher shutdownDispatcher )
{
this.shutdownDispatcher = shutdownDispatcher;
}
public Serializable[] execute( GraphDatabaseService db ) throws RemoteException
{
Transaction tx = db.beginTx();
boolean successful = false;
long nodeId = 0;
try
{
Node node = db.createNode();
nodeId = node.getId();
db.getReferenceNode().createRelationshipTo( node, REL_TYPE );
tx.success();
}
finally
{
this.shutdownDispatcher.doShutdown();
try
{
tx.finish();
successful = true;
}
catch ( RuntimeException e )
{
}
}
return new Serializable[] { successful, nodeId };
}
}
public static class SetSubRefPropertyJob extends TransactionalJob<Object>
{
private final String key;
private final Object value;
public SetSubRefPropertyJob( String key, Object value )
{
this.key = key;
this.value = value;
}
@Override
protected Object executeInTransaction( GraphDatabaseService db, Transaction tx )
{
Node refNode = db.getReferenceNode();
// To force it to pull updates
refNode.removeProperty( "yoyoyoyo" );
Node node = refNode.getSingleRelationship( REL_TYPE,
Direction.OUTGOING ).getEndNode();
Object oldValue = node.getProperty( key, null );
node.setProperty( key, value );
tx.success();
return oldValue;
}
}
public static class CreateSomeEntitiesJob extends TransactionalJob<Void>
{
@Override
protected Void executeInTransaction( GraphDatabaseService db, Transaction tx )
{
Node node1 = db.createNode();
Relationship rel1 = db.getReferenceNode().createRelationshipTo( node1, REL_TYPE );
node1.setProperty( "name", "Mattias" );
rel1.setProperty( "something else", "Somewhat different" );
Node node2 = db.createNode();
Relationship rel2 = node1.createRelationshipTo( node2, REL_TYPE );
node2.setProperty( "why o why", "Stuff" );
rel2.setProperty( "random integer", "4" );
tx.success();
return null;
}
}
public static class GetNodeByIdJob implements Job<Boolean>
{
private final long id;
public GetNodeByIdJob( long id )
{
this.id = id;
}
public Boolean execute( GraphDatabaseService db )
{
try
{
db.getNodeById( id );
return Boolean.TRUE;
}
catch ( NotFoundException e )
{
return Boolean.FALSE;
}
}
}
public interface ShutdownDispatcher extends Remote
{
void doShutdown() throws RemoteException;
}
public static class ShutdownJvm extends UnicastRemoteObject implements ShutdownDispatcher
{
private final StandaloneDatabase jvm;
public ShutdownJvm( StandaloneDatabase jvm ) throws RemoteException
{
super();
this.jvm = jvm;
}
public void doShutdown()
{
this.jvm.shutdown();
}
}
public static class DeleteNodeJob implements Job<Boolean>
{
private final long id;
private final boolean deleteIndexing;
public DeleteNodeJob( long id, boolean deleteIndexing )
{
this.id = id;
this.deleteIndexing = deleteIndexing;
}
public Boolean execute( GraphDatabaseService db ) throws RemoteException
{
Transaction tx = db.beginTx();
boolean successful = false;
try
{
Node node = db.getNodeById( id );
// if ( deleteIndexing )
// {
// IndexService index = ((HighlyAvailableGraphDatabase) db).getIndexService();
// for ( String key : node.getPropertyKeys() )
// {
// index.removeIndex( node, key, node.getProperty( key ) );
// }
// }
node.delete();
tx.success();
}
finally
{
try
{
tx.finish();
successful = true;
}
catch ( RuntimeException e )
{
}
}
return successful;
}
}
public static class GetRelationshipCountJob implements Job<Integer>
{
private final String[] types;
public GetRelationshipCountJob( String... types )
{
this.types = types;
}
public Integer execute( GraphDatabaseService db )
{
int counter = 0;
for ( Relationship rel : db.getReferenceNode().getRelationships(
toRelationshipTypes( types ) ) )
{
counter++;
}
return counter;
}
}
private static RelationshipType[] toRelationshipTypes( String... names )
{
RelationshipType[] types = new RelationshipType[names.length];
for ( int i = 0; i < names.length; i++ )
{
types[i] = DynamicRelationshipType.withName( names[i] );
}
return types;
}
public static class CreateNodeOutsideOfTxJob implements Job<Boolean>
{
public Boolean execute( GraphDatabaseService db ) throws RemoteException
{
try
{
db.getReferenceNode().createRelationshipTo( db.createNode(), REL_TYPE );
return Boolean.TRUE;
}
catch ( Exception e )
{
return Boolean.FALSE;
}
}
}
public static class CreateNodeJob extends TransactionalJob<Long>
{
@Override
protected Long executeInTransaction( GraphDatabaseService db, Transaction tx )
{
Node node = db.createNode();
tx.success();
return node.getId();
}
}
public static class SetNodePropertyJob extends TransactionalJob<Boolean>
{
private final long id;
private final String key;
private final Object value;
public SetNodePropertyJob( long id, String key, Object value )
{
this.id = id;
this.key = key;
this.value = value;
}
@Override
protected Boolean executeInTransaction( GraphDatabaseService db, Transaction tx )
{
try
{
db.getNodeById( id ).setProperty( key, value );
tx.success();
return Boolean.TRUE;
}
catch ( Exception e )
{
return Boolean.FALSE;
}
}
}
public static class CreateNodesJob extends TransactionalJob<Long[]>
{
private final int count;
public CreateNodesJob( int count )
{
this.count = count;
}
@Override
protected Long[] executeInTransaction( GraphDatabaseService db, Transaction tx )
{
Long[] result = new Long[count];
for ( int i = 0; i < count; i++ )
{
result[i] = db.createNode().getId();
}
tx.success();
return result;
}
}
public static class Worker1Job extends TransactionalJob<Boolean[]>
{
private final long node1;
private final long node2;
private final Fetcher<DoubleLatch> fetcher;
public Worker1Job( long node1, long node2, Fetcher<DoubleLatch> fetcher )
{
this.node1 = node1;
this.node2 = node2;
this.fetcher = fetcher;
}
@Override
protected Boolean[] executeInTransaction( GraphDatabaseService db, Transaction tx )
{
boolean success = false;
boolean deadlock = false;
try
{
DoubleLatch latch = fetcher.fetch();
db.getNodeById( node1 ).setProperty( "1", "T1 1" );
latch.countDownSecond();
latch.awaitFirst();
db.getNodeById( node2 ).removeProperty( "2" );
db.getNodeById( node1 ).removeProperty( "1" );
tx.success();
success = true;
}
catch ( DeadlockDetectedException e )
{
deadlock = true;
}
catch ( Exception e )
{
throw new RuntimeException( e );
}
return new Boolean[] { success, deadlock };
}
}
public static class Worker2Job extends TransactionalJob<Boolean[]>
{
private final long node1;
private final long node2;
private final Fetcher<DoubleLatch> fetcher;
public Worker2Job( long node1, long node2, Fetcher<DoubleLatch> fetcher )
{
this.node1 = node1;
this.node2 = node2;
this.fetcher = fetcher;
}
@Override
protected Boolean[] executeInTransaction( GraphDatabaseService db, Transaction tx )
{
boolean success = false;
boolean deadlock = false;
try
{
DoubleLatch latch = fetcher.fetch();
db.getNodeById( node2 ).setProperty( "2", "T2 2" );
latch.countDownFirst();
latch.awaitSecond();
db.getNodeById( node1 ).setProperty( "1", "T2 2" );
tx.success();
success = true;
}
catch ( DeadlockDetectedException e )
{
deadlock = true;
}
catch ( Exception e )
{
throw new RuntimeException( e );
}
return new Boolean[] { success, deadlock };
}
}
public static class PerformanceAcquireWriteLocksJob extends TransactionalJob<Void>
{
private final int amount;
public PerformanceAcquireWriteLocksJob( int amount )
{
this.amount = amount;
}
@Override
protected Void executeInTransaction( GraphDatabaseService db, Transaction tx )
{
Config config = getConfig( db );
LockManager lockManager = config.getLockManager();
LockReleaser lockReleaser = config.getLockReleaser();
for ( int i = 0; i < amount; i++ )
{
Object resource = new LockableNode( i );
lockManager.getWriteLock( resource );
lockReleaser.addLockToTransaction( resource, LockType.WRITE );
}
return null;
}
}
public static class PerformanceIdAllocationJob extends AbstractJob<Void>
{
private final int count;
public PerformanceIdAllocationJob( int count )
{
this.count = count;
}
public Void execute( GraphDatabaseService db )
{
Config config = getConfig( db );
IdGenerator generator = config.getIdGeneratorFactory().get( IdType.NODE );
for ( int i = 0; i < count; i++ )
{
generator.nextId();
}
return null;
}
}
public static class PerformanceCreateNodesJob extends AbstractJob<Void>
{
private final int numTx;
private final int numNodesInEach;
public PerformanceCreateNodesJob( int numTx, int numNodesInEach )
{
this.numTx = numTx;
this.numNodesInEach = numNodesInEach;
}
public Void execute( GraphDatabaseService db ) throws RemoteException
{
for ( int i = 0; i < numTx; i++ )
{
Transaction tx = db.beginTx();
try
{
for ( int ii = 0; ii < numNodesInEach; ii++ )
{
db.createNode();
}
tx.success();
}
finally
{
tx.finish();
}
}
return null;
}
}
public static class CreateNodeAndIndexJob extends TransactionalJob<Long>
{
private final String key;
private final Object value;
public CreateNodeAndIndexJob( String key, Object value )
{
this.key = key;
this.value = value;
}
@Override
protected Long executeInTransaction( GraphDatabaseService db, Transaction tx )
{
// IndexService index = db instanceof HighlyAvailableGraphDatabase ?
// ((HighlyAvailableGraphDatabase) db).getIndexService() :
// new LuceneIndexService( db );
Node node = db.createNode();
node.setProperty( key, value );
// index.index( node, key, value );
tx.success();
return node.getId();
}
}
public static class CreateNodeAndNewIndexJob extends TransactionalJob<Long>
{
private final String key;
private final Object value;
private final String indexName;
private final String key2;
private final Object value2;
public CreateNodeAndNewIndexJob( String indexName,
String key, Object value, String key2, Object value2 )
{
this.indexName = indexName;
this.key = key;
this.value = value;
this.key2 = key2;
this.value2 = value2;
}
@Override
protected Long executeInTransaction( GraphDatabaseService db, Transaction tx )
{
Node node = db.createNode();
node.setProperty( key, value );
node.setProperty( key2, value2 );
Index<Node> index = db.index().forNodes( indexName );
index.add( node, key, value );
index.add( node, key2, value2 );
tx.success();
return node.getId();
}
}
public static class AddIndex extends TransactionalJob<Void>
{
private final Map<String, Object> properties;
private final long nodeId;
public AddIndex( long nodeId, Map<String, Object> properties )
{
this.nodeId = nodeId;
this.properties = properties;
}
@Override
protected Void executeInTransaction( GraphDatabaseService db, Transaction tx )
{
// IndexService index = ((HighlyAvailableGraphDatabase) db).getIndexService();
Node node = db.getNodeById( nodeId );
for ( Map.Entry<String, Object> entry : properties.entrySet() )
{
// index.index( node, entry.getKey(), entry.getValue() );
}
tx.success();
return null;
}
}
public static class LargeTransactionJob extends AbstractJob<Void>
{
private final int txSizeMb;
private final int numTxs;
public LargeTransactionJob( int txSizeMb, int numTxs )
{
this.txSizeMb = txSizeMb;
this.numTxs = numTxs;
}
public Void execute( GraphDatabaseService db ) throws RemoteException
{
byte[] largeArray = new byte[1*1024*1021]; /* 1021 So that it doesn't align with block size in BlockLogBuffer and all that :) */
for ( int t = 0; t < numTxs; t++ )
{
Transaction tx = db.beginTx();
try
{
for ( int i = 0; i < largeArray.length; i++ )
{
largeArray[i] = (byte) (i % 256);
}
for ( int i = 0; i < txSizeMb; i++ )
{
Node node = db.createNode();
node.setProperty( "data", largeArray );
}
tx.success();
}
finally
{
tx.finish();
}
}
return null;
}
}
}