package com.ldbc.driver.runtime;
import com.ldbc.driver.control.LoggingService;
import com.ldbc.driver.control.LoggingServiceFactory;
import com.ldbc.driver.control.RecentThroughputAndDuration;
import com.ldbc.driver.runtime.coordination.CompletionTimeService;
import com.ldbc.driver.runtime.metrics.MetricsService.MetricsServiceWriter;
import com.ldbc.driver.runtime.metrics.WorkloadStatusSnapshot;
import com.ldbc.driver.runtime.scheduling.Spinner;
import java.util.concurrent.atomic.AtomicBoolean;
import static java.lang.String.format;
class WorkloadStatusThread extends Thread
{
private final long statusUpdateIntervalAsMilli;
private final MetricsServiceWriter metricsServiceWriter;
private final ConcurrentErrorReporter errorReporter;
private final CompletionTimeService completionTimeService;
private final LoggingService loggingService;
private AtomicBoolean continueRunning = new AtomicBoolean( true );
WorkloadStatusThread(
long statusUpdateIntervalAsMilli,
MetricsServiceWriter metricsServiceWriter,
ConcurrentErrorReporter errorReporter,
CompletionTimeService completionTimeService,
LoggingServiceFactory loggingServiceFactory )
{
super( WorkloadStatusThread.class.getSimpleName() + "-" + System.currentTimeMillis() );
this.statusUpdateIntervalAsMilli = statusUpdateIntervalAsMilli;
this.metricsServiceWriter = metricsServiceWriter;
this.errorReporter = errorReporter;
this.completionTimeService = completionTimeService;
this.loggingService = loggingServiceFactory.loggingServiceFor( getClass().getSimpleName() );
}
@Override
public void run()
{
final SettableRecentThroughputAndDuration
settableRecentThroughputAndDuration = new SettableRecentThroughputAndDuration();
final int statusRecency = 4;
final long[][] operationCountsAtDurations = new long[statusRecency][2];
for ( int i = 0; i < operationCountsAtDurations.length; i++ )
{
operationCountsAtDurations[i][0] = -1;
operationCountsAtDurations[i][1] = -1;
}
int statusRecencyIndex = 0;
while ( continueRunning.get() )
{
try
{
WorkloadStatusSnapshot status = metricsServiceWriter.status();
operationCountsAtDurations[statusRecencyIndex][0] = status.operationCount();
operationCountsAtDurations[statusRecencyIndex][1] = status.runDurationAsMilli();
statusRecencyIndex = (statusRecencyIndex + 1) % statusRecency;
updateRecentThroughput( operationCountsAtDurations, settableRecentThroughputAndDuration );
loggingService.status(
status,
settableRecentThroughputAndDuration,
completionTimeService.globalCompletionTimeAsMilli()
);
Spinner.powerNap( statusUpdateIntervalAsMilli );
}
catch ( Throwable e )
{
errorReporter.reportError(
this,
format(
"Status reporting thread encountered unexpected error - exiting\n%s",
ConcurrentErrorReporter.stackTraceToString( e )
)
);
break;
}
}
}
synchronized public final void shutdown()
{
if ( false == continueRunning.get() )
{
return;
}
continueRunning.set( false );
}
private void updateRecentThroughput( final long[][] recentOperationCountsAtDurations,
final SettableRecentThroughputAndDuration settableRecentThroughputAndDuration )
{
long minOperationCount = Long.MAX_VALUE;
long maxOperationCount = Long.MIN_VALUE;
long minDurationAsMilli = Long.MAX_VALUE;
long maxDurationAsMilli = Long.MIN_VALUE;
for ( int i = 0; i < recentOperationCountsAtDurations.length; i++ )
{
long operationCount = recentOperationCountsAtDurations[i][0];
long durationAsMilli = recentOperationCountsAtDurations[i][1];
if ( -1 == operationCount )
{
continue;
}
minOperationCount = Math.min( minOperationCount, operationCount );
maxOperationCount = Math.max( maxOperationCount, operationCount );
minDurationAsMilli = Math.min( minDurationAsMilli, durationAsMilli );
maxDurationAsMilli = Math.max( maxDurationAsMilli, durationAsMilli );
}
long recentRunDurationAsMilli = maxDurationAsMilli - minDurationAsMilli;
long recentOperationCount = maxOperationCount - minOperationCount;
double recentThroughput = (0 == recentRunDurationAsMilli)
? 0
: (double) recentOperationCount / recentRunDurationAsMilli * 1000;
settableRecentThroughputAndDuration.setThroughput( recentThroughput );
settableRecentThroughputAndDuration.setDuration( recentRunDurationAsMilli );
}
private class SettableRecentThroughputAndDuration implements RecentThroughputAndDuration
{
private double throughput = 0.0;
private long duration = 0;
private void setThroughput( double throughput )
{
this.throughput = throughput;
}
private void setDuration( long duration )
{
this.duration = duration;
}
public double throughput()
{
return throughput;
}
public long duration()
{
return duration;
}
}
}