package com.ldbc.driver;
import com.ldbc.driver.temporal.TemporalUtil;
import stormpot.Allocator;
import stormpot.BlazePool;
import stormpot.Completion;
import stormpot.Config;
import stormpot.Expiration;
import stormpot.Slot;
import stormpot.SlotInfo;
import stormpot.Timeout;
import java.util.concurrent.TimeUnit;
import static java.lang.String.format;
public class PoolingOperationHandlerRunnerFactory implements OperationHandlerRunnerFactory
{
private static final int INITIAL_POOL_SIZE = 512;
private static final int MAX_POOL_SIZE = (int) Math.round( Math.pow( 2, 15 ) ); // ~32,000
private static final Timeout POOL_CLAIM_TIMEOUT = new Timeout( 100, TimeUnit.MILLISECONDS );
private static final Timeout POOL_CLAIM_AFTER_RESIZE_TIMEOUT = new Timeout( 1000, TimeUnit.MILLISECONDS );
private static final Timeout POOL_SHUTDOWN_TIMEOUT = new Timeout( 10, TimeUnit.SECONDS );
private final BlazePool<OperationHandlerRunnableContext> operationHandlerRunnerPool;
private final OperationHandlerRunnerFactory innerOperationHandlerRunnerFactory;
int highestSetPoolSize = 0;
public PoolingOperationHandlerRunnerFactory( OperationHandlerRunnerFactory operationHandlerRunnerFactory )
{
this.innerOperationHandlerRunnerFactory = operationHandlerRunnerFactory;
OperationHandlerRunnerAllocator operationHandlerRunnerAllocator =
new OperationHandlerRunnerAllocator( innerOperationHandlerRunnerFactory );
Config<OperationHandlerRunnableContext> operationHandlerRunnerPoolConfig = new Config<>();
operationHandlerRunnerPoolConfig.setAllocator( operationHandlerRunnerAllocator );
operationHandlerRunnerPoolConfig.setBackgroundExpirationEnabled( false );
operationHandlerRunnerPoolConfig.setPreciseLeakDetectionEnabled( false );
operationHandlerRunnerPoolConfig.setExpiration( new NeverExpiration() );
this.operationHandlerRunnerPool = new BlazePool<>( operationHandlerRunnerPoolConfig );
this.operationHandlerRunnerPool.setTargetSize( INITIAL_POOL_SIZE );
this.highestSetPoolSize = INITIAL_POOL_SIZE;
}
@Override
public OperationHandlerRunnableContext newOperationHandlerRunner() throws OperationException
{
try
{
OperationHandlerRunnableContext operationHandlerRunner =
operationHandlerRunnerPool.claim( POOL_CLAIM_TIMEOUT );
while ( null == operationHandlerRunner )
{
int currentPoolSize = operationHandlerRunnerPool.getTargetSize();
if ( currentPoolSize < MAX_POOL_SIZE )
{
operationHandlerRunnerPool.setTargetSize( currentPoolSize * 2 );
highestSetPoolSize = currentPoolSize * 2;
}
operationHandlerRunner = operationHandlerRunnerPool.claim( POOL_CLAIM_AFTER_RESIZE_TIMEOUT );
}
return operationHandlerRunner;
}
catch ( Exception e )
{
int currentPoolSize = operationHandlerRunnerPool.getTargetSize();
throw new OperationException(
format( "Error encountered while attempting to allocate handler runner from pool\n"
+ "Max pool size: %s\n"
+ "Highest set pool size: %s\n"
+ "Current pool size: %s",
MAX_POOL_SIZE,
highestSetPoolSize,
currentPoolSize ),
e
);
}
}
@Override
public void shutdown() throws OperationException
{
TemporalUtil temporalUtil = new TemporalUtil();
innerOperationHandlerRunnerFactory.shutdown();
Completion completion = operationHandlerRunnerPool.shutdown();
try
{
boolean isSuccessfulShutdown = completion.await( POOL_SHUTDOWN_TIMEOUT );
if ( false == isSuccessfulShutdown )
{
throw new OperationException(
format(
"Operation handler pool did not shutdown before timeout: %s\n"
+ "Pool Target Size: %s\n"
+ "Pool Allocation Count: %s\n"
+ "Pool Failed Allocation Count: %s\n"
+ "Pool Leaked Objects Count: %s\n",
temporalUtil.milliDurationToString( TimeUnit.MILLISECONDS
.convert( POOL_SHUTDOWN_TIMEOUT.getTimeout(),
POOL_SHUTDOWN_TIMEOUT.getUnit() ) ),
operationHandlerRunnerPool.getTargetSize(),
operationHandlerRunnerPool.getAllocationCount(),
operationHandlerRunnerPool.getFailedAllocationCount(),
operationHandlerRunnerPool.getLeakedObjectsCount()
// TODO percentile stats require MetricsRecorder implementation to work
// TODO http://chrisvest.github.io/stormpot/site/apidocs/stormpot/MetricsRecorder.html
// operationHandlerRunnerPool.getAllocationLatencyPercentile(90),
// operationHandlerRunnerPool.getAllocationLatencyPercentile(99),
// operationHandlerRunnerPool.getAllocationLatencyPercentile(100),
// operationHandlerRunnerPool.getDeallocationLatencyPercentile(90),
// operationHandlerRunnerPool.getDeallocationLatencyPercentile(99),
// operationHandlerRunnerPool.getDeallocationLatencyPercentile(100),
// operationHandlerRunnerPool.getAllocationFailureLatencyPercentile(90),
// operationHandlerRunnerPool.getAllocationFailureLatencyPercentile(99),
// operationHandlerRunnerPool.getAllocationFailureLatencyPercentile(100),
// operationHandlerRunnerPool.getObjectLifetimePercentile(90),
// operationHandlerRunnerPool.getObjectLifetimePercentile(99),
// operationHandlerRunnerPool.getObjectLifetimePercentile(100),
// operationHandlerRunnerPool.getReallocationFailureLatencyPercentile(90),
// operationHandlerRunnerPool.getReallocationFailureLatencyPercentile(99),
// operationHandlerRunnerPool.getReallocationFailureLatencyPercentile(100)
)
);
}
}
catch ( InterruptedException e )
{
throw new OperationException( "Error encountered while shutting down operation handler pool", e );
}
}
@Override
public String toString()
{
return PoolingOperationHandlerRunnerFactory.class.getSimpleName() + "{" +
innerOperationHandlerRunnerFactory.toString() + "}";
}
private static class OperationHandlerRunnerAllocator implements Allocator<OperationHandlerRunnableContext>
{
private final OperationHandlerRunnerFactory operationHandlerRunnerFactory;
public OperationHandlerRunnerAllocator( OperationHandlerRunnerFactory operationHandlerRunnerFactory )
{
this.operationHandlerRunnerFactory = operationHandlerRunnerFactory;
}
@Override
public OperationHandlerRunnableContext allocate( Slot slot ) throws Exception
{
OperationHandlerRunnableContext operationHandlerRunner =
operationHandlerRunnerFactory.newOperationHandlerRunner();
operationHandlerRunner.setSlot( slot );
return operationHandlerRunner;
}
@Override
public void deallocate( OperationHandlerRunnableContext operationHandlerRunner ) throws Exception
{
// I think nothing needs to be done here
}
}
private static class NeverExpiration implements Expiration<OperationHandlerRunnableContext>
{
@Override
public boolean hasExpired( SlotInfo<? extends OperationHandlerRunnableContext> slotInfo ) throws Exception
{
return false;
}
}
}