package com.ldbc.driver.runtime; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.ldbc.driver.Db; import com.ldbc.driver.DbException; import com.ldbc.driver.Operation; import com.ldbc.driver.WorkloadException; import com.ldbc.driver.WorkloadStreams; import com.ldbc.driver.control.DriverConfigurationException; import com.ldbc.driver.control.Log4jLoggingServiceFactory; import com.ldbc.driver.control.LoggingService; import com.ldbc.driver.control.LoggingServiceFactory; import com.ldbc.driver.csv.simple.SimpleCsvFileWriter; import com.ldbc.driver.generator.GeneratorFactory; import com.ldbc.driver.generator.RandomDataGeneratorFactory; import com.ldbc.driver.runtime.coordination.CompletionTimeException; import com.ldbc.driver.runtime.coordination.CompletionTimeService; import com.ldbc.driver.runtime.coordination.CompletionTimeServiceAssistant; import com.ldbc.driver.runtime.metrics.MetricsCollectionException; import com.ldbc.driver.runtime.metrics.MetricsService; import com.ldbc.driver.runtime.metrics.ThreadedQueuedMetricsService; import com.ldbc.driver.runtime.scheduling.Spinner; import com.ldbc.driver.temporal.ManualTimeSource; import com.ldbc.driver.temporal.TimeSource; import com.ldbc.driver.workloads.dummy.DummyDb; import com.ldbc.driver.workloads.dummy.DummyWorkload; import com.ldbc.driver.workloads.dummy.TimedNamedOperation1; import com.ldbc.driver.workloads.dummy.TimedNamedOperation2; import com.ldbc.driver.workloads.dummy.TimedNamedOperation2Factory; import com.ldbc.driver.workloads.dummy.TimedNamedOperation3; import org.junit.Test; import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; public class WorkloadRunnerComplexScenarioTests { private static final LoggingServiceFactory LOG4J_LOGGING_SERVICE_FACTORY = new Log4jLoggingServiceFactory( false ); private final long ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING = 300; private final long SPINNER_SLEEP_DURATION_AS_MILLI = 0; private final ManualTimeSource timeSource = new ManualTimeSource( 0 ); private final CompletionTimeServiceAssistant completionTimeServiceAssistant = new CompletionTimeServiceAssistant(); private final GeneratorFactory gf = new GeneratorFactory( new RandomDataGeneratorFactory( 42l ) ); @Test public void oneExecutorShouldNotBeAbleToStarveAnotherOfThreads() throws WorkloadException, CompletionTimeException, DbException, InterruptedException, MetricsCollectionException, IOException { // fails with 1 thread, need to investigate further, probably because there is no available thread to execute // an operation handler in time <-- not necessarily a bug // oneExecutorShouldNotBeAbleToStarveAnotherOfThreads(1); oneExecutorShouldNotBeAbleToStarveAnotherOfThreads( 4 ); oneExecutorShouldNotBeAbleToStarveAnotherOfThreads( 16 ); } public void oneExecutorShouldNotBeAbleToStarveAnotherOfThreads( int threadCount ) throws WorkloadException, CompletionTimeException, DbException, InterruptedException, MetricsCollectionException, IOException { // @formatter:off /* // @formatter:off Number of writers: 1 (blocking) Number of executors: 2 (blocking & async) Initialized to: IT[ , ] CT[0,1] Thread Pool Size: 2 ASYNC THREADS BLOCKING THREADS READ READ_WRITE GCT (assumes initiated time submitted quickly) ACTION COMPLETED TimedNamedOperation1 TimedNamedOperation2 0 [] [] 1 <-- S(4)D(0) initialized 0 1 [] [] 1 0 2 S(2)D(0) [S(2)] [] 1 BLOCK S(2)D(0) 0 3 S(3)D(0) [S(2),S(3)] [] 1 BLOCK S(3)D(0) 0 4 [S(2),S(3)] S(4)D(0) S(4)<-[] 4 <-- S(5)D(0) initiated 1 5 [S(2),S(3)] S(5)D(0) [S(5)] 4 BLOCK S(5)D(0) 1 6 S(3)<-[S(2)] [S(5)] 4 UNBLOCK S(3)D(0) 2 7 S(7)D(0) S(7)<-[S(2)] [S(5)] 4 3 8 [] [S(5)] 4 3 9 [] [S(5)] 4 3 10 [] [S(5)] 4 3 11 [] [S(5)] 4 3 // @formatter:on */ // @formatter:on LoggingService loggingService = new Log4jLoggingServiceFactory( false ).loggingServiceFor( "Test" ); ConcurrentErrorReporter errorReporter = new ConcurrentErrorReporter(); SimpleCsvFileWriter csvResultsLogWriter = null; Map<Integer,Class<? extends Operation>> operationTypeToClassMapping = new HashMap<>(); operationTypeToClassMapping.put( TimedNamedOperation1.TYPE, TimedNamedOperation1.class ); operationTypeToClassMapping.put( TimedNamedOperation2.TYPE, TimedNamedOperation2.class ); operationTypeToClassMapping.put( TimedNamedOperation3.TYPE, TimedNamedOperation3.class ); MetricsService metricsService = ThreadedQueuedMetricsService.newInstanceUsingBlockingBoundedQueue( timeSource, errorReporter, TimeUnit.MILLISECONDS, ThreadedQueuedMetricsService.DEFAULT_HIGHEST_EXPECTED_RUNTIME_DURATION_AS_NANO, csvResultsLogWriter, operationTypeToClassMapping, LOG4J_LOGGING_SERVICE_FACTORY ); Set<String> peerIds = new HashSet<>(); // TODO test also with threaded completion time service implementation CompletionTimeService completionTimeService = completionTimeServiceAssistant.newSynchronizedConcurrentCompletionTimeServiceFromPeerIds( peerIds ); WorkloadStreams workloadStreams = new WorkloadStreams(); Set<Class<? extends Operation>> asynchronousDependentOperationTypes = Sets.<Class<? extends Operation>>newHashSet( TimedNamedOperation1.class ); Set<Class<? extends Operation>> asynchronousDependencyOperationTypes = Sets.newHashSet( // nothing ); Iterator<Operation> asynchronousDependencyOperations = Collections.emptyIterator(); Iterator<Operation> asynchronousNonDependencyOperations = Lists.<Operation>newArrayList( new TimedNamedOperation1( 2, 2, 0, "S(2)D(0)" ), new TimedNamedOperation1( 3, 3, 0, "S(3)D(0)" ), new TimedNamedOperation1( 7, 7, 0, "S(7)D(0)" ) ).iterator(); workloadStreams.setAsynchronousStream( asynchronousDependentOperationTypes, asynchronousDependencyOperationTypes, asynchronousDependencyOperations, asynchronousNonDependencyOperations, null ); Set<Class<? extends Operation>> blockingDependentOperationTypes = Sets.<Class<? extends Operation>>newHashSet( TimedNamedOperation2.class ); Set<Class<? extends Operation>> blockingDependencyOperationTypes = Sets.<Class<? extends Operation>>newHashSet( TimedNamedOperation2.class ); Iterator<Operation> blockingDependencyOperations = Lists.<Operation>newArrayList( new TimedNamedOperation2( 4, 4, 0, "S(4)D(0)" ), new TimedNamedOperation2( 5, 5, 0, "S(5)D(0)" ) ).iterator(); Iterator<Operation> blockingNonDependencyOperations = Lists.<Operation>newArrayList( ).iterator(); workloadStreams.addBlockingStream( blockingDependentOperationTypes, blockingDependencyOperationTypes, blockingDependencyOperations, blockingNonDependencyOperations, null ); Map<String,String> params = new HashMap<>(); params.put( DummyDb.ALLOWED_DEFAULT_ARG, "true" ); try ( DummyDb db = new DummyDb() ) { db.init( params, loggingService, DummyWorkload.OPERATION_TYPE_CLASS_MAPPING ); db.setNameAllowedValue( "S(2)D(0)", false ); db.setNameAllowedValue( "S(3)D(0)", false ); db.setNameAllowedValue( "S(4)D(0)", true ); db.setNameAllowedValue( "S(5)D(0)", false ); db.setNameAllowedValue( "S(7)D(0)", true ); WorkloadRunnerThread runnerThread = workloadRunnerThread( timeSource, workloadStreams, threadCount, errorReporter, metricsService, completionTimeService, db ); MetricsService.MetricsServiceWriter metricsServiceWriter = metricsService.getWriter(); // initialize GCT completionTimeServiceAssistant.writeInitiatedAndCompletedTimesToAllWriters( completionTimeService, 0 ); completionTimeServiceAssistant.writeInitiatedAndCompletedTimesToAllWriters( completionTimeService, 1 ); timeSource.setNowFromMilli( 0 ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), is( 0l ) ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); runnerThread.start(); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 1l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 1 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 1l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 2 ); // S(2)D(0) is blocked, nothing will change Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 1l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 3 ); // S(3)D(0) is blocked, nothing will change Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 1l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 4 ); // check that S(4)D(0) is able to complete (is not starved of thread) Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 1l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 4l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 5 ); // S(5)D(0) is blocked, nothing will change Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 1l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 4l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 6 ); db.setNameAllowedValue( "S(3)D(0)", true ); // S(3)D(0) is unblocked -> S(3)D(0) finishes Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 2l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 4l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 7 ); // check that S(7)D(0) is able to complete (is not starved of thread) Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 3l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 4l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 8 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 3l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 4l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); // allow S(2)D(0) & S(5)D(0) to complete, so workload runner can terminate db.setNameAllowedValue( "S(2)D(0)", true ); db.setNameAllowedValue( "S(5)D(0)", true ); long durationToWaitForRunnerToCompleteAsMilli = WorkloadRunner.RUNNER_POLLING_INTERVAL_AS_MILLI * 4; long timeoutTimeAsMilli = timeSource.nowAsMilli() + durationToWaitForRunnerToCompleteAsMilli; while ( timeSource.nowAsMilli() < timeoutTimeAsMilli ) { if ( runnerThread.runnerHasCompleted() ) { break; } Spinner.powerNap( 100 ); } db.setAllowedValueForAll( true ); assertThat( errorReporter.toString(), runnerThread.runnerHasCompleted(), is( true ) ); assertThat( errorReporter.toString(), runnerThread.runnerCompletedSuccessfully(), is( true ) ); } catch ( Throwable e ) { e.printStackTrace(); throw e; } finally { Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); metricsService.shutdown(); completionTimeService.shutdown(); } } @Test public void oneExecutorShouldNotBeCapableOfAdvancingInitiatedTimeOfAnotherExecutor() throws CompletionTimeException, InterruptedException, MetricsCollectionException, DbException, WorkloadException, IOException { oneExecutorShouldNotBeCapableOfAdvancingInitiatedTimeOfAnotherExecutor( 1 ); oneExecutorShouldNotBeCapableOfAdvancingInitiatedTimeOfAnotherExecutor( 4 ); oneExecutorShouldNotBeCapableOfAdvancingInitiatedTimeOfAnotherExecutor( 16 ); } public void oneExecutorShouldNotBeCapableOfAdvancingInitiatedTimeOfAnotherExecutor( int threadCount ) throws CompletionTimeException, InterruptedException, MetricsCollectionException, DbException, WorkloadException, IOException { // @formatter:off /* Number of writers: 2 (blocking & async) Number of executors: 2 (blocking & async) Initialized to: IT[ , ] CT[0,1] ASYNC BLOCKING READ_WRITE READ_WRITE GCT (assumes initiated time submitted quickly) ACTION TimedNamedOperation1 TimedNamedOperation2 0 1 <-- S(2)D(0) initialized <--S (3)D(0) initialized 1 1 2 S(2)D(0) 2 <-- S(5)D(0) initialized 3 S(3)D(0) 3 <-- S(4)D(0) initialized 4 S(4)D(0) x 10,000,000 3 <-- S(4)D(0) initialized x 9,999,999 5 S(5)D(0) 3 !!BLOCK S(5)D(0)!! <*> NOTE - <*> must block, otherwise S(5)D(0) may (race condition) complete before first S(4)D(0) initiated time is submitted, and GCT would advance - time should be advanced directly from 3 to 5 - Async & Blocking executors will both try to submit initiated times for all due operations - the aim is for Async to manage to submit its 1 operation before the last Blocking time is submitted - the goal is to force initiated time 5 to be submitted before at least one initiated time 4 is submitted <-- illegal operation - this is only illegal if both executors were using the same local completion time writer, this test makes sure they are not */ // @formatter:on LoggingService loggingService = new Log4jLoggingServiceFactory( false ).loggingServiceFor( "Test" ); ConcurrentErrorReporter errorReporter = new ConcurrentErrorReporter(); SimpleCsvFileWriter csvResultsLogWriter = null; Map<Integer,Class<? extends Operation>> operationTypeToClassMapping = new HashMap<>(); operationTypeToClassMapping.put( TimedNamedOperation1.TYPE, TimedNamedOperation1.class ); operationTypeToClassMapping.put( TimedNamedOperation2.TYPE, TimedNamedOperation2.class ); operationTypeToClassMapping.put( TimedNamedOperation3.TYPE, TimedNamedOperation3.class ); MetricsService metricsService = ThreadedQueuedMetricsService.newInstanceUsingBlockingBoundedQueue( timeSource, errorReporter, TimeUnit.MILLISECONDS, ThreadedQueuedMetricsService.DEFAULT_HIGHEST_EXPECTED_RUNTIME_DURATION_AS_NANO, csvResultsLogWriter, operationTypeToClassMapping, LOG4J_LOGGING_SERVICE_FACTORY ); Set<String> peerIds = new HashSet<>(); // TODO test also with threaded completion time service implementation CompletionTimeService completionTimeService = completionTimeServiceAssistant.newSynchronizedConcurrentCompletionTimeServiceFromPeerIds( peerIds ); WorkloadStreams workloadStreams = new WorkloadStreams(); Set<Class<? extends Operation>> asynchronousDependentOperationTypes = Sets.<Class<? extends Operation>>newHashSet( TimedNamedOperation1.class ); Set<Class<? extends Operation>> asynchronousDependencyOperationTypes = Sets.<Class<? extends Operation>>newHashSet( TimedNamedOperation1.class ); Iterator<Operation> asynchronousDependencyOperations = Lists.<Operation>newArrayList( new TimedNamedOperation1( 2, 2, 0, "read1" ), new TimedNamedOperation1( 5, 5, 0, "read2" ) ).iterator(); Iterator<Operation> asynchronousNonDependencyOperations = Lists.<Operation>newArrayList( ).iterator(); workloadStreams.setAsynchronousStream( asynchronousDependentOperationTypes, asynchronousDependencyOperationTypes, asynchronousDependencyOperations, asynchronousNonDependencyOperations, null ); Set<Class<? extends Operation>> blockingDependentOperationTypes = Sets.<Class<? extends Operation>>newHashSet( TimedNamedOperation2.class ); Set<Class<? extends Operation>> blockingDependencyOperationTypes = Sets.<Class<? extends Operation>>newHashSet( TimedNamedOperation2.class ); List<Operation> blockingDependencyOperationsList = Lists.<Operation>newArrayList( new TimedNamedOperation2( 3, 3, 0, "readwrite1" ) ); int operationCountAtTime4 = 1000000; Iterator<Operation> manyReadWriteOperationsAtTime4 = gf.limit( new TimedNamedOperation2Factory( gf.constant( 4l ), gf.constant( 0l ), gf.constant( "oneOfManyReadWrite2" ) ), operationCountAtTime4 ); blockingDependencyOperationsList.addAll( Lists.newArrayList( manyReadWriteOperationsAtTime4 ) ); Iterator<Operation> blockingDependencyOperations = blockingDependencyOperationsList.iterator(); Iterator<Operation> blockingNonDependencyOperations = Lists.<Operation>newArrayList( ).iterator(); workloadStreams.addBlockingStream( blockingDependentOperationTypes, blockingDependencyOperationTypes, blockingDependencyOperations, blockingNonDependencyOperations, null ); Map<String,String> params = new HashMap<>(); params.put( DummyDb.ALLOWED_DEFAULT_ARG, "true" ); try ( DummyDb db = new DummyDb() ) { db.init( params, loggingService, DummyWorkload.OPERATION_TYPE_CLASS_MAPPING ); WorkloadRunnerThread runnerThread = workloadRunnerThread( timeSource, workloadStreams, threadCount, errorReporter, metricsService, completionTimeService, db ); MetricsService.MetricsServiceWriter metricsServiceWriter = metricsService.getWriter(); // initialize GCT completionTimeServiceAssistant.writeInitiatedAndCompletedTimesToAllWriters( completionTimeService, 0 ); completionTimeServiceAssistant.writeInitiatedAndCompletedTimesToAllWriters( completionTimeService, 1 ); timeSource.setNowFromMilli( 0 ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), is( 0l ) ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); runnerThread.start(); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 1l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 1 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 1l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); // read1 can execute timeSource.setNowFromMilli( 2 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 1l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 2l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); // readwrite1 can execute timeSource.setNowFromMilli( 3 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 2l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 3l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); // at this point read2 and all readWrite2 can execute <-- read2 must be blocked for test to do what is // intended db.setNameAllowedValue( "read2", false ); timeSource.setNowFromMilli( 5 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), is( 3l ) ); // if initiated time 4 was submitted after initiated time 5 an error should have been reported (hopefully // it was not) assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); // allow read2 to complete, so workload runner can terminate db.setNameAllowedValue( "read2", true ); long durationToWaitForRunnerToCompleteAsMilli = WorkloadRunner.RUNNER_POLLING_INTERVAL_AS_MILLI * 4; long timeoutTimeAsMilli = timeSource.nowAsMilli() + durationToWaitForRunnerToCompleteAsMilli; while ( timeSource.nowAsMilli() < timeoutTimeAsMilli ) { if ( runnerThread.runnerHasCompleted() ) { break; } Spinner.powerNap( 100 ); } db.setAllowedValueForAll( true ); assertThat( errorReporter.toString(), runnerThread.runnerHasCompleted(), is( true ) ); assertThat( errorReporter.toString(), runnerThread.runnerCompletedSuccessfully(), is( true ) ); } catch ( Throwable e ) { e.printStackTrace(); throw e; } finally { Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); metricsService.shutdown(); completionTimeService.shutdown(); } } @Test public void shouldSuccessfullyCompleteWhenAllOperationsFinishOnTimeWithReadAsyncReadWriteAsync() throws DriverConfigurationException, DbException, CompletionTimeException, WorkloadException, InterruptedException, MetricsCollectionException, IOException { shouldSuccessfullyCompleteWhenAllOperationsFinishOnTimeWithReadAsyncReadWriteAsync( 1 ); shouldSuccessfullyCompleteWhenAllOperationsFinishOnTimeWithReadAsyncReadWriteAsync( 4 ); shouldSuccessfullyCompleteWhenAllOperationsFinishOnTimeWithReadAsyncReadWriteAsync( 16 ); } public void shouldSuccessfullyCompleteWhenAllOperationsFinishOnTimeWithReadAsyncReadWriteAsync( int threadCount ) throws DriverConfigurationException, DbException, CompletionTimeException, WorkloadException, InterruptedException, MetricsCollectionException, IOException { // @formatter:off /* Number of writers: 1 (async) Number of executors: 2 (blocking & async) Initialized to: IT[ , ] CT[0,1] ASYNC ASYNC READ READ_WRITE TimedNamedOperation1 TimedNamedOperation2 GCT (assumes initiated time submitted quickly) 0 1 <~~ S(2)D(0) initialized (READ ONLY) 1 1 2 S(2)D(0) 1 <-- S(3)D(0) initialized 3 S(3)D(0) 3 <~~ S(4)D(0) initialized (READ ONLY) 4 S(4)D(0) 3 <-- S(6)D(0) initialized 5 3 <~~ S(4)D(0) initialized (READ ONLY) 6 S(6)D(0) 6 <~~ S(7)D(3) initialized (READ ONLY) 7 S(7)D(3) 6 <-- S(9)D(3) initialized 8 6 9 S(9)D(3) 9 // executor knows this is last WRITE 10 9 11 S(11)D(0) 9 <~~ S(11)D(0) initialized (READ ONLY) 12 9 13 S(13)D(6) 9 <~~ S(13)D(6) initialized (READ ONLY) */ // @formatter:on LoggingService loggingService = new Log4jLoggingServiceFactory( false ).loggingServiceFor( "Test" ); ConcurrentErrorReporter errorReporter = new ConcurrentErrorReporter(); SimpleCsvFileWriter csvResultsLogWriter = null; Map<Integer,Class<? extends Operation>> operationTypeToClassMapping = new HashMap<>(); operationTypeToClassMapping.put( TimedNamedOperation1.TYPE, TimedNamedOperation1.class ); operationTypeToClassMapping.put( TimedNamedOperation2.TYPE, TimedNamedOperation2.class ); operationTypeToClassMapping.put( TimedNamedOperation3.TYPE, TimedNamedOperation3.class ); MetricsService metricsService = ThreadedQueuedMetricsService.newInstanceUsingBlockingBoundedQueue( timeSource, errorReporter, TimeUnit.MILLISECONDS, ThreadedQueuedMetricsService.DEFAULT_HIGHEST_EXPECTED_RUNTIME_DURATION_AS_NANO, csvResultsLogWriter, operationTypeToClassMapping, LOG4J_LOGGING_SERVICE_FACTORY ); Set<String> peerIds = new HashSet<>(); // TODO test also with threaded completion time service implementation CompletionTimeService completionTimeService = completionTimeServiceAssistant.newSynchronizedConcurrentCompletionTimeServiceFromPeerIds( peerIds ); WorkloadStreams workloadStreams = new WorkloadStreams(); Set<Class<? extends Operation>> asynchronousDependentOperationTypes = Sets.<Class<? extends Operation>>newHashSet( TimedNamedOperation1.class, TimedNamedOperation2.class ); Set<Class<? extends Operation>> asynchronousDependencyOperationTypes = Sets.<Class<? extends Operation>>newHashSet( TimedNamedOperation2.class ); Iterator<Operation> asynchronousDependencyOperations = Lists.<Operation>newArrayList( new TimedNamedOperation2( 3, 3, 0, "readwrite1" ), new TimedNamedOperation2( 6, 6, 0, "readwrite2" ), new TimedNamedOperation2( 9, 9, 3, "readwrite3" ) ).iterator(); Iterator<Operation> asynchronousNonDependencyOperations = Lists.<Operation>newArrayList( new TimedNamedOperation1( 2, 2, 0, "read1" ), new TimedNamedOperation1( 4, 4, 0, "read2" ), new TimedNamedOperation1( 7, 7, 3, "read3" ), new TimedNamedOperation1( 11, 11, 0, "read4" ), new TimedNamedOperation1( 13, 13, 6, "read5" ) ).iterator(); workloadStreams.setAsynchronousStream( asynchronousDependentOperationTypes, asynchronousDependencyOperationTypes, asynchronousDependencyOperations, asynchronousNonDependencyOperations, null ); Set<Class<? extends Operation>> blockingDependentOperationTypes = Sets.newHashSet( // nothing ); Set<Class<? extends Operation>> blockingDependencyOperationTypes = Sets.newHashSet( // nothing ); Iterator<Operation> blockingDependencyOperations = Lists.<Operation>newArrayList( // nothing ).iterator(); Iterator<Operation> blockingNonDependencyOperations = Lists.<Operation>newArrayList( // nothing ).iterator(); workloadStreams.addBlockingStream( blockingDependentOperationTypes, blockingDependencyOperationTypes, blockingDependencyOperations, blockingNonDependencyOperations, null ); Map<String,String> params = new HashMap<>(); params.put( DummyDb.ALLOWED_DEFAULT_ARG, "false" ); try ( DummyDb db = new DummyDb() ) { db.init( params, loggingService, DummyWorkload.OPERATION_TYPE_CLASS_MAPPING ); WorkloadRunnerThread runnerThread = workloadRunnerThread( timeSource, workloadStreams, threadCount, errorReporter, metricsService, completionTimeService, db ); MetricsService.MetricsServiceWriter metricsServiceWriter = metricsService.getWriter(); // initialize GCT completionTimeServiceAssistant.writeInitiatedAndCompletedTimesToAllWriters( completionTimeService, 0 ); completionTimeServiceAssistant.writeInitiatedAndCompletedTimesToAllWriters( completionTimeService, 1 ); timeSource.setNowFromMilli( 0 ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), is( 0l ) ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); runnerThread.start(); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), is( 1l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 1 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), is( 1l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 2 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), is( 1l ) ); db.setNameAllowedValue( "read1", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 1l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), is( 1l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 3 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 1l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), is( 1l ) ); db.setNameAllowedValue( "readwrite1", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 2l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 3l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 4 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 2l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 3l ) ); db.setNameAllowedValue( "read2", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 3l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 3l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 5 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 3l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 3l ) ); timeSource.setNowFromMilli( 6 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 3l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 3l ) ); db.setNameAllowedValue( "readwrite2", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 4l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 6l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 7 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 4l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 6l ) ); db.setNameAllowedValue( "read3", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 5l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 6l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 8 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 5l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 6l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 9 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 5l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 6l ) ); db.setNameAllowedValue( "readwrite3", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 6l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 10 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 6l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 11 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 6l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); db.setNameAllowedValue( "read4", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 7l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 12 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 7l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 13 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 7l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); db.setNameAllowedValue( "read5", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 8l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); long durationToWaitForRunnerToCompleteAsMilli = WorkloadRunner.RUNNER_POLLING_INTERVAL_AS_MILLI * 4; long timeoutTimeAsMilli = timeSource.nowAsMilli() + durationToWaitForRunnerToCompleteAsMilli; while ( timeSource.nowAsMilli() < timeoutTimeAsMilli ) { if ( runnerThread.runnerHasCompleted() ) { break; } Spinner.powerNap( 100 ); } db.setAllowedValueForAll( true ); assertThat( errorReporter.toString(), runnerThread.runnerHasCompleted(), is( true ) ); assertThat( errorReporter.toString(), runnerThread.runnerCompletedSuccessfully(), is( true ) ); } catch ( Throwable e ) { e.printStackTrace(); throw e; } finally { Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); metricsService.shutdown(); completionTimeService.shutdown(); } } @Test public void shouldSuccessfullyCompleteWhenAllOperationsFinishOnTimeWithReadAsyncReadWriteBlocking() throws DriverConfigurationException, DbException, CompletionTimeException, WorkloadException, InterruptedException, MetricsCollectionException, IOException { shouldSuccessfullyCompleteWhenAllOperationsFinishOnTimeWithReadAsyncReadWriteBlocking( 1 ); shouldSuccessfullyCompleteWhenAllOperationsFinishOnTimeWithReadAsyncReadWriteBlocking( 4 ); shouldSuccessfullyCompleteWhenAllOperationsFinishOnTimeWithReadAsyncReadWriteBlocking( 16 ); } public void shouldSuccessfullyCompleteWhenAllOperationsFinishOnTimeWithReadAsyncReadWriteBlocking( int threadCount ) throws DriverConfigurationException, DbException, CompletionTimeException, WorkloadException, InterruptedException, MetricsCollectionException, IOException { // @formatter:off /* Number of writers: 1 (blocking) Number of executors: 2 (blocking & async) Initialized to: IT[ , ] CT[0,1] ASYNC BLOCKING READ READ_WRITE TimedNamedOperation1 TimedNamedOperation2 GCT (assumes initiated time submitted quickly) 0 1 <-- S(3)D(0) initialized <~~ S(2)D(0) initialized (READ ONLY) 1 1 2 S(2)D(0) 1 <~~ S(4)D(0) initialized (READ ONLY) 3 S(3)D(0) 3 <-- S(6)D(0) initialized 4 S(4)D(0) 3 <~~ S(7)D(3) initialized (READ ONLY) 5 3 6 S(6)D(0) 6 <-- S(9)D(3) initialized 7 S(7)D(3) 6 <~~ S(11)D(0) initialized (READ ONLY) 8 6 9 S(9)D(3) 6 10 6 11 S(11)D(0) 6 <~~ S(13)D(6) initialized (READ ONLY) 12 6 13 S(13)D(6) 6 */ // @formatter:on LoggingService loggingService = new Log4jLoggingServiceFactory( false ).loggingServiceFor( "Test" ); ConcurrentErrorReporter errorReporter = new ConcurrentErrorReporter(); SimpleCsvFileWriter csvResultsLogWriter = null; Map<Integer,Class<? extends Operation>> operationTypeToClassMapping = new HashMap<>(); operationTypeToClassMapping.put( TimedNamedOperation1.TYPE, TimedNamedOperation1.class ); operationTypeToClassMapping.put( TimedNamedOperation2.TYPE, TimedNamedOperation2.class ); operationTypeToClassMapping.put( TimedNamedOperation3.TYPE, TimedNamedOperation3.class ); MetricsService metricsService = ThreadedQueuedMetricsService.newInstanceUsingBlockingBoundedQueue( timeSource, errorReporter, TimeUnit.MILLISECONDS, ThreadedQueuedMetricsService.DEFAULT_HIGHEST_EXPECTED_RUNTIME_DURATION_AS_NANO, csvResultsLogWriter, operationTypeToClassMapping, LOG4J_LOGGING_SERVICE_FACTORY ); Set<String> peerIds = new HashSet<>(); // TODO test also with threaded completion time service implementation CompletionTimeService completionTimeService = completionTimeServiceAssistant.newSynchronizedConcurrentCompletionTimeServiceFromPeerIds( peerIds ); WorkloadStreams workloadStreams = new WorkloadStreams(); Set<Class<? extends Operation>> asynchronousDependentOperationTypes = Sets.<Class<? extends Operation>>newHashSet( TimedNamedOperation1.class ); Set<Class<? extends Operation>> asynchronousDependencyOperationTypes = Sets.<Class<? extends Operation>>newHashSet( ); Iterator<Operation> asynchronousDependencyOperations = Lists.<Operation>newArrayList( // nothing ).iterator(); Iterator<Operation> asynchronousNonDependencyOperations = Lists.<Operation>newArrayList( new TimedNamedOperation1( 2, 2, 0, "read1" ), new TimedNamedOperation1( 4, 4, 0, "read2" ), new TimedNamedOperation1( 7, 7, 3, "read3" ), new TimedNamedOperation1( 11, 11, 0, "read4" ), new TimedNamedOperation1( 13, 13, 6, "read5" ) ).iterator(); workloadStreams.setAsynchronousStream( asynchronousDependentOperationTypes, asynchronousDependencyOperationTypes, asynchronousDependencyOperations, asynchronousNonDependencyOperations, null ); Set<Class<? extends Operation>> blockingDependentOperationTypes = Sets.<Class<? extends Operation>>newHashSet( TimedNamedOperation2.class ); Set<Class<? extends Operation>> blockingDependencyOperationTypes = Sets.<Class<? extends Operation>>newHashSet( TimedNamedOperation2.class ); Iterator<Operation> blockingDependencyOperations = Lists.<Operation>newArrayList( new TimedNamedOperation2( 3, 3, 0, "readwrite1" ), new TimedNamedOperation2( 6, 6, 0, "readwrite2" ), new TimedNamedOperation2( 9, 9, 3, "readwrite3" ) ).iterator(); Iterator<Operation> blockingNonDependencyOperations = Lists.<Operation>newArrayList( // nothing ).iterator(); workloadStreams.addBlockingStream( blockingDependentOperationTypes, blockingDependencyOperationTypes, blockingDependencyOperations, blockingNonDependencyOperations, null ); Map<String,String> params = new HashMap<>(); params.put( DummyDb.ALLOWED_DEFAULT_ARG, "false" ); try ( DummyDb db = new DummyDb() ) { db.init( params, loggingService, DummyWorkload.OPERATION_TYPE_CLASS_MAPPING ); WorkloadRunnerThread runnerThread = workloadRunnerThread( timeSource, workloadStreams, threadCount, errorReporter, metricsService, completionTimeService, db ); MetricsService.MetricsServiceWriter metricsServiceWriter = metricsService.getWriter(); // initialize GCT completionTimeServiceAssistant.writeInitiatedAndCompletedTimesToAllWriters( completionTimeService, 0 ); completionTimeServiceAssistant.writeInitiatedAndCompletedTimesToAllWriters( completionTimeService, 1 ); timeSource.setNowFromMilli( 0 ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), is( 0l ) ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); runnerThread.start(); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); // GCT may be 0 or 1 at this stage, depending on the OperationHandlerExecutor used // anyOf because it depends on whether "readwrite1"/S(3)D(0) has been initialized yet, or not // SameThreadOperationHandlerExecutor will be 0, as it must wait for previous operation to complete // before it can initiate the next operation // SingleThread/ThreadPoolOperationHandlerExecutor will be 1, as it can initiate the next operation as // soon as it has submitted the previous one for execution assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( is( 0l ), is( 1l ) ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 1 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( is( 0l ), is( 1l ) ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 2 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( is( 0l ), is( 1l ) ) ); db.setNameAllowedValue( "read1", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 1l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( is( 0l ), is( 1l ) ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 3 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 1l ) ); // anyOf because it depends on whether "readwrite2"/S(6)D(0) has been initialized yet, or not assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( is( 1l ), is( 3l ) ) ); db.setNameAllowedValue( "readwrite1", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 2l ) ); // anyOf because it depends on whether "readwrite2"/S(6)D(0) has been initialized yet, or not assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( is( 1l ), is( 3l ) ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 4 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 2l ) ); // anyOf because it depends on whether "readwrite2"/S(6)D(0) has been initialized yet, or not assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( is( 1l ), is( 3l ) ) ); db.setNameAllowedValue( "read2", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 3l ) ); // anyOf because it depends on whether "readwrite2"/S(6)D(0) has been initialized yet, or not assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( is( 1l ), is( 3l ) ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 5 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 3l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); // anyOf because it depends on whether "readwrite2"/S(6)D(0) has been initialized yet, or not assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( is( 1l ), is( 3l ) ) ); timeSource.setNowFromMilli( 6 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 3l ) ); // anyOf because it depends on whether "readwrite3"/S(9)D(0) has been initialized yet, or not assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( is( 3l ), is( 6l ) ) ); db.setNameAllowedValue( "readwrite2", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 4l ) ); // anyOf because it depends on whether "readwrite3"/S(9)D(0) has been initialized yet, or not assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( is( 3l ), is( 6l ) ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 7 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 4l ) ); // anyOf because it depends on whether "readwrite3"/S(9)D(0) has been initialized yet, or not assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( is( 3l ), is( 6l ) ) ); db.setNameAllowedValue( "read3", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 5l ) ); // anyOf because it depends on whether "readwrite3"/S(9)D(0) has been initialized yet, or not assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( is( 3l ), is( 6l ) ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 8 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 5l ) ); // anyOf because it depends on whether "readwrite3"/S(9)D(0) has been initialized yet, or not assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( is( 3l ), is( 6l ) ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 9 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 5l ) ); // anyOf because it depends on whether "readwrite3"/S(9)D(0) has been initialized yet, or not assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( is( 6l ), is( 9l ) ) ); db.setNameAllowedValue( "readwrite3", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 6l ) ); // should advance to 9, because this is the last GCT writing operation in the stream assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 10 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 6l ) ); // should advance to 9, because this is the last GCT writing operation in the stream assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 11 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 6l ) ); // should advance to 9, because this is the last GCT writing operation in the stream assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); db.setNameAllowedValue( "read4", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 7l ) ); // should advance to 9, because this is the last GCT writing operation in the stream assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 12 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 7l ) ); // should advance to 9, because this is the last GCT writing operation in the stream assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 13 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 7l ) ); // should advance to 9, because this is the last GCT writing operation in the stream assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); db.setNameAllowedValue( "read5", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 8l ) ); // should advance to 9, because this is the last GCT writing operation in the stream assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); long durationToWaitForRunnerToCompleteAsMilli = WorkloadRunner.RUNNER_POLLING_INTERVAL_AS_MILLI * 4; long timeoutTimeAsMilli = timeSource.nowAsMilli() + durationToWaitForRunnerToCompleteAsMilli; while ( timeSource.nowAsMilli() < timeoutTimeAsMilli ) { if ( runnerThread.runnerHasCompleted() ) { break; } Spinner.powerNap( 100 ); } db.setAllowedValueForAll( true ); assertThat( errorReporter.toString(), runnerThread.runnerHasCompleted(), is( true ) ); assertThat( errorReporter.toString(), runnerThread.runnerCompletedSuccessfully(), is( true ) ); } catch ( Throwable e ) { e.printStackTrace(); throw e; } finally { Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); metricsService.shutdown(); completionTimeService.shutdown(); } } @Test public void shouldSuccessfullyCompleteWhenAllOperationsFinishOnTimeWithReadBlockingReadWriteAsync() throws DriverConfigurationException, DbException, CompletionTimeException, WorkloadException, InterruptedException, MetricsCollectionException, IOException { shouldSuccessfullyCompleteWhenAllOperationsFinishOnTimeWithReadBlockingReadWriteAsync( 1 ); shouldSuccessfullyCompleteWhenAllOperationsFinishOnTimeWithReadBlockingReadWriteAsync( 4 ); shouldSuccessfullyCompleteWhenAllOperationsFinishOnTimeWithReadBlockingReadWriteAsync( 16 ); } public void shouldSuccessfullyCompleteWhenAllOperationsFinishOnTimeWithReadBlockingReadWriteAsync( int threadCount ) throws DriverConfigurationException, DbException, CompletionTimeException, WorkloadException, InterruptedException, MetricsCollectionException, IOException { // @formatter:off /* Number of writers: 1 (async) Number of executors: 2 (blocking & async) Initialized to: IT[ , ] CT[0,1] BLOCKING ASYNC READ READ_WRITE TimedNamedOperation1 TimedNamedOperation2 GCT (assumes initiated time submitted quickly) 0 1 <-- S(3)D(0) initialized <~~ S(2)D(0) initialized (READ ONLY) 1 1 2 S(2)D(0) 1 <~~ S(4)D(0) initialized (READ ONLY) 3 S(3)D(0) 3 <-- S(6)D(0) initialized 4 S(4)D(0) 3 <~~ S(7)D(3) initialized (READ ONLY) 5 3 6 S(6)D(0) 6 <-- S(9)D(3) initialized 7 S(7)D(3) 6 <~~ S(11)D(0) initialized (READ ONLY) 8 6 9 S(9)D(3) 9 // Executor knows this is the last WRITE 10 9 11 S(11)D(0) 9 <~~ S(13)D(6) initialized (READ ONLY) 12 9 13 S(13)D(6) 9 */ // @formatter:on LoggingService loggingService = new Log4jLoggingServiceFactory( false ).loggingServiceFor( "Test" ); ConcurrentErrorReporter errorReporter = new ConcurrentErrorReporter(); SimpleCsvFileWriter csvResultsLogWriter = null; Map<Integer,Class<? extends Operation>> operationTypeToClassMapping = new HashMap<>(); operationTypeToClassMapping.put( TimedNamedOperation1.TYPE, TimedNamedOperation1.class ); operationTypeToClassMapping.put( TimedNamedOperation2.TYPE, TimedNamedOperation2.class ); operationTypeToClassMapping.put( TimedNamedOperation3.TYPE, TimedNamedOperation3.class ); MetricsService metricsService = ThreadedQueuedMetricsService.newInstanceUsingBlockingBoundedQueue( timeSource, errorReporter, TimeUnit.MILLISECONDS, ThreadedQueuedMetricsService.DEFAULT_HIGHEST_EXPECTED_RUNTIME_DURATION_AS_NANO, csvResultsLogWriter, operationTypeToClassMapping, LOG4J_LOGGING_SERVICE_FACTORY ); Set<String> peerIds = new HashSet<>(); // TODO test also with threaded completion time service implementation CompletionTimeService completionTimeService = completionTimeServiceAssistant.newSynchronizedConcurrentCompletionTimeServiceFromPeerIds( peerIds ); WorkloadStreams workloadStreams = new WorkloadStreams(); Set<Class<? extends Operation>> asynchronousDependentOperationTypes = Sets.<Class<? extends Operation>>newHashSet( TimedNamedOperation2.class ); Set<Class<? extends Operation>> asynchronousDependencyOperationTypes = Sets.<Class<? extends Operation>>newHashSet( TimedNamedOperation2.class ); Iterator<Operation> asynchronousDependencyOperations = Lists.<Operation>newArrayList( new TimedNamedOperation2( 3, 3, 0, "readwrite1" ), new TimedNamedOperation2( 6, 6, 0, "readwrite2" ), new TimedNamedOperation2( 9, 9, 3, "readwrite3" ) ).iterator(); Iterator<Operation> asynchronousNonDependencyOperations = Lists.<Operation>newArrayList( // nothing ).iterator(); workloadStreams.setAsynchronousStream( asynchronousDependentOperationTypes, asynchronousDependencyOperationTypes, asynchronousDependencyOperations, asynchronousNonDependencyOperations, null ); Set<Class<? extends Operation>> blockingDependentOperationTypes = Sets.<Class<? extends Operation>>newHashSet( TimedNamedOperation1.class ); Set<Class<? extends Operation>> blockingDependencyOperationTypes = Sets.<Class<? extends Operation>>newHashSet( ); Iterator<Operation> blockingDependencyOperations = Lists.<Operation>newArrayList( // nothing ).iterator(); Iterator<Operation> blockingNonDependencyOperations = Lists.<Operation>newArrayList( new TimedNamedOperation1( 2, 2, 0, "read1" ), new TimedNamedOperation1( 4, 4, 0, "read2" ), new TimedNamedOperation1( 7, 7, 3, "read3" ), new TimedNamedOperation1( 11, 11, 0, "read4" ), new TimedNamedOperation1( 13, 13, 6, "read5" ) ).iterator(); workloadStreams.addBlockingStream( blockingDependentOperationTypes, blockingDependencyOperationTypes, blockingDependencyOperations, blockingNonDependencyOperations, null ); Map<String,String> params = new HashMap<>(); params.put( DummyDb.ALLOWED_DEFAULT_ARG, "false" ); try ( DummyDb db = new DummyDb() ) { db.init( params, loggingService, DummyWorkload.OPERATION_TYPE_CLASS_MAPPING ); WorkloadRunnerThread runnerThread = workloadRunnerThread( timeSource, workloadStreams, threadCount, errorReporter, metricsService, completionTimeService, db ); MetricsService.MetricsServiceWriter metricsServiceWriter = metricsService.getWriter(); // initialize GCT completionTimeServiceAssistant.writeInitiatedAndCompletedTimesToAllWriters( completionTimeService, 0 ); completionTimeServiceAssistant.writeInitiatedAndCompletedTimesToAllWriters( completionTimeService, 1 ); timeSource.setNowFromMilli( 0 ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), is( 0l ) ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); runnerThread.start(); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), is( 1l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 1 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), is( 1l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 2 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), is( 1l ) ); db.setNameAllowedValue( "read1", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 1l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), is( 1l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 3 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 1l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), is( 1l ) ); db.setNameAllowedValue( "readwrite1", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 2l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 3l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 4 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 2l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 3l ) ); db.setNameAllowedValue( "read2", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 3l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 3l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 5 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 3l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 3l ) ); timeSource.setNowFromMilli( 6 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 3l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 3l ) ); db.setNameAllowedValue( "readwrite2", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 4l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 6l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 7 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 4l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 6l ) ); db.setNameAllowedValue( "read3", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 5l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 6l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 8 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 5l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 6l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 9 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 5l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 6l ) ); db.setNameAllowedValue( "readwrite3", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 6l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 10 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 6l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 11 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 6l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); db.setNameAllowedValue( "read4", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 7l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 12 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 7l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 13 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 7l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); db.setNameAllowedValue( "read5", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 8l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); long durationToWaitForRunnerToCompleteAsMilli = WorkloadRunner.RUNNER_POLLING_INTERVAL_AS_MILLI * 4; long timeoutTimeAsMilli = timeSource.nowAsMilli() + durationToWaitForRunnerToCompleteAsMilli; while ( timeSource.nowAsMilli() < timeoutTimeAsMilli ) { if ( runnerThread.runnerHasCompleted() ) { break; } Spinner.powerNap( 100 ); } db.setAllowedValueForAll( true ); assertThat( errorReporter.toString(), runnerThread.runnerHasCompleted(), is( true ) ); assertThat( errorReporter.toString(), runnerThread.runnerCompletedSuccessfully(), is( true ) ); } catch ( Throwable e ) { e.printStackTrace(); throw e; } finally { Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); metricsService.shutdown(); completionTimeService.shutdown(); } } @Test public void shouldSuccessfullyCompleteWhenAllOperationsFinishOnTimeWithReadBlockingReadWriteBlocking() throws DriverConfigurationException, DbException, CompletionTimeException, WorkloadException, InterruptedException, MetricsCollectionException, IOException { shouldSuccessfullyCompleteWhenAllOperationsFinishOnTimeWithReadBlockingReadWriteBlocking( 1 ); shouldSuccessfullyCompleteWhenAllOperationsFinishOnTimeWithReadBlockingReadWriteBlocking( 4 ); shouldSuccessfullyCompleteWhenAllOperationsFinishOnTimeWithReadBlockingReadWriteBlocking( 16 ); } public void shouldSuccessfullyCompleteWhenAllOperationsFinishOnTimeWithReadBlockingReadWriteBlocking( int threadCount ) throws DriverConfigurationException, DbException, CompletionTimeException, WorkloadException, InterruptedException, MetricsCollectionException, IOException { // @formatter:off /* Number of writers: 1 (blocking) Number of executors: 1 (blocking) Initialized to: IT[ , ] CT[0,1] BLOCKING BLOCKING READ READ_WRITE TimedNamedOperation1 TimedNamedOperation2 GCT (assumes initiated time submitted quickly) 0 0 <~~ S(2)D(0) initialized (READ ONLY) 1 0 2 S(2)D(0) 0 <-- S(3)D(0) initialized 3 S(3)D(0) 1 <~~ S(4)D(0) initialized (READ ONLY) 4 S(4)D(0) 3 <-- S(6)D(0) initialized 5 3 6 S(6)D(0) 3 <~~ S(7)D(3) initialized (READ ONLY) 7 S(7)D(3) 6 <-- S(9)D(3) initialized 8 6 9 S(9)D(3) 6 <~~ S(11)D(0) initialized (READ ONLY) 10 6 11 S(11)D(0) 6 <~~ S(13)D(6) initialized (READ ONLY) 12 6 13 S(13)D(6) 6 */ // @formatter:on LoggingService loggingService = new Log4jLoggingServiceFactory( false ).loggingServiceFor( "Test" ); ConcurrentErrorReporter errorReporter = new ConcurrentErrorReporter(); SimpleCsvFileWriter csvResultsLogWriter = null; Map<Integer,Class<? extends Operation>> operationTypeToClassMapping = new HashMap<>(); operationTypeToClassMapping.put( TimedNamedOperation1.TYPE, TimedNamedOperation1.class ); operationTypeToClassMapping.put( TimedNamedOperation2.TYPE, TimedNamedOperation2.class ); operationTypeToClassMapping.put( TimedNamedOperation3.TYPE, TimedNamedOperation3.class ); MetricsService metricsService = ThreadedQueuedMetricsService.newInstanceUsingBlockingBoundedQueue( timeSource, errorReporter, TimeUnit.MILLISECONDS, ThreadedQueuedMetricsService.DEFAULT_HIGHEST_EXPECTED_RUNTIME_DURATION_AS_NANO, csvResultsLogWriter, operationTypeToClassMapping, LOG4J_LOGGING_SERVICE_FACTORY ); Set<String> peerIds = new HashSet<>(); // TODO test also with threaded completion time service implementation CompletionTimeService completionTimeService = completionTimeServiceAssistant.newSynchronizedConcurrentCompletionTimeServiceFromPeerIds( peerIds ); WorkloadStreams workloadStreams = new WorkloadStreams(); Set<Class<? extends Operation>> asynchronousDependentOperationTypes = Sets.<Class<? extends Operation>>newHashSet( // nothing ); Set<Class<? extends Operation>> asynchronousDependencyOperationTypes = Sets.<Class<? extends Operation>>newHashSet( // nothing ); Iterator<Operation> asynchronousDependencyOperations = Lists.<Operation>newArrayList( // nothing ).iterator(); Iterator<Operation> asynchronousNonDependencyOperations = Lists.<Operation>newArrayList( // nothing ).iterator(); workloadStreams.setAsynchronousStream( asynchronousDependentOperationTypes, asynchronousDependencyOperationTypes, asynchronousDependencyOperations, asynchronousNonDependencyOperations, null ); Set<Class<? extends Operation>> blockingDependentOperationTypes = Sets.<Class<? extends Operation>>newHashSet( TimedNamedOperation1.class, TimedNamedOperation2.class ); Set<Class<? extends Operation>> blockingDependencyOperationTypes = Sets.<Class<? extends Operation>>newHashSet( TimedNamedOperation2.class ); Iterator<Operation> blockingDependencyOperations = Lists.<Operation>newArrayList( new TimedNamedOperation2( 3, 3, 0, "readwrite1" ), new TimedNamedOperation2( 6, 6, 0, "readwrite2" ), new TimedNamedOperation2( 9, 9, 3, "readwrite3" ) ).iterator(); Iterator<Operation> blockingNonDependencyOperations = Lists.<Operation>newArrayList( new TimedNamedOperation1( 2, 2, 0, "read1" ), new TimedNamedOperation1( 4, 4, 0, "read2" ), new TimedNamedOperation1( 7, 7, 3, "read3" ), new TimedNamedOperation1( 11, 11, 0, "read4" ), new TimedNamedOperation1( 13, 13, 6, "read5" ) ).iterator(); workloadStreams.addBlockingStream( blockingDependentOperationTypes, blockingDependencyOperationTypes, blockingDependencyOperations, blockingNonDependencyOperations, null ); Map<String,String> params = new HashMap<>(); params.put( DummyDb.ALLOWED_DEFAULT_ARG, "false" ); try ( DummyDb db = new DummyDb() ) { db.init( params, loggingService, DummyWorkload.OPERATION_TYPE_CLASS_MAPPING ); WorkloadRunnerThread runnerThread = workloadRunnerThread( timeSource, workloadStreams, threadCount, errorReporter, metricsService, completionTimeService, db ); MetricsService.MetricsServiceWriter metricsServiceWriter = metricsService.getWriter(); // initialize GCT completionTimeServiceAssistant.writeInitiatedAndCompletedTimesToAllWriters( completionTimeService, 0 ); completionTimeServiceAssistant.writeInitiatedAndCompletedTimesToAllWriters( completionTimeService, 1 ); timeSource.setNowFromMilli( 0 ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), is( 0l ) ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); runnerThread.start(); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); // GCT may be 0 or 1 at this stage, depending on the OperationHandlerExecutor used // anyOf because it depends on whether "readwrite1"/S(3)D(0) has been initialized yet, or not // SameThreadOperationHandlerExecutor will be 0, as it must wait for previous operation to complete // before it can initiate the next operation // SingleThread/ThreadPoolOperationHandlerExecutor will be 1, as it can initiate the next operation as // soon as it has submitted the previous one for execution assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( is( 0l ), is( 1l ) ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 1 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); // anyOf because it depends on whether "readwrite1"/S(3)D(0) has been initialized yet, or not assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( is( 0l ), is( 1l ) ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 2 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 0l ) ); // anyOf because it depends on whether "readwrite1"/S(3)D(0) has been initialized yet, or not assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( equalTo( 0l ), equalTo( 1l ) ) ); db.setNameAllowedValue( "read1", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 1l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), is( 1l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 3 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 1l ) ); // anyOf because it depends on whether "readwrite2"/S(6)D(0) has been initialized yet, or not assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( equalTo( 1l ), equalTo( 3l ) ) ); db.setNameAllowedValue( "readwrite1", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 2l ) ); // anyOf because it depends on whether "readwrite2"/S(6)D(0) has been initialized yet, or not assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( equalTo( 1l ), equalTo( 3l ) ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 4 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 2l ) ); // anyOf because it depends on whether "readwrite2"/S(6)D(0) has been initialized yet, or not assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( equalTo( 1l ), equalTo( 3l ) ) ); db.setNameAllowedValue( "read2", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 3l ) ); // GCT may be 0 or 1 at this stage, depending on the OperationHandlerExecutor used // anyOf because it depends on whether "readwrite2"/S(6)D(0) has been initialized yet, or not assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( equalTo( 1l ), equalTo( 3l ) ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 5 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 3l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); // anyOf because it depends on whether "readwrite2"/S(6)D(0) has been initialized yet, or not assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( equalTo( 1l ), equalTo( 3l ) ) ); timeSource.setNowFromMilli( 6 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 3l ) ); // anyOf because it depends on whether "readwrite3"/S(9)D(0) has been initialized yet, or not assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( equalTo( 3l ), equalTo( 6l ) ) ); db.setNameAllowedValue( "readwrite2", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 4l ) ); // anyOf because it depends on whether "readwrite3"/S(9)D(0) has been initialized yet, or not assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( equalTo( 3l ), equalTo( 6l ) ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 7 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 4l ) ); // anyOf because it depends on whether "readwrite3"/S(9)D(0) has been initialized yet, or not assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( equalTo( 3l ), equalTo( 6l ) ) ); db.setNameAllowedValue( "read3", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 5l ) ); // anyOf because it depends on whether "readwrite3"/S(9)D(0) has been initialized yet, or not assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( equalTo( 3l ), equalTo( 6l ) ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 8 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 5l ) ); // anyOf because it depends on whether "readwrite3"/S(9)D(0) has been initialized yet, or not assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), anyOf( equalTo( 3l ), equalTo( 6l ) ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 9 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 5l ) ); assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 6l ) ); db.setNameAllowedValue( "readwrite3", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 6l ) ); // should advance to 9, because this is the last GCT writing operation in the stream assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 10 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 6l ) ); // should advance to 9, because this is the last GCT writing operation in the stream assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 11 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 6l ) ); // should advance to 9, because this is the last GCT writing operation in the stream assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); db.setNameAllowedValue( "read4", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 7l ) ); // should advance to 9, because this is the last GCT writing operation in the stream assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 12 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 7l ) ); // should advance to 9, because this is the last GCT writing operation in the stream assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); timeSource.setNowFromMilli( 13 ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 7l ) ); // should advance to 9, because this is the last GCT writing operation in the stream assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); db.setNameAllowedValue( "read5", true ); Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); assertThat( errorReporter.toString(), metricsServiceWriter.results().totalOperationCount(), is( 8l ) ); // should advance to 9, because this is the last GCT writing operation in the stream assertThat( errorReporter.toString(), completionTimeService.globalCompletionTimeAsMilli(), equalTo( 9l ) ); assertThat( errorReporter.toString(), errorReporter.errorEncountered(), is( false ) ); long durationToWaitForRunnerToCompleteAsMilli = WorkloadRunner.RUNNER_POLLING_INTERVAL_AS_MILLI * 4; long timeoutTimeAsMilli = timeSource.nowAsMilli() + durationToWaitForRunnerToCompleteAsMilli; while ( timeSource.nowAsMilli() < timeoutTimeAsMilli ) { if ( runnerThread.runnerHasCompleted() ) { break; } Spinner.powerNap( 100 ); } db.setAllowedValueForAll( true ); assertThat( errorReporter.toString(), runnerThread.runnerHasCompleted(), is( true ) ); assertThat( errorReporter.toString(), runnerThread.runnerCompletedSuccessfully(), is( true ) ); } catch ( Throwable e ) { e.printStackTrace(); throw e; } finally { Thread.sleep( ENOUGH_MILLISECONDS_FOR_RUNNER_THREAD_TO_DO_ITS_THING ); metricsService.shutdown(); completionTimeService.shutdown(); } } private WorkloadRunnerThread workloadRunnerThread( TimeSource timeSource, WorkloadStreams workloadStreams, int threadCount, ConcurrentErrorReporter errorReporter, MetricsService metricsService, CompletionTimeService completionTimeService, Db db ) throws WorkloadException, CompletionTimeException, DbException, MetricsCollectionException { boolean ignoreScheduledStartTime = false; long statusDisplayIntervalAsMilli = 0; long spinnerSleepDurationAsMilli = SPINNER_SLEEP_DURATION_AS_MILLI; int operationHandlerExecutorsBoundedQueueSize = 100; boolean detailedStatus = false; LoggingServiceFactory loggingServiceFactory = new Log4jLoggingServiceFactory( detailedStatus ); WorkloadRunner runner = new WorkloadRunner( timeSource, db, workloadStreams, metricsService, errorReporter, completionTimeService, loggingServiceFactory, threadCount, statusDisplayIntervalAsMilli, spinnerSleepDurationAsMilli, ignoreScheduledStartTime, operationHandlerExecutorsBoundedQueueSize ); return new WorkloadRunnerThread( runner, errorReporter ); } private class WorkloadRunnerThread extends Thread { private final WorkloadRunner runner; private final AtomicBoolean runnerHasCompleted; private final AtomicBoolean runnerCompletedSuccessfully; private final ConcurrentErrorReporter errorReporter; WorkloadRunnerThread( WorkloadRunner runner, ConcurrentErrorReporter errorReporter ) { super( WorkloadRunnerThread.class.getSimpleName() + "-" + System.currentTimeMillis() ); this.runner = runner; this.runnerHasCompleted = new AtomicBoolean( false ); this.runnerCompletedSuccessfully = new AtomicBoolean( false ); this.errorReporter = errorReporter; } @Override public void run() { try { runner.getFuture().get(); runnerCompletedSuccessfully.set( true ); runnerHasCompleted.set( true ); } catch ( Throwable e ) { errorReporter.reportError( this, ConcurrentErrorReporter.stackTraceToString( e ) ); runnerCompletedSuccessfully.set( false ); runnerHasCompleted.set( true ); } } boolean runnerHasCompleted() { return runnerHasCompleted.get(); } boolean runnerCompletedSuccessfully() { return runnerCompletedSuccessfully.get(); } } }