typename RouterInfo::RouteHandlePtr createEagerShardSelectionShadowRoute()

in mcrouter/routes/ShardSelectionRouteFactory-inl.h [498:713]


typename RouterInfo::RouteHandlePtr createEagerShardSelectionShadowRoute(
    RouteHandleFactory<typename RouterInfo::RouteHandleIf>& factory,
    const folly::dynamic& json,
    const ChildrenFactoryMap<RouterInfo>& childrenFactoryMap,
    const ChildrenFactoryMap<RouterInfo>& shadowChildrenFactoryMap,
    const folly::Optional<ShadowSelectorPolicy>& shadowSelectorPolicy,
    const uint32_t seed) {
  checkLogic(
      json.isObject(),
      "EagerShardSelectionShadowRoute config should be an object");

  const auto childrenType = [&json]() {
    auto jChildType = json.get_ptr("children_type");
    checkLogic(
        jChildType && jChildType->isString(),
        "EagerShardSelectionShadowRoute: 'children_type' not found or is not a "
        "string");
    return jChildType->stringPiece();
  }();

  const auto& childrenSettings = [&json]() {
    auto jSettings = json.get_ptr("children_settings");
    checkLogic(
        jSettings && jSettings->isObject(),
        "EagerShardSelectionShadowRoute: 'children_settings' not found or not an "
        "object");
    return *jSettings;
  }();

  std::vector<typename RouterInfo::RouteHandlePtr> destinations;
  auto shardMap = detail::getShardDestinationsMap<RouterInfo>(factory, json);
  MapType shardToDestinationIndexMap = detail::prepareMap<MapType>(
      shardMap.size(), detail::getMaxShardId(shardMap));
  if (!shardMap.empty()) {
    if (childrenType == "LoadBalancerRoute") {
      detail::buildChildrenLoadBalancerRoutes<RouterInfo, MapType>(
          factory,
          childrenSettings,
          shardMap,
          destinations,
          shardToDestinationIndexMap);
    } else if (childrenType == "LatestRoute") {
      detail::buildChildrenLatestRoutes<RouterInfo, MapType>(
          factory,
          childrenSettings,
          shardMap,
          destinations,
          shardToDestinationIndexMap);
    } else if (childrenType == "CustomJsonmRoute") {
      detail::buildChildrenCustomJsonmRoutes<RouterInfo, MapType>(
          factory,
          childrenSettings,
          shardMap,
          destinations,
          shardToDestinationIndexMap);
    } else {
      auto it = childrenFactoryMap.find(childrenType.str());
      if (it != childrenFactoryMap.end()) {
        detail::buildChildrenCustomRoutesFromMap<RouterInfo, MapType>(
            factory,
            childrenSettings,
            shardMap,
            it->second,
            destinations,
            shardToDestinationIndexMap);
      } else {
        throwLogic(
            "EagerShardSelectionRoute: 'children_type' {} not supported",
            childrenType);
      }
    }
  }

  // Out Of Range Destinations
  typename RouterInfo::RouteHandlePtr outOfRangeDestination = nullptr;
  if (auto outOfRangeJson = json.get_ptr("out_of_range")) {
    outOfRangeDestination = factory.create(*outOfRangeJson);
  }

  // If shard map is empty, the selector will mark everything out_of_range
  ShardSelector selector(std::move(shardToDestinationIndexMap));

  /**
   * Shadow settings
   */

  // Shadow children type
  const auto shadowChildrenType = [&json]() {
    auto jChildType = json.get_ptr("shadow_children_type");
    checkLogic(
        jChildType && jChildType->isString(),
        "EagerShardSelectionShadowRoute: 'shadow_children_type' not found or is not a "
        "string");
    return jChildType->stringPiece();
  }();

  // Shadow children settings
  const auto& shadowChildrenSettings = [&json]() {
    auto jSettings = json.get_ptr("shadow_children_settings");
    checkLogic(
        jSettings && jSettings->isObject(),
        "EagerShardSelectionShadowRoute: 'shadow_children_settings' not found or not an "
        "object");
    return *jSettings;
  }();

  // Weight of hosts in shadow shard map. If > 1.0, hosts in shadow shard map
  // will receive more shadow requests. If < 1.0, hosts in shadow shard map
  // will receive less shadow requests.
  double shadowWeights = 1.0;
  if (auto jShadowWeights = json.get_ptr("shadow_weights")) {
    checkLogic(jShadowWeights->isDouble(), "shadow_weights is not a double");
    shadowWeights = jShadowWeights->asDouble();
  }

  constexpr folly::StringPiece shadowShardName = "shadow_shards";
  if (detail::hasShardsJson<RouterInfo>(factory, json, shadowShardName) ==
      false) {
    // Shadow shards property is missing, create a regular selection route
    return createSelectionRoute<RouterInfo, ShardSelector>(
        std::move(destinations),
        std::move(selector),
        std::move(outOfRangeDestination));
  }

  // Create Shadow Shard Map
  auto shadowShardMap = detail::getShardDestinationsMap<RouterInfo>(
      factory, json, shadowShardName);
  if (shadowShardMap.empty()) {
    // Shard and Shadow Shard maps are empty, create error route.
    if (shardMap.empty()) {
      return mcrouter::createErrorRoute<RouterInfo>(
          "EagerShardSelectionShadowRoute has an empty list of destinations");
    } else {
      // Shadow shards are empty, create a regular selection route
      return createSelectionRoute<RouterInfo, ShardSelector>(
          std::move(destinations),
          std::move(selector),
          std::move(outOfRangeDestination));
    }
  }

  // Builds vector containing the probabilities
  std::vector<uint16_t> shadowProbabilities;
  detail::ShardDestinationsMapCustomFn<RouterInfo> probabilityBuilderFn =
      [&shadowProbabilities, &shardMap, &shadowWeights](
          uint32_t shardId,
          std::vector<typename RouterInfo::RouteHandlePtr>&
              childrenRouteHandles) {
        auto it = shardMap.find(shardId);
        size_t shardSize = 0;
        if (it != shardMap.end()) {
          // Implementation goes here
          shardSize = it->second.size();
        }
        // Entries in vector are from (0,100] representing % of time that if
        // selected by shadowSelector that request will be shadowed to child
        // route handle.
        uint16_t probability =
            (((uint16_t)(100 * childrenRouteHandles.size() * shadowWeights)) /
             (shardSize + childrenRouteHandles.size()));
        shadowProbabilities.push_back(probability);
      };

  MapType shadowShardToDestinationIndexMap = detail::prepareMap<MapType>(
      shadowShardMap.size(), detail::getMaxShardId(shadowShardMap));
  std::vector<typename RouterInfo::RouteHandlePtr> shadowDestinations;
  if (shadowChildrenType == "LoadBalancerRoute") {
    detail::buildChildrenLoadBalancerRoutes<RouterInfo, MapType>(
        factory,
        shadowChildrenSettings,
        shadowShardMap,
        shadowDestinations,
        shadowShardToDestinationIndexMap,
        probabilityBuilderFn);
  } else if (shadowChildrenType == "LatestRoute") {
    detail::buildChildrenLatestRoutes<RouterInfo, MapType>(
        factory,
        shadowChildrenSettings,
        shadowShardMap,
        shadowDestinations,
        shadowShardToDestinationIndexMap,
        probabilityBuilderFn);
  } else if (shadowChildrenType == "CustomJsonmRoute") {
    throwLogic(
        "EagerShardSelectionShadowRoute: {} not supported", shadowChildrenType);
  } else {
    auto it = shadowChildrenFactoryMap.find(shadowChildrenType.str());
    if (it != shadowChildrenFactoryMap.end()) {
      detail::buildChildrenCustomRoutesFromMap<RouterInfo, MapType>(
          factory,
          shadowChildrenSettings,
          shadowShardMap,
          it->second,
          shadowDestinations,
          shadowShardToDestinationIndexMap,
          probabilityBuilderFn);
    } else {
      throwLogic(
          "EagerShardSelectionRoute: 'shadow_children_type' {} not supported",
          shadowChildrenType);
    }
  }

  ShardSelector shadowSelector(std::move(shadowShardToDestinationIndexMap));
  shadowProbabilities.shrink_to_fit();
  return createSelectionRoute<RouterInfo, ShardSelector, ShadowSelectorPolicy>(
      std::move(destinations),
      std::move(selector),
      std::move(outOfRangeDestination),
      std::move(shadowDestinations),
      std::move(shadowSelector),
      std::move(shadowProbabilities),
      std::move(shadowSelectorPolicy),
      seed);
}