content: 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);
      }
  }
  