Skip to content

Commit

Permalink
Always add guarding operations level to have possibility to terminate…
Browse files Browse the repository at this point in the history
… transaction with custom timeouts.

Allow execution of queries with custom timeout using GraphDatabaseService API.
  • Loading branch information
MishaDemianenko committed Sep 7, 2016
1 parent 5874196 commit ef9d6d8
Show file tree
Hide file tree
Showing 11 changed files with 84 additions and 110 deletions.
Expand Up @@ -66,7 +66,7 @@ class PerformanceTest extends CypherFunSuite {
val t0: Double = System.nanoTime val t0: Double = System.nanoTime


val result = db.execute("start a=node({root}) match a-->b-->c, b-->d return a,count(*)", val result = db.execute("start a=node({root}) match a-->b-->c, b-->d return a,count(*)",
Collections.singletonMap("root", startPoints)) Collections.singletonMap("root", startPoints), 0)
result.resultAsString() result.resultAsString()
result.close() result.close()


Expand Down
Expand Up @@ -249,6 +249,7 @@ public interface GraphDatabaseService
* Please ensure that any returned {@link ResourceIterable} is closed correctly and as soon as possible * Please ensure that any returned {@link ResourceIterable} is closed correctly and as soon as possible
* inside your transaction to avoid potential blocking of write operations. * inside your transaction to avoid potential blocking of write operations.
* *
* @param timeout transaction timeout
* @return a new transaction instance * @return a new transaction instance
*/ */
Transaction beginTx( long timeout ); Transaction beginTx( long timeout );
Expand All @@ -264,6 +265,19 @@ public interface GraphDatabaseService
*/ */
Result execute( String query ) throws QueryExecutionException; Result execute( String query ) throws QueryExecutionException;


/**
* Executes a query and returns an iterable that contains the result set.
* If query will not gonna be able to complete within specified timeout time interval it will be terminated.
*
* This method is the same as {@link #execute(String, java.util.Map)} with an empty parameters-map.
*
* @param query The query to execute
* @param timeout The maximum time interval within which query should be completed.
* @return A {@link org.neo4j.graphdb.Result} that contains the result set.
* @throws QueryExecutionException If the Query contains errors
*/
Result execute( String query, long timeout ) throws QueryExecutionException;

/** /**
* Executes a query and returns an iterable that contains the result set. * Executes a query and returns an iterable that contains the result set.
* *
Expand All @@ -274,6 +288,18 @@ public interface GraphDatabaseService
*/ */
Result execute( String query, Map<String,Object> parameters ) throws QueryExecutionException; Result execute( String query, Map<String,Object> parameters ) throws QueryExecutionException;


/**
* Executes a query and returns an iterable that contains the result set.
* If query will not gonna be able to complete within specified timeout time interval it will be terminated.
*
* @param query The query to execute
* @param parameters Parameters for the query
* @param timeout The maximum time interval within which query should be completed.
* @return A {@link org.neo4j.graphdb.Result} that contains the result set
* @throws QueryExecutionException If the Query contains errors
*/
Result execute( String query, Map<String,Object> parameters, long timeout ) throws QueryExecutionException;

/** /**
* Registers {@code handler} as a handler for transaction events which * Registers {@code handler} as a handler for transaction events which
* are generated from different places in the lifecycle of each * are generated from different places in the lifecycle of each
Expand Down
Expand Up @@ -48,7 +48,6 @@
import org.neo4j.kernel.api.legacyindex.AutoIndexing; import org.neo4j.kernel.api.legacyindex.AutoIndexing;
import org.neo4j.kernel.configuration.Config; import org.neo4j.kernel.configuration.Config;
import org.neo4j.kernel.extension.dependency.HighestSelectionStrategy; import org.neo4j.kernel.extension.dependency.HighestSelectionStrategy;
import org.neo4j.kernel.guard.EmptyGuard;
import org.neo4j.kernel.guard.Guard; import org.neo4j.kernel.guard.Guard;
import org.neo4j.kernel.impl.api.CommitProcessFactory; import org.neo4j.kernel.impl.api.CommitProcessFactory;
import org.neo4j.kernel.impl.api.ConstraintEnforcingEntityOperations; import org.neo4j.kernel.impl.api.ConstraintEnforcingEntityOperations;
Expand Down Expand Up @@ -1009,13 +1008,10 @@ private StatementOperationParts buildStatementOperations(
parts = parts.override( null, null, null, lockingContext, lockingContext, lockingContext, lockingContext, parts = parts.override( null, null, null, lockingContext, lockingContext, lockingContext, lockingContext,
lockingContext, null, null, null, null ); lockingContext, null, null, null, null );
// + Guard // + Guard
if ( !EmptyGuard.EMPTY_GUARD.equals( guard ) ) GuardingStatementOperations guardingOperations = new GuardingStatementOperations(
{ parts.entityWriteOperations(), parts.entityReadOperations(), guard );
GuardingStatementOperations guardingOperations = new GuardingStatementOperations( parts = parts.override( null, null, guardingOperations, guardingOperations, null, null, null, null,
parts.entityWriteOperations(), parts.entityReadOperations(), guard ); null, null, null, null );
parts = parts.override( null, null, guardingOperations, guardingOperations, null, null, null, null,
null, null, null, null );
}


return parts; return parts;
} }
Expand Down

This file was deleted.

Expand Up @@ -48,7 +48,10 @@ public void check( KernelStatement statement )


private void check( KernelTransactionImplementation transaction ) private void check( KernelTransactionImplementation transaction )
{ {
check( getMaxTransactionCompletionTime( transaction ), "Transaction timeout." ); if ( transaction.timeout() > 0 )
{
check( getMaxTransactionCompletionTime( transaction ), "Transaction timeout." );
}
} }


private void check( long maxCompletionTime, String timeoutDescription ) private void check( long maxCompletionTime, String timeoutDescription )
Expand Down
Expand Up @@ -48,7 +48,6 @@
import org.neo4j.kernel.api.security.AuthSubject; import org.neo4j.kernel.api.security.AuthSubject;
import org.neo4j.kernel.builtinprocs.SpecialBuiltInProcedures; import org.neo4j.kernel.builtinprocs.SpecialBuiltInProcedures;
import org.neo4j.kernel.configuration.Config; import org.neo4j.kernel.configuration.Config;
import org.neo4j.kernel.guard.EmptyGuard;
import org.neo4j.kernel.guard.Guard; import org.neo4j.kernel.guard.Guard;
import org.neo4j.kernel.guard.TimeoutGuard; import org.neo4j.kernel.guard.TimeoutGuard;
import org.neo4j.kernel.impl.api.NonTransactionalTokenNameLookup; import org.neo4j.kernel.impl.api.NonTransactionalTokenNameLookup;
Expand Down Expand Up @@ -159,7 +158,7 @@ public DataSourceModule( final GraphDatabaseFacadeFactory.Dependencies dependenc
SchemaWriteGuard schemaWriteGuard = deps.satisfyDependency( editionModule.schemaWriteGuard ); SchemaWriteGuard schemaWriteGuard = deps.satisfyDependency( editionModule.schemaWriteGuard );


Clock clock = getClock(); Clock clock = getClock();
Guard guard = createGuard( deps, config, clock, logging ); Guard guard = createGuard( deps, clock, logging );


kernelEventHandlers = new KernelEventHandlers( logging.getInternalLog( KernelEventHandlers.class ) ); kernelEventHandlers = new KernelEventHandlers( logging.getInternalLog( KernelEventHandlers.class ) );


Expand Down Expand Up @@ -355,11 +354,9 @@ public Relationship newRelationshipProxy( long id, long startNodeId, int typeId,
}; };
} }


private Guard createGuard( Dependencies deps, Config config, Clock clock, LogService logging ) private Guard createGuard( Dependencies deps, Clock clock, LogService logging )
{ {
long configuredTimeout = config.get( GraphDatabaseSettings.transaction_timeout ); TimeoutGuard guard = createTimeoutGuard( clock, logging );
boolean isTimeoutConfigured = configuredTimeout > 0;
Guard guard = isTimeoutConfigured ? createTimeoutGuard( clock, logging ) : EmptyGuard.EMPTY_GUARD;
deps.satisfyDependency( guard ); deps.satisfyDependency( guard );
return guard; return guard;
} }
Expand Down
Expand Up @@ -366,6 +366,12 @@ public Result execute( String query ) throws QueryExecutionException
return execute( query, Collections.emptyMap() ); return execute( query, Collections.emptyMap() );
} }


@Override
public Result execute( String query, long timeout ) throws QueryExecutionException
{
return execute( query, Collections.emptyMap(), timeout );
}

@Override @Override
public Result execute( String query, Map<String,Object> parameters ) throws QueryExecutionException public Result execute( String query, Map<String,Object> parameters ) throws QueryExecutionException
{ {
Expand All @@ -374,6 +380,13 @@ public Result execute( String query, Map<String,Object> parameters ) throws Quer
return execute( transaction, query, parameters ); return execute( transaction, query, parameters );
} }


@Override
public Result execute( String query, Map<String,Object> parameters, long timeout ) throws QueryExecutionException
{
InternalTransaction transaction = beginTransaction( KernelTransaction.Type.implicit, AccessMode.Static.FULL, timeout );
return execute( transaction, query, parameters );
}

// This version of execute is only needed for internal testing of LOAD CSV PERIODIC COMMIT. Can be refactored? // This version of execute is only needed for internal testing of LOAD CSV PERIODIC COMMIT. Can be refactored?
public Result execute( InternalTransaction transaction, String query, Map<String,Object> parameters ) public Result execute( InternalTransaction transaction, String query, Map<String,Object> parameters )
throws QueryExecutionException throws QueryExecutionException
Expand Down
Expand Up @@ -22,12 +22,7 @@
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;


import java.util.Map;

import org.neo4j.graphdb.DependencyResolver; import org.neo4j.graphdb.DependencyResolver;
import org.neo4j.graphdb.config.Setting;
import org.neo4j.graphdb.factory.GraphDatabaseSettings;
import org.neo4j.kernel.guard.EmptyGuard;
import org.neo4j.kernel.guard.Guard; import org.neo4j.kernel.guard.Guard;
import org.neo4j.kernel.guard.TimeoutGuard; import org.neo4j.kernel.guard.TimeoutGuard;
import org.neo4j.kernel.impl.api.GuardingStatementOperations; import org.neo4j.kernel.impl.api.GuardingStatementOperations;
Expand All @@ -37,39 +32,27 @@
import org.neo4j.test.rule.CleanupRule; import org.neo4j.test.rule.CleanupRule;


import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.neo4j.helpers.collection.MapUtil.genericMap;


public class GuardIT public class GuardIT
{ {
@Rule @Rule
public CleanupRule cleanupRule = new CleanupRule(); public CleanupRule cleanupRule = new CleanupRule();


@Test @Test
public void timeoutGuardUsedWhenGuardEnabled() throws Exception public void useTimeoutGuard() throws Exception
{ {
GraphDatabaseAPI database = startDataBase( getEnabledGuardConfigMap() ); GraphDatabaseAPI database = startDataBase();


DependencyResolver dependencyResolver = database.getDependencyResolver(); DependencyResolver dependencyResolver = database.getDependencyResolver();
Guard guard = dependencyResolver.resolveDependency( Guard.class ); Guard guard = dependencyResolver.resolveDependency( Guard.class );
assertThat( guard, instanceOf( TimeoutGuard.class ) ); assertThat( guard, instanceOf( TimeoutGuard.class ) );
} }


@Test @Test
public void emptyGuardUsedWhenGuardDisabled() throws Exception public void includeGuardingOperationLayer() throws Exception
{ {
GraphDatabaseAPI database = startDataBase( getDisabledGuardConfigMap() ); GraphDatabaseAPI database = startDataBase();

DependencyResolver dependencyResolver = database.getDependencyResolver();
Guard guard = dependencyResolver.resolveDependency( Guard.class );
assertThat( guard, instanceOf( EmptyGuard.class ) );
}

@Test
public void includeGuardingOperationLayerWhenGuardEnabled() throws Exception
{
GraphDatabaseAPI database = startDataBase( getEnabledGuardConfigMap() );


DependencyResolver dependencyResolver = database.getDependencyResolver(); DependencyResolver dependencyResolver = database.getDependencyResolver();
StatementOperationParts operationParts = StatementOperationParts operationParts =
Expand All @@ -78,33 +61,11 @@ public void includeGuardingOperationLayerWhenGuardEnabled() throws Exception
assertThat( operationParts.entityWriteOperations(), instanceOf( GuardingStatementOperations.class ) ); assertThat( operationParts.entityWriteOperations(), instanceOf( GuardingStatementOperations.class ) );
} }


@Test private GraphDatabaseAPI startDataBase()
public void noGuardingOperationLayerWhenGuardDisabled() throws Exception
{
GraphDatabaseAPI database = startDataBase( getDisabledGuardConfigMap() );

DependencyResolver dependencyResolver = database.getDependencyResolver();
StatementOperationParts operationParts =
dependencyResolver.resolveDependency( StatementOperationParts.class );
assertThat( operationParts.entityReadOperations(), not( instanceOf( GuardingStatementOperations.class ) ) );
assertThat( operationParts.entityWriteOperations(), not( instanceOf( GuardingStatementOperations.class ) ) );
}

private GraphDatabaseAPI startDataBase( Map<Setting<?>,String> disabledGuardConfigMap )
{ {
GraphDatabaseAPI database = GraphDatabaseAPI database =
(GraphDatabaseAPI) new TestGraphDatabaseFactory().newImpermanentDatabase( disabledGuardConfigMap ); (GraphDatabaseAPI) new TestGraphDatabaseFactory().newImpermanentDatabase();
cleanupRule.add( database ); cleanupRule.add( database );
return database; return database;
} }

private Map<Setting<?>,String> getEnabledGuardConfigMap()
{
return genericMap( GraphDatabaseSettings.transaction_timeout, "60s" );
}

private Map<Setting<?>,String> getDisabledGuardConfigMap()
{
return genericMap( GraphDatabaseSettings.transaction_timeout, "0" );
}
} }
Expand Up @@ -129,12 +129,24 @@ public Result execute( String query ) throws QueryExecutionException
return getGraphDatabaseAPI().execute( query ); return getGraphDatabaseAPI().execute( query );
} }


@Override
public Result execute( String query, long timeout ) throws QueryExecutionException
{
return getGraphDatabaseAPI().execute( query, timeout );
}

@Override @Override
public Result execute( String query, Map<String, Object> parameters ) throws QueryExecutionException public Result execute( String query, Map<String, Object> parameters ) throws QueryExecutionException
{ {
return getGraphDatabaseAPI().execute( query, parameters ); return getGraphDatabaseAPI().execute( query, parameters );
} }


@Override
public Result execute( String query, Map<String,Object> parameters, long timeout ) throws QueryExecutionException
{
return getGraphDatabaseAPI().execute( query, parameters, timeout );
}

@Override @Override
public InternalTransaction beginTransaction( KernelTransaction.Type type, AccessMode accessMode ) public InternalTransaction beginTransaction( KernelTransaction.Type type, AccessMode accessMode )
{ {
Expand Down
Expand Up @@ -24,7 +24,6 @@


import org.neo4j.cypher.internal.javacompat.ExecutionEngine; import org.neo4j.cypher.internal.javacompat.ExecutionEngine;
import org.neo4j.graphdb.DependencyResolver; import org.neo4j.graphdb.DependencyResolver;
import org.neo4j.graphdb.factory.GraphDatabaseSettings;
import org.neo4j.kernel.GraphDatabaseQueryService; import org.neo4j.kernel.GraphDatabaseQueryService;
import org.neo4j.kernel.api.KernelTransaction; import org.neo4j.kernel.api.KernelTransaction;
import org.neo4j.kernel.api.security.AccessMode; import org.neo4j.kernel.api.security.AccessMode;
Expand All @@ -51,14 +50,12 @@ public class CypherExecutor extends LifecycleAdapter
private ThreadToStatementContextBridge txBridge; private ThreadToStatementContextBridge txBridge;


private static final PropertyContainerLocker locker = new PropertyContainerLocker(); private static final PropertyContainerLocker locker = new PropertyContainerLocker();
private final boolean guardEnabled;
private final Log log; private final Log log;


public CypherExecutor( Database database, Config config, LogProvider logProvider ) public CypherExecutor( Database database, Config config, LogProvider logProvider )
{ {
this.database = database; this.database = database;
log = logProvider.getLog( getClass() ); log = logProvider.getLog( getClass() );
guardEnabled = config.get( GraphDatabaseSettings.transaction_timeout ) > 0;
} }


public ExecutionEngine getExecutionEngine() public ExecutionEngine getExecutionEngine()
Expand Down Expand Up @@ -93,15 +90,8 @@ public QuerySession createSession( String query, Map<String, Object> parameters,


private InternalTransaction getInternalTransaction( HttpServletRequest request ) private InternalTransaction getInternalTransaction( HttpServletRequest request )
{ {
if ( guardEnabled ) long customTimeout = getTransactionTimeLimit( request );
{ return customTimeout > 0 ? beginCustomTransaction( customTimeout ) : beginDefaultTransaction();
long customTimeout = getTransactionTimeLimit( request );
if ( customTimeout > 0 )
{
return beginCustomTransaction( customTimeout );
}
}
return beginDefaultTransaction();
} }


private InternalTransaction beginCustomTransaction( long customTimeout ) private InternalTransaction beginCustomTransaction( long customTimeout )
Expand Down
Expand Up @@ -31,6 +31,7 @@
import org.neo4j.graphdb.Label; import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.PropertyContainer; import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.QueryExecutionException;
import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType; import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.ResourceIterable; import org.neo4j.graphdb.ResourceIterable;
Expand Down Expand Up @@ -115,12 +116,24 @@ public Result execute( String query )
return execute( query, Collections.emptyMap() ); return execute( query, Collections.emptyMap() );
} }


@Override
public Result execute( String query, long timeout ) throws QueryExecutionException
{
return execute( query, Collections.emptyMap(), timeout );
}

@Override @Override
public Result execute( String query, Map<String, Object> parameters ) public Result execute( String query, Map<String, Object> parameters )
{ {
return readOnly(); return readOnly();
} }


@Override
public Result execute( String query, Map<String,Object> parameters, long timeout ) throws QueryExecutionException
{
return readOnly();
}

@Override @Override
public Node createNode() public Node createNode()
{ {
Expand Down

0 comments on commit ef9d6d8

Please sign in to comment.