/**
   * Algo: - we put the query into the execution pool. - after x ms, if we don't have a result, we
   * add the queries for the secondary replicas - we take the first answer - when done, we cancel
   * what's left. Cancelling means: - removing from the pool if the actual call was not started -
   * interrupting the call if it has started Client side, we need to take into account - a call is
   * not executed immediately after being put into the pool - a call is a thread. Let's not multiply
   * the number of thread by the number of replicas. Server side, if we can cancel when it's still
   * in the handler pool, it's much better, as a call can take some i/o.
   *
   * <p>Globally, the number of retries, timeout and so on still applies, but it's per replica, not
   * global. We continue until all retries are done, or all timeouts are exceeded.
   */
  public synchronized Result call()
      throws DoNotRetryIOException, InterruptedIOException, RetriesExhaustedException {
    boolean isTargetReplicaSpecified = (get.getReplicaId() >= 0);

    RegionLocations rl =
        getRegionLocations(
            true,
            (isTargetReplicaSpecified ? get.getReplicaId() : RegionReplicaUtil.DEFAULT_REPLICA_ID),
            cConnection,
            tableName,
            get.getRow());
    ResultBoundedCompletionService<Result> cs =
        new ResultBoundedCompletionService<Result>(this.rpcRetryingCallerFactory, pool, rl.size());

    if (isTargetReplicaSpecified) {
      addCallsForReplica(cs, rl, get.getReplicaId(), get.getReplicaId());
    } else {
      addCallsForReplica(cs, rl, 0, 0);
      try {
        // wait for the timeout to see whether the primary responds back
        Future<Result> f = cs.poll(timeBeforeReplicas, TimeUnit.MICROSECONDS); // Yes, microseconds
        if (f != null) {
          return f.get(); // great we got a response
        }
      } catch (ExecutionException e) {
        throwEnrichedException(e, retries);
      } catch (CancellationException e) {
        throw new InterruptedIOException();
      } catch (InterruptedException e) {
        throw new InterruptedIOException();
      }

      // submit call for the all of the secondaries at once
      addCallsForReplica(cs, rl, 1, rl.size() - 1);
    }

    try {
      try {
        Future<Result> f = cs.take();
        return f.get();
      } catch (ExecutionException e) {
        throwEnrichedException(e, retries);
      }
    } catch (CancellationException e) {
      throw new InterruptedIOException();
    } catch (InterruptedException e) {
      throw new InterruptedIOException();
    } finally {
      // We get there because we were interrupted or because one or more of the
      // calls succeeded or failed. In all case, we stop all our tasks.
      cs.cancelAll();
    }

    return null; // unreachable
  }
 /**
  * Creates the calls and submit them
  *
  * @param cs - the completion service to use for submitting
  * @param rl - the region locations
  * @param min - the id of the first replica, inclusive
  * @param max - the id of the last replica, inclusive.
  */
 private void addCallsForReplica(
     ResultBoundedCompletionService<Result> cs, RegionLocations rl, int min, int max) {
   for (int id = min; id <= max; id++) {
     HRegionLocation hrl = rl.getRegionLocation(id);
     ReplicaRegionServerCallable callOnReplica = new ReplicaRegionServerCallable(id, hrl);
     cs.submit(callOnReplica, callTimeout, id);
   }
 }