private void loadMetaLocationsFromZk(RetryCounter retryCounter, ZNodeOpType opType) {
    TraceUtil.trace(() -> {
      List<String> znodes = null;
      while (retryCounter.shouldRetry()) {
        try {
          znodes = watcher.getMetaReplicaNodesAndWatchChildren();
          break;
        } catch (KeeperException ke) {
          LOG.debug("Error populating initial meta locations", ke);
          if (!retryCounter.shouldRetry()) {
            // Retries exhausted and watchers not set. This is not a desirable state since the cache
            // could remain stale forever. Propagate the exception.
            watcher.abort("Error populating meta locations", ke);
            return;
          }
          try {
            retryCounter.sleepUntilNextRetry();
          } catch (InterruptedException ie) {
            LOG.error("Interrupted while loading meta locations from ZK", ie);
            Thread.currentThread().interrupt();
            return;
          }
        }
      }
      if (znodes == null || znodes.isEmpty()) {
        // No meta znodes exist at this point but we registered a watcher on the base znode to
        // listen for updates. They will be handled via nodeChildrenChanged().
        return;
      }
      if (znodes.size() == cachedMetaLocations.size()) {
        // No new meta znodes got added.
        return;
      }
      for (String znode : znodes) {
        String path = ZNodePaths.joinZNode(watcher.getZNodePaths().baseZNode, znode);
        updateMetaLocation(path, opType);
      }
    }, "MetaRegionLocationCache.loadMetaLocationsFromZk");
  }
Populates the current snapshot of meta locations from ZK. If no meta znodes exist, it registers
a watcher on base znode to check for any CREATE/DELETE events on the children.
@param retryCounter controls the number of retries and sleep between retries.
hbase-server/src/main/java/org/apache/hadoop/hbase/MetaRegionLocationCache.java