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(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);
    }
}

@@ -2486,24 +2486,29 @@
      assert(
+(int)
 result.get(0

Support building with java 8