Skip to content

Commit

Permalink
Mcrouter: support nested key_fractions from runtime_vars config
Browse files Browse the repository at this point in the history
Summary:
as part of [Mcrouter Runtime Variables Safety project](https://docs.google.com/document/d/1sC7CDHKn7NuemYxtr4x8WCXvr1DHa1bbO-JllwsRNhI/edit?tab=t.0), a new schema for runtime_vars is defined:
```
struct RuntimeVars {
  1: list<DynamicSampler> dynamic_samplers;
  2: map<string, map<string, PrefixOverride>> prefix_overrides;
  // mcrouter key fractions: tier name -> fraction of keyspace to sample
  3: map<string, float> key_fractions;
}
```

existing `key_fraction_range_` are listed at the root-level of `/configerator-gz/mcrouter/web/runtime_vars.json`, which are not compatible with thrift config
{F1974385271}

To allow thrift compatible schema, key are moved under `key_fractions`

This diff adds support for consuming KVRs from key_fractions vs from root level of runtime_vars.json

example:
```
{
  "dynamic_samplers": [],
  "prefix_overrides": {},
  "key_fractions": {
    "ucache.shadow.ucache_gateway_proxy_ftw": 0.01234,
  },
  "key_fraction_range_ucache.shadow.ucache_gateway_proxy_ftw": [0, 0.01234]
}
```

Reviewed By: disylh

Differential Revision: D68250145

fbshipit-source-id: 222ab9012a2591a0973911f9897419f706fd26b7
  • Loading branch information
alfozan authored and facebook-github-bot committed Jan 29, 2025
1 parent 5cedcd9 commit 2c429dc
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 23 deletions.
59 changes: 40 additions & 19 deletions mcrouter/routes/ShadowSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,25 +128,46 @@ void ShadowSettings::registerOnUpdateCallback(
if (!newVars || keyFractionRangeRv_.empty()) {
return;
}
auto val = newVars->getVariableByName(keyFractionRangeRv_);
if (val != nullptr) {
checkLogic(
val.isArray(),
"runtime vars: {} is not an array",
keyFractionRangeRv_);
checkLogic(
val.size() == 2,
"runtime vars: size of {} is not 2",
keyFractionRangeRv_);
checkLogic(
val[0].isNumber(),
"runtime vars: {}#0 is not a number",
keyFractionRangeRv_);
checkLogic(
val[1].isNumber(),
"runtime vars: {}#1 is not a number",
keyFractionRangeRv_);
setKeyRange(val[0].asDouble(), val[1].asDouble());

// read value from "key_fractions" object
auto keyFractions = newVars->getVariableByName("key_fractions");
if (keyFractions != nullptr && keyFractions.isObject()) {
std::string_view tiername{keyFractionRangeRv_};
if (tiername.starts_with("key_fraction_range_")) {
tiername.remove_prefix(19);
}

auto val = keyFractions.get_ptr(tiername);
if (val != nullptr) {
checkLogic(
val->isNumber(),
"runtime vars: {} fraction is not a number",
tiername);
setKeyRange(0.0, val->asDouble());
}

} else {
// fallback to reading KFR from the root-level variables
auto val = newVars->getVariableByName(keyFractionRangeRv_);
if (val != nullptr) {
checkLogic(
val.isArray(),
"runtime vars: {} is not an array",
keyFractionRangeRv_);
checkLogic(
val.size() == 2,
"runtime vars: size of {} is not 2",
keyFractionRangeRv_);
checkLogic(
val[0].isNumber(),
"runtime vars: {}#0 is not a number",
keyFractionRangeRv_);
checkLogic(
val[1].isNumber(),
"runtime vars: {}#1 is not a number",
keyFractionRangeRv_);
setKeyRange(val[0].asDouble(), val[1].asDouble());
}
}
});
}
Expand Down
57 changes: 53 additions & 4 deletions mcrouter/routes/test/ShadowSettingsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,15 @@
* LICENSE file in the root directory of this source tree.
*/

#include <memory>

#include <gtest/gtest.h>

#include <folly/Range.h>
#include <folly/json/json.h>

#include "configerator/distribution/api/ScopedConfigeratorFake.h"
#include "mcrouter/CarbonRouterInstance.h"
#include "mcrouter/facebook/FbConfigApi.h"
#include "mcrouter/lib/network/gen/MemcacheRouterInfo.h"
#include "mcrouter/options.h"
#include "mcrouter/routes/ShadowSettings.h"

using namespace facebook::memcache;
Expand All @@ -29,7 +28,18 @@ class ShadowSettingsTest : public ::testing::Test {
folly::to<std::string>("TestRouter:", kRouterInfoName);
auto router =
CarbonRouterInstance<RouterInfo>::init(kInstanceName, getOpts());
CHECK(router != nullptr) << "router shouldn't be nullptr";
CHECK_NE(router, nullptr) << "router shouldn't be nullptr";
return *router;
}

template <class RouterInfo>
CarbonRouterInstance<RouterInfo>& getRouter(
const McrouterOptions& opts) const {
constexpr folly::StringPiece kRouterInfoName(RouterInfo::name);
const std::string kInstanceName =
folly::to<std::string>("TestRouter:", kRouterInfoName);
auto router = CarbonRouterInstance<RouterInfo>::init(kInstanceName, opts);
CHECK_NE(router, nullptr) << "router shouldn't be nullptr";
return *router;
}

Expand Down Expand Up @@ -173,3 +183,42 @@ TEST_F(ShadowSettingsTest, shouldRouteByBucket) {
i >= 19 && i <= 59);
}
}

TEST_F(ShadowSettingsTest, keyFractionRangeRV) {
// configure RuntimeVarsFile
facebook::configerator::ScopedConfigeratorFake configeratorFake;

constexpr folly::StringPiece kRuntimeVarsFile = "runtimeVarsDummy";

std::string rv_vars_configs = R"(
{
"key_fractions": {
"ucache.test": 0.1
}
}
)";
configeratorFake.setConfig(kRuntimeVarsFile.str(), rv_vars_configs);

// configure router
auto opts = defaultTestOptions();
opts.config = "{ \"route\": \"NullRoute\" }";
opts.runtime_vars_file =
FbConfigApi::kConfigeratorPrefix + kRuntimeVarsFile.str();

auto& router = getRouter<MemcacheRouterInfo>(opts);

constexpr folly::StringPiece kConfig = R"(
{
"key_fraction_range_rv": "key_fraction_range_ucache.test"
}
)";
const auto json = folly::parseJson(kConfig);

// create shadow settings
auto shadowSettings = ShadowSettings::create(json, router);
ASSERT_TRUE(shadowSettings != nullptr);

auto keyFraction = std::get<1>(shadowSettings->keyRange());
uint32_t expectedKeyFraction = 0.1 * std::numeric_limits<uint32_t>::max();
ASSERT_TRUE(keyFraction == expectedKeyFraction);
}

0 comments on commit 2c429dc

Please sign in to comment.