private void refreshLocalityChangedRegions(ClusterMetrics oldStatus, ClusterMetrics newStatus) {
    if (oldStatus == null || newStatus == null) {
      LOG.debug("Skipping locality-based refresh due to oldStatus={}, newStatus={}", oldStatus,
        newStatus);
      return;
    }

    Map<ServerName, ServerMetrics> oldServers = oldStatus.getLiveServerMetrics();
    Map<ServerName, ServerMetrics> newServers = newStatus.getLiveServerMetrics();

    Map<String, RegionInfo> regionsByName = new HashMap<>(cache.asMap().size());
    for (RegionInfo regionInfo : cache.asMap().keySet()) {
      regionsByName.put(regionInfo.getEncodedName(), regionInfo);
    }

    for (Map.Entry<ServerName, ServerMetrics> serverEntry : newServers.entrySet()) {
      Map<byte[], RegionMetrics> newRegions = serverEntry.getValue().getRegionMetrics();
      for (Map.Entry<byte[], RegionMetrics> regionEntry : newRegions.entrySet()) {
        String encodedName = RegionInfo.encodeRegionName(regionEntry.getKey());
        RegionInfo region = regionsByName.get(encodedName);
        if (region == null) {
          continue;
        }

        float newLocality = regionEntry.getValue().getDataLocality();
        float oldLocality = getOldLocality(serverEntry.getKey(), regionEntry.getKey(), oldServers);

        if (Math.abs(newLocality - oldLocality) > EPSILON) {
          LOG.debug("Locality for region {} changed from {} to {}, refreshing cache",
            region.getEncodedName(), oldLocality, newLocality);
          cache.refresh(region);
        }
      }

    }
  }
If locality for a region has changed, that pretty certainly means our cache is out of date.
Compare oldStatus and newStatus, refreshing any regions which have moved or changed locality.
hbase-balancer/src/main/java/org/apache/hadoop/hbase/master/balancer/RegionHDFSBlockLocationFinder.java