@Override
  @NonNull
  public Map<ServerName, List<RegionInfo>> retainAssignment(Map<RegionInfo, ServerName> regions,
    List<ServerName> servers) throws HBaseIOException {
    Map<ServerName, List<RegionInfo>> assignmentMap = Maps.newHashMap();
    Map<ServerName, List<RegionInfo>> result = super.retainAssignment(regions, servers);
    if (result.isEmpty()) {
      LOG.warn("Nothing to assign to, probably no servers or no regions");
      return result;
    }

    // Lets check if favored nodes info is in META, if not generate now.
    FavoredNodeAssignmentHelper helper = new FavoredNodeAssignmentHelper(servers, getConf());
    helper.initialize();

    LOG.debug("Generating favored nodes for regions missing them.");
    Map<RegionInfo, List<ServerName>> regionFNMap = Maps.newHashMap();

    try {
      for (Map.Entry<ServerName, List<RegionInfo>> entry : result.entrySet()) {
        ServerName sn = entry.getKey();
        ServerName primary = ServerName.valueOf(sn.getHostname(), sn.getPort(), NON_STARTCODE);

        for (RegionInfo hri : entry.getValue()) {

          if (FavoredNodesManager.isFavoredNodeApplicable(hri)) {
            List<ServerName> favoredNodes = fnm.getFavoredNodes(hri);
            if (favoredNodes == null || favoredNodes.size() < FAVORED_NODES_NUM) {

              LOG.debug("Generating favored nodes for: " + hri + " with primary: " + primary);
              ServerName[] secondaryAndTertiaryNodes = helper.getSecondaryAndTertiary(hri, primary);
              if (secondaryAndTertiaryNodes != null && secondaryAndTertiaryNodes.length == 2) {
                List<ServerName> newFavoredNodes = Lists.newArrayList();
                newFavoredNodes.add(primary);
                newFavoredNodes.add(ServerName.valueOf(secondaryAndTertiaryNodes[0].getHostname(),
                  secondaryAndTertiaryNodes[0].getPort(), NON_STARTCODE));
                newFavoredNodes.add(ServerName.valueOf(secondaryAndTertiaryNodes[1].getHostname(),
                  secondaryAndTertiaryNodes[1].getPort(), NON_STARTCODE));
                regionFNMap.put(hri, newFavoredNodes);
                addRegionToMap(assignmentMap, hri, sn);

              } else {
                throw new HBaseIOException(
                  "Cannot generate secondary/tertiary FN for " + hri + " generated "
                    + (secondaryAndTertiaryNodes != null ? secondaryAndTertiaryNodes : " nothing"));
              }
            } else {
              List<ServerName> onlineFN = getOnlineFavoredNodes(servers, favoredNodes);
              if (onlineFN.isEmpty()) {
                // All favored nodes are dead, lets assign it to BOGUS
                addRegionToMap(assignmentMap, hri, BOGUS_SERVER_NAME);
              } else {
                // Is primary not on FN? Less likely, but we can still take care of this.
                if (FavoredNodesPlan.getFavoredServerPosition(favoredNodes, sn) != null) {
                  addRegionToMap(assignmentMap, hri, sn);
                } else {
                  ServerName destination =
                    onlineFN.get(ThreadLocalRandom.current().nextInt(onlineFN.size()));
                  LOG.warn("Region: " + hri + " not hosted on favored nodes: " + favoredNodes
                    + " current: " + sn + " moving to: " + destination);
                  addRegionToMap(assignmentMap, hri, destination);
                }
              }
            }
          } else {
            addRegionToMap(assignmentMap, hri, sn);
          }
        }
      }

      if (!regionFNMap.isEmpty()) {
        LOG.debug("Updating FN in meta for missing regions, count: " + regionFNMap.size());
        fnm.updateFavoredNodes(regionFNMap);
      }

    } catch (IOException e) {
      throw new HBaseIOException("Cannot generate/update FN for regions: " + regionFNMap.keySet());
    }

    return assignmentMap;
  }
Reuse BaseLoadBalancer's retainAssignment, but generate favored nodes when its missing.
hbase-balancer/src/main/java/org/apache/hadoop/hbase/master/balancer/FavoredStochasticBalancer.java