Skip to content

Commit

Permalink
Pass clock as parameter to kernel components.
Browse files Browse the repository at this point in the history
Introduce way to override clock with custom implementation for whole DataSourceModule.
Introduce transaction guard integration tests for api call and for cypher query.
  • Loading branch information
MishaDemianenko committed Aug 19, 2016
1 parent 64e1401 commit 4b4f7ea
Show file tree
Hide file tree
Showing 7 changed files with 218 additions and 30 deletions.
Expand Up @@ -294,6 +294,7 @@ boolean applicable( DiagnosticsPhase phase )
private final ConstraintSemantics constraintSemantics;
private final Procedures procedures;
private final IOLimiter ioLimiter;
private final Clock clock;

private Dependencies dependencies;
private LifeSupport life;
Expand Down Expand Up @@ -342,7 +343,8 @@ public NeoStoreDataSource(
Monitors monitors,
Tracers tracers,
Procedures procedures,
IOLimiter ioLimiter )
IOLimiter ioLimiter,
Clock clock )
{
this.storeDir = storeDir;
this.config = config;
Expand Down Expand Up @@ -374,6 +376,7 @@ public NeoStoreDataSource(
this.tracers = tracers;
this.procedures = procedures;
this.ioLimiter = ioLimiter;
this.clock = clock;

readOnly = config.get( Configuration.read_only );
msgLog = logProvider.getLog( getClass() );
Expand Down Expand Up @@ -474,7 +477,7 @@ public void start() throws IOException
dependencies.resolveDependency( IndexingService.class ),
storageEngine.storeReadLayer(),
updateableSchemaState, dependencies.resolveDependency( LabelScanStore.class ),
storageEngine, indexConfigStore, transactionIdStore );
storageEngine, indexConfigStore, transactionIdStore, clock );

// Do these assignments last so that we can ensure no cyclical dependencies exist
this.storageEngine = storageEngine;
Expand Down Expand Up @@ -662,7 +665,7 @@ public long getTimestampForVersion( long version ) throws IOException

long timeMillisThreshold = config.get( GraphDatabaseSettings.check_point_interval_time );
TimeCheckPointThreshold timeCheckPointThreshold =
new TimeCheckPointThreshold( timeMillisThreshold, Clock.systemUTC() );
new TimeCheckPointThreshold( timeMillisThreshold, clock );

CheckPointThreshold threshold =
CheckPointThresholds.or( countCommittedTransactionThreshold, timeCheckPointThreshold );
Expand Down Expand Up @@ -767,12 +770,12 @@ public void init() throws Throwable
}

private KernelModule buildKernel( TransactionAppender appender,
IndexingService indexingService,
StoreReadLayer storeLayer,
UpdateableSchemaState updateableSchemaState, LabelScanStore labelScanStore,
StorageEngine storageEngine,
IndexConfigStore indexConfigStore,
TransactionIdStore transactionIdStore ) throws KernelException, IOException
IndexingService indexingService,
StoreReadLayer storeLayer,
UpdateableSchemaState updateableSchemaState, LabelScanStore labelScanStore,
StorageEngine storageEngine,
IndexConfigStore indexConfigStore,
TransactionIdStore transactionIdStore, Clock clock ) throws KernelException, IOException
{
TransactionCommitProcess transactionCommitProcess = commitProcessFactory.create( appender, storageEngine,
config );
Expand All @@ -797,7 +800,7 @@ private KernelModule buildKernel( TransactionAppender appender,
KernelTransactions kernelTransactions = life.add( new KernelTransactions( statementLocksFactory,
constraintIndexCreator, statementOperations, schemaWriteGuard, transactionHeaderInformationFactory,
transactionCommitProcess, indexConfigStore, legacyIndexProviderLookup, hooks, transactionMonitor,
life, tracers, storageEngine, procedures, transactionIdStore, config, Clock.systemUTC() ) );
life, tracers, storageEngine, procedures, transactionIdStore, config, clock ) );

final Kernel kernel = new Kernel( kernelTransactions, hooks, databaseHealth, transactionMonitor, procedures,
config );
Expand Down
Expand Up @@ -31,7 +31,7 @@ public class TimeoutGuard implements Guard
private final Log log;
private Clock clock;

public TimeoutGuard( final Log log, Clock clock )
public TimeoutGuard( Clock clock, final Log log )
{
this.log = log;
this.clock = clock;
Expand Down
Expand Up @@ -152,7 +152,8 @@ public DataSourceModule( final GraphDatabaseFacadeFactory.Dependencies dependenc

SchemaWriteGuard schemaWriteGuard = deps.satisfyDependency( editionModule.schemaWriteGuard );

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

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

Expand Down Expand Up @@ -208,7 +209,8 @@ public DataSourceModule( final GraphDatabaseFacadeFactory.Dependencies dependenc
platformModule.monitors,
platformModule.tracers,
procedures,
editionModule.ioLimiter ) );
editionModule.ioLimiter,
clock ) );

dataSourceManager.register( neoStoreDataSource );

Expand Down Expand Up @@ -253,17 +255,9 @@ public void unregistered( NeoStoreDataSource dataSource )
this.kernelAPI = neoStoreDataSource::getKernel;
}

private Guard buildGuard( Dependencies deps, Config config, LogService logging )
protected Clock getClock()
{
Boolean isGuardEnabled = config.get( GraphDatabaseSettings.execution_guard_enabled );
Guard guard = isGuardEnabled ? createTimeoutGuard( logging ) : EmptyGuard.EMPTY_GUARD;
deps.satisfyDependency( guard );
return guard;
}

private TimeoutGuard createTimeoutGuard( LogService logging )
{
return new TimeoutGuard( logging.getInternalLog( TimeoutGuard.class ), Clock.systemUTC() );
return Clock.systemUTC();
}

protected RelationshipProxy.RelationshipActions createRelationshipActions(
Expand Down Expand Up @@ -359,6 +353,19 @@ public Relationship newRelationshipProxy( long id, long startNodeId, int typeId,
};
}

private Guard createGuard( Dependencies deps, Config config, Clock clock, LogService logging )
{
Boolean isGuardEnabled = config.get( GraphDatabaseSettings.execution_guard_enabled );
Guard guard = isGuardEnabled ? createTimeoutGuard( clock, logging ) : EmptyGuard.EMPTY_GUARD;
deps.satisfyDependency( guard );
return guard;
}

private TimeoutGuard createTimeoutGuard( Clock clock, LogService logging )
{
return new TimeoutGuard( clock, logging.getInternalLog( TimeoutGuard.class ) );
}

private Procedures setupProcedures( PlatformModule platform, CoreAPIAvailabilityGuard coreAPIAvailabilityGuard )
{
File pluginDir = platform.config.get( GraphDatabaseSettings.plugin_dir );
Expand Down Expand Up @@ -407,7 +414,7 @@ private static class StartupWaiter extends LifecycleAdapter
private final AvailabilityGuard availabilityGuard;
private final long timeout;

public StartupWaiter( AvailabilityGuard availabilityGuard, long timeout )
StartupWaiter( AvailabilityGuard availabilityGuard, long timeout )
{
this.availabilityGuard = availabilityGuard;
this.timeout = timeout;
Expand Down
Expand Up @@ -132,7 +132,7 @@ private KernelStatement getKernelStatement( long transactionTimeout )
private TimeoutGuard buildGuard( AssertableLogProvider logProvider )
{
Log log = logProvider.getLog( TimeoutGuard.class );
return new TimeoutGuard( log, clock );
return new TimeoutGuard( clock, log );
}

private long getStartTime()
Expand Down
Expand Up @@ -54,7 +54,7 @@ public void timeoutGuardUsedWhenGuardEnabled() throws Exception

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

@Test
Expand All @@ -64,7 +64,7 @@ public void emptyGuardUsedWhenGuardDisabled() throws Exception

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

@Test
Expand Down Expand Up @@ -101,11 +101,11 @@ private GraphDatabaseAPI startDataBase( Map<Setting<?>,String> disabledGuardConf

private Map<Setting<?>,String> getEnabledGuardConfigMap()
{
return genericMap( GraphDatabaseSettings.execution_guard_enabled, Settings.TRUE);
return genericMap( GraphDatabaseSettings.execution_guard_enabled, Settings.TRUE );
}

private Map<Setting<?>,String> getDisabledGuardConfigMap()
{
return genericMap( GraphDatabaseSettings.execution_guard_enabled, Settings.FALSE);
return genericMap( GraphDatabaseSettings.execution_guard_enabled, Settings.FALSE );
}
}
Expand Up @@ -20,6 +20,7 @@
package org.neo4j.test;

import java.io.File;
import java.time.Clock;
import java.util.Map;

import org.neo4j.graphdb.DependencyResolver;
Expand Down Expand Up @@ -115,7 +116,7 @@ fs, mock( TransactionMonitor.class ), databaseHealth,
new StandardConstraintSemantics(), monitors,
new Tracers( "null", NullLog.getInstance(), monitors, jobScheduler ),
mock( Procedures.class ),
IOLimiter.unlimited() );
IOLimiter.unlimited(), Clock.systemUTC() );

return dataSource;
}
Expand Down
@@ -0,0 +1,177 @@
/*
* Copyright (c) 2002-2016 "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;

import org.junit.Rule;
import org.junit.Test;

import java.io.File;
import java.time.Clock;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.config.Setting;
import org.neo4j.graphdb.factory.GraphDatabaseBuilder;
import org.neo4j.graphdb.factory.GraphDatabaseSettings;
import org.neo4j.helpers.collection.MapUtil;
import org.neo4j.kernel.GraphDatabaseDependencies;
import org.neo4j.kernel.configuration.Settings;
import org.neo4j.kernel.guard.GuardTimeoutException;
import org.neo4j.kernel.impl.factory.CommunityFacadeFactory;
import org.neo4j.kernel.impl.factory.DataSourceModule;
import org.neo4j.kernel.impl.factory.EditionModule;
import org.neo4j.kernel.impl.factory.GraphDatabaseFacadeFactory;
import org.neo4j.kernel.impl.factory.PlatformModule;
import org.neo4j.kernel.internal.GraphDatabaseAPI;
import org.neo4j.test.CleanupRule;
import org.neo4j.test.TestGraphDatabaseFactory;
import org.neo4j.test.TestGraphDatabaseFactoryState;
import org.neo4j.time.FakeClock;

import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;

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

@Test
public void terminateLongRunningTransaction()
{
FakeClock clock = new FakeClock();
Map<Setting<?>,String> configMap = MapUtil.genericMap(
GraphDatabaseSettings.execution_guard_enabled, Settings.TRUE,
GraphDatabaseSettings.transaction_timeout, "2s",
GraphDatabaseSettings.statement_timeout, "100s" );
GraphDatabaseAPI database = startCustomDatabase( clock, configMap );
try ( Transaction transaction = database.beginTx() )
{
clock.forward( 3, TimeUnit.SECONDS );
transaction.success();
database.createNode();
fail( "Transaction should be already terminated." );
}
catch ( GuardTimeoutException e )
{
assertThat( e.getMessage(), startsWith( "Transaction timeout." ) );
}

assertDatabaseDoesNotHaveNodes( database );
}

@Test
public void terminateLongRunningQueryTransaction()
{
FakeClock clock = new FakeClock();
Map<Setting<?>,String> configMap = MapUtil.genericMap(
GraphDatabaseSettings.execution_guard_enabled, Settings.TRUE,
GraphDatabaseSettings.transaction_timeout, "2s",
GraphDatabaseSettings.statement_timeout, "100s" );
GraphDatabaseAPI database = startCustomDatabase( clock, configMap );
try ( Transaction transaction = database.beginTx() )
{
clock.forward( 3, TimeUnit.SECONDS );
transaction.success();
database.execute( "create (n)" );
fail( "Transaction should be already terminated." );
}
catch ( GuardTimeoutException e )
{
assertThat( e.getMessage(), startsWith( "Transaction timeout." ) );
}

assertDatabaseDoesNotHaveNodes( database );
}

private void assertDatabaseDoesNotHaveNodes( GraphDatabaseAPI database )
{
try ( Transaction ignored = database.beginTx() )
{
assertEquals( 0, database.getAllNodes().stream().count() );
}
}

private GraphDatabaseAPI startCustomDatabase( Clock clock, Map<Setting<?>,String> configMap )
{
GuardCommunityFacadeFactory guardCommunityFacadeFactory = new GuardCommunityFacadeFactory( clock );
GraphDatabaseAPI database =
(GraphDatabaseAPI) new GuardTestGraphDatabaseFactory( guardCommunityFacadeFactory )
.newImpermanentDatabase( configMap );
cleanupRule.add( database );
return database;
}

private class GuardTestGraphDatabaseFactory extends TestGraphDatabaseFactory
{

private CommunityFacadeFactory customFacadeFactory;

GuardTestGraphDatabaseFactory( CommunityFacadeFactory customFacadeFactory )
{
this.customFacadeFactory = customFacadeFactory;
}

@Override
protected GraphDatabaseBuilder.DatabaseCreator createImpermanentDatabaseCreator( File storeDir,
TestGraphDatabaseFactoryState state )
{
return config -> customFacadeFactory.newFacade( storeDir, config,
GraphDatabaseDependencies.newDependencies( state.databaseDependencies() ) );
}
}

private class GuardCommunityFacadeFactory extends CommunityFacadeFactory
{

private Clock clock;

GuardCommunityFacadeFactory( Clock clock )
{
this.clock = clock;
}

@Override
protected DataSourceModule createDataSource( Dependencies dependencies,
PlatformModule platformModule, EditionModule editionModule )
{
return new GuardDataSourceModule( dependencies, platformModule, editionModule, clock );
}

private class GuardDataSourceModule extends DataSourceModule
{

GuardDataSourceModule( GraphDatabaseFacadeFactory.Dependencies dependencies,
PlatformModule platformModule, EditionModule editionModule, Clock clock )
{
super( dependencies, platformModule, editionModule );
}

@Override
protected Clock getClock()
{
return clock;
}
}
}
}

0 comments on commit 4b4f7ea

Please sign in to comment.