package com.ldbc.driver.runtime.scheduling; import com.ldbc.driver.Operation; import com.ldbc.driver.temporal.TimeSource; import com.ldbc.driver.util.Function2; // TODO if an error policy DOES NOT terminate the benchmark and DOES NOT allow the operation to complete // TODO something needs to be done about DEPENDENT/GCT, because the initiated time for the operation has already been // reported // TODO perhaps the completed time for that operation needs to be reported too (to GCT service, not to MetricsService), // TODO to make sure DEPENDENT/GCT does not freeze at the start time of that "Failed" operation // // TODO alternatively, if the operation is a Dependency the run should terminate for sure, as the data may not have // been written // TODO and if it is not a Dependency a policy could decide if the benchmark should terminate or not // // TODO take boolean result from spinner into consideration, i.e., DO NOT execute handler for "Failed" operations public class Spinner { public static final long DEFAULT_SLEEP_DURATION_10_MILLI = 10; public static final SpinnerCheck TRUE_CHECK = new TrueCheck(); private final Function2<Operation,SpinnerCheck,Boolean,RuntimeException> spinFun; public Spinner( TimeSource timeSource, long sleepDurationAsMilli, boolean ignoreScheduleStartTimes ) { this.spinFun = (ignoreScheduleStartTimes) ? new WaitForChecksFun( sleepDurationAsMilli ) : new WaitForChecksAndScheduledStartTimeFun( timeSource, sleepDurationAsMilli ); } public boolean waitForScheduledStartTime( Operation operation ) { return waitForScheduledStartTime( operation, TRUE_CHECK ); } /** * waits for the scheduled start time of an operation, and returns boolean value indicating if operation should be * executed or not. * true = operation may still be executed * false = operation should not be scheduled * return value calculated as follows: * true && handleFailedCheck * i.e., if error occurs it (1) has ability to cancel operation execution (2) can do anything else in its handler * * @param operation operation to wait for * @param check checks that must all pass before spinner returns * @return operation may be executed */ public boolean waitForScheduledStartTime( Operation operation, SpinnerCheck check ) { return spinFun.apply( operation, check ); } // sleep to reduce CPU load while spinning // NOTE: longer sleep == lower scheduling accuracy AND lower achievable throughput public static void powerNap( long sleepMs ) { if ( 0 == sleepMs ) { return; } else { try { Thread.sleep( sleepMs ); } catch ( InterruptedException e ) { // do nothing } } } private static class WaitForChecksAndScheduledStartTimeFun implements Function2<Operation,SpinnerCheck,Boolean,RuntimeException> { private final TimeSource timeSource; private final long sleepDurationAsMilli; private WaitForChecksAndScheduledStartTimeFun( TimeSource timeSource, long sleepDurationAsMilli ) { this.timeSource = timeSource; this.sleepDurationAsMilli = sleepDurationAsMilli; } @Override public Boolean apply( Operation operation, SpinnerCheck check ) { // earliest time at which operation may start // wait for checks to have all passed before allowing operation to start while ( SpinnerCheck.SpinnerCheckResult.STILL_CHECKING == check.doCheck( operation ) ) { powerNap( sleepDurationAsMilli ); } // wait for scheduled operation start time while ( timeSource.nowAsMilli() < operation.scheduledStartTimeAsMilli() ) { powerNap( sleepDurationAsMilli ); } return SpinnerCheck.SpinnerCheckResult.PASSED == check.doCheck( operation ); } } private static class WaitForChecksFun implements Function2<Operation,SpinnerCheck,Boolean,RuntimeException> { private final long sleepDurationAsMilli; private WaitForChecksFun( long sleepDurationAsMilli ) { this.sleepDurationAsMilli = sleepDurationAsMilli; } @Override public Boolean apply( Operation operation, SpinnerCheck check ) { // wait for checks to have all passed before allowing operation to start while ( SpinnerCheck.SpinnerCheckResult.STILL_CHECKING == check.doCheck( operation ) ) { powerNap( sleepDurationAsMilli ); } return SpinnerCheck.SpinnerCheckResult.PASSED == check.doCheck( operation ); } } private static class TrueCheck implements SpinnerCheck { @Override public SpinnerCheckResult doCheck( Operation operation ) { return SpinnerCheckResult.PASSED; } @Override public boolean handleFailedCheck( Operation operation ) { return true; } } }