package com.airbnb.airpal.core.health;
import com.codahale.metrics.health.HealthCheck;
import com.facebook.presto.client.StatementClient;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import static com.airbnb.airpal.presto.QueryRunner.QueryRunnerFactory;
public class PrestoHealthCheck extends HealthCheck
{
private static final String HEALTH_CHECK_QUERY = "SELECT 1";
private final Supplier<Future<Result>> resultSupplier;
@Inject
public PrestoHealthCheck(
final QueryRunnerFactory queryRunnerFactory,
@Named("presto") final ExecutorService executorService)
{
// To prevent a lagging Presto health check from freezing Airpal, by blocking a large
// number of health check threads, we have the supplier return a Future<Result>. This
// way, the Future is immediately memoized and all calls will be successful if the
// future resolved successfully.
Supplier<Future<Result>> baseSupplier = new Supplier<Future<Result>>()
{
@Override
public Future<Result> get()
{
return executorService.submit(new Callable<Result>() {
@Override
public Result call()
throws Exception
{
final List<Object> invalidValue = ImmutableList.of((Object) new Integer(-1));
List<Object> result;
try (StatementClient client = queryRunnerFactory.create().startInternalQuery(HEALTH_CHECK_QUERY)) {
while (client.isValid() && !Thread.currentThread().isInterrupted()) {
Iterable<List<Object>> results = client.current().getData();
if (results != null) {
result = Iterables.getFirst(results, invalidValue);
assert(result != null);
assert(result.size() == 1);
assert((int)result.get(0) == 1);
}
client.advance();
}
return Result.healthy();
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
});
}
};
this.resultSupplier = Suppliers.memoizeWithExpiration(baseSupplier, 120, TimeUnit.SECONDS);
}
@Override
protected Result check() throws Exception
{
// Wait at most 5 seconds for the future to resolve, so that we don't block too many
// threads awaiting the result of this check.
return resultSupplier.get().get(5, TimeUnit.SECONDS);
}
}