@Override
  public Server chooseServer(Object key) {
    if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {
      logger.debug("Zone aware logic disabled or there is only one zone");
      return super.chooseServer(key);
    }
    Server server = null;
    try {
      LoadBalancerStats lbStats = getLoadBalancerStats();
      Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);
      logger.debug("Zone snapshots: {}", zoneSnapshot);
      if (triggeringLoad == null) {
        triggeringLoad =
            DynamicPropertyFactory.getInstance()
                .getDoubleProperty(
                    "ZoneAwareNIWSDiscoveryLoadBalancer."
                        + this.getName()
                        + ".triggeringLoadPerServerThreshold",
                    0.2d);
      }

      if (triggeringBlackoutPercentage == null) {
        triggeringBlackoutPercentage =
            DynamicPropertyFactory.getInstance()
                .getDoubleProperty(
                    "ZoneAwareNIWSDiscoveryLoadBalancer."
                        + this.getName()
                        + ".avoidZoneWithBlackoutPercetage",
                    0.99999d);
      }
      Set<String> availableZones =
          ZoneAvoidanceRule.getAvailableZones(
              zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get());
      logger.debug("Available zones: {}", availableZones);
      if (availableZones != null && availableZones.size() < zoneSnapshot.keySet().size()) {
        String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones);
        logger.debug("Zone chosen: {}", zone);
        if (zone != null) {
          BaseLoadBalancer zoneLoadBalancer = getLoadBalancer(zone);
          server = zoneLoadBalancer.chooseServer(key);
        }
      }
    } catch (Throwable e) {
      logger.error("Unexpected exception when choosing server using zone aware logic", e);
    }
    if (server != null) {
      return server;
    } else {
      logger.debug("Zone avoidance logic is not invoked.");
      return super.chooseServer(key);
    }
  }
  /**
   * @param availableServerStats the server instances to choose from
   * @return the nearest, less loaded server instance
   * @see {@link LoadBalancer#choose(java.util.List)}
   */
  @Override
  public ServerStats choose(List<ServerStats> availableServerStats) {
    // cache properties to speed up loop
    double maxRequestsPerSecond = propMaxRequestsPerSecond.get();
    double escapeAreaThreshold = propEscapeAreaThreshold.get();
    double escapeRegionThreshold = propEscapeRegionThreshold.get();
    double escapeAvailabilityThreshold = propEscapeAvailabilityThreshold.get();

    // find the best available server
    MetaData max = null;
    for (ServerStats stat : availableServerStats) {
      ServerStats s = (ServerStats) stat;
      ServerInstance instance = s.getServerInstance();

      MetaData meta = new MetaData();
      meta.location = locations.getUnchecked(s);
      meta.server = s;
      meta.load = s.getSentMessagesPerSecond() / maxRequestsPerSecond;
      meta.sessionCount = s.getOpenSessionsCount();
      meta.locationBits = 0;
      if (myLocation.getAvailabilityZone().equals(meta.location.getAvailabilityZone())) {
        // same availability zone
        meta.locationBits = 7;
      } else if (myLocation.getRegion().equals(meta.location.getRegion())) {
        // same region
        meta.locationBits = 3;
      } else if (myLocation.getArea().equals(meta.location.getArea())) {
        // same area
        meta.locationBits = 1;
      }

      // keep the best server instance
      if (max == null) {
        max = meta;
      } else if (meta.isBetterThan(
          max, escapeAreaThreshold, escapeRegionThreshold, escapeAvailabilityThreshold)) {
        max = meta;
      }
    }

    if (max != null) {
      if (metricRegistry != null) {
        Counter counter = availabilityZoneToCounter.get(max.location.getAvailabilityZone());
        if (counter == null) {
          counter =
              metricRegistry.counter(
                  name(serviceName, "az-requests", max.location.getAvailabilityZone()));

          Counter prevCount =
              availabilityZoneToCounter.putIfAbsent(max.location.getAvailabilityZone(), counter);
          if (prevCount != null) {
            // another thread snuck in their counter during a race condition so use it instead.
            counter = prevCount;
          }
        }
        counter.inc();
      }
      return max.server;
    } else {
      return null;
    }
  }