Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runtime: changing runtime features to be ABSL_FLAGs based #19880

Merged
merged 17 commits into from
Feb 17, 2022

Conversation

alyssawilk
Copy link
Contributor

@alyssawilk alyssawilk commented Feb 8, 2022

Note: this does NOT remove the runtime singleton, which will be done in a follow-up PR.

Risk Level: high
Testing: existing tests cover
Docs Changes: n/a
Release Notes:
Fixes #19847
Part of envoyproxy/envoy-mobile#2003

@repokitteh-read-only
Copy link

As a reminder, PRs marked as draft will not be automatically assigned reviewers,
or be handled by maintainer-oncall triage.

Please mark your PR as ready when you want it to be reviewed!

🐱

Caused by: #19880 was opened by alyssawilk.

see: more, trace.

@alyssawilk alyssawilk changed the title Runtime runtime: changing runtime features to be ABSL_FLAGs based Feb 9, 2022
@alyssawilk alyssawilk marked this pull request as ready for review February 9, 2022 14:48
Signed-off-by: Alyssa Wilk <[email protected]>
Signed-off-by: Alyssa Wilk <[email protected]>
Copy link
Contributor

@adisuissa adisuissa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this.
Overall looks good.
Left a few comments. My concern is regarding some non-conventional usage of the previous Runtime Layer by extensions that I think could be broken by this.

#include "absl/strings/match.h"
#include "absl/strings/str_replace.h"

/* To add a runtime guard to Envoy, add 2 lines to this file.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could find a way to fetch the number of abseil flags, but if there is one, I suggest to make this constraint in code.
Specifically, adding a RELEASE_ASSERT in RuntimeFeatures c'tor to validate that the number of abseil flags equals to the size of runtime_features + size of non-bool flags.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that's necessary? If you add a runtime flag and only add it to the top, then when you try to access it you'll fail the ENVOY_BUG in runtimeFeatureEnabled so all existing tests going near that code path will hard fail. Meanwhile if we add that check it'll 100% break import so I'd prefer to stick with the ENVOY_BUG :-)

if (Runtime::LoaderSingleton::getExisting()) {
return Runtime::LoaderSingleton::getExisting()->threadsafeSnapshot()->getInteger(
std::string(feature), default_value);
if (!absl::StartsWith(feature, "envoy.")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking out loud here. Technically, an extension can use this method, and fetch a value that is set by RTDS.
I'm pretty confident no one does it, but if they do, should we have an ENVOY_BUG or log a warning here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good call, refactored to catch both cases and done. Alternately I could also rename to getDeprecatedInteger and break folks' compile if they use it - think that's worth doing?

source/common/runtime/runtime_features.cc Show resolved Hide resolved
absl::flat_hash_map<std::string, bool> quiche_flags_override;
for (const auto& it : flag_map) {
if (absl::StartsWith(it.first, quiche::EnvoyQuicheReloadableFlagPrefix) &&
it.second.bool_value_.has_value()) {
quiche_flags_override[it.first.substr(quiche::EnvoyFeaturePrefix.length())] =
it.second.bool_value_.value();
}
if (it.second.bool_value_.has_value() && isRuntimeFeature(it.first)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again for the exceptional case where someone sets a non-conventional runtime flag by RTDS, we should probably have an ENVOY_BUG here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry you're saying if someone sets a boolean in their own code base envoy.reloadable_flag.not_in_runtime_features? Their code would crash on lookup any time they tried to reference the value so I'm not convinced anyone would compile that in but I can add a check regardless if you think it's worth it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe my understanding of the previous runtime and loader is incorrect.
I thought that envoy.reloadable_flag.not_in_runtime_features can be set by RTDS (or more specifically when a new snapshot is created), and it can be read by any extension, no?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes someone can set a random name, and it could be read from the snapshot, which is fine. It will only refresh a flag if the flag exists. I'm not sure what Alyssa's crashing comment is referring to?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Adi was asking if previously we supported a non-envoy-registered envoy.reloadable_feature being set via runtime config and fetched via runtimeFeatureEnabled(). It was not supported previously - you can set it in RTDS but runtimeFeatureEnabled checked that Envoy knew about it so you could only access it via explicit runtime checks. I think basically we have support parity here.

@adisuissa
Copy link
Contributor

BTW: will it be possible to add the absl flags usage example into test/common/runtime/BUILD?

Signed-off-by: Alyssa Wilk <[email protected]>
Copy link
Contributor Author

@alyssawilk alyssawilk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW //test/server/config_validation:xds_fuzz_test still fails - will debug tomorrow

#include "absl/strings/match.h"
#include "absl/strings/str_replace.h"

/* To add a runtime guard to Envoy, add 2 lines to this file.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that's necessary? If you add a runtime flag and only add it to the top, then when you try to access it you'll fail the ENVOY_BUG in runtimeFeatureEnabled so all existing tests going near that code path will hard fail. Meanwhile if we add that check it'll 100% break import so I'd prefer to stick with the ENVOY_BUG :-)

if (Runtime::LoaderSingleton::getExisting()) {
return Runtime::LoaderSingleton::getExisting()->threadsafeSnapshot()->getInteger(
std::string(feature), default_value);
if (!absl::StartsWith(feature, "envoy.")) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good call, refactored to catch both cases and done. Alternately I could also rename to getDeprecatedInteger and break folks' compile if they use it - think that's worth doing?

source/common/runtime/runtime_features.cc Show resolved Hide resolved
absl::flat_hash_map<std::string, bool> quiche_flags_override;
for (const auto& it : flag_map) {
if (absl::StartsWith(it.first, quiche::EnvoyQuicheReloadableFlagPrefix) &&
it.second.bool_value_.has_value()) {
quiche_flags_override[it.first.substr(quiche::EnvoyFeaturePrefix.length())] =
it.second.bool_value_.value();
}
if (it.second.bool_value_.has_value() && isRuntimeFeature(it.first)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry you're saying if someone sets a boolean in their own code base envoy.reloadable_flag.not_in_runtime_features? Their code would crash on lookup any time they tried to reference the value so I'm not convinced anyone would compile that in but I can add a check regardless if you think it's worth it.

@alyssawilk
Copy link
Contributor Author

"BTW: will it be possible to add the absl flags usage example into test/common/runtime/BUILD?"
I don't think so. we're not parsing command line flags, just using them for storage.

@repokitteh-read-only repokitteh-read-only bot added the deps Approval required for changes to Envoy's external dependencies label Feb 10, 2022
@repokitteh-read-only
Copy link

CC @envoyproxy/dependency-shepherds: Your approval is needed for changes made to (bazel/.*repos.*\.bzl)|(bazel/dependency_imports\.bzl)|(api/bazel/.*\.bzl)|(.*/requirements\.txt)|(.*\.patch).
envoyproxy/dependency-shepherds assignee is @wrowe

🐱

Caused by: #19880 was synchronize by alyssawilk.

see: more, trace.

@moderation
Copy link
Contributor

One line patch change

/lgtm deps

@repokitteh-read-only repokitteh-read-only bot removed the deps Approval required for changes to Envoy's external dependencies label Feb 10, 2022
Signed-off-by: Alyssa Wilk <[email protected]>
Signed-off-by: Alyssa Wilk <[email protected]>
Signed-off-by: Alyssa Wilk <[email protected]>
adisuissa
adisuissa previously approved these changes Feb 16, 2022
Copy link
Contributor

@adisuissa adisuissa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, LGTM modulo a nit.
/assign-from @envoyproxy/senior-maintainers

std::string envoy_name = swapPrefix(std::string(reflection.Name()));
all_features_.emplace(envoy_name, feature);
absl::optional<bool> value = reflection.TryGet<bool>();
if (value.has_value() && value.value()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: consider adding:

if (!value.has_value()) {
  IS_ENVOY_BUG("Feature not found in ABSL flags...");
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added an assert - everything in that struct has to be a boolean by definition so really the value check is silly.

absl::StrCat("MainThreadLeak: [", test_info.test_suite_name(), ".",
test_info.name(), "] test exited before main thread shut down"));
}
Runtime::RuntimeFeaturesDefaults::get().restoreDefaults();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting...
My initial thoughts were that this wouldn't work, because some tests will have a Scope defined as a class member, and override a runtime value in the c'tor, but if CI is green pass I guess that's ok.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah all we need to do is set the flags back between unit tests

test/test_runner.cc Show resolved Hide resolved
@repokitteh-read-only
Copy link

@envoyproxy/senior-maintainers assignee is @mattklein123

🐱

Caused by: a #19880 (review) was submitted by @adisuissa.

see: more, trace.

Signed-off-by: Alyssa Wilk <[email protected]>
Signed-off-by: Alyssa Wilk <[email protected]>
Signed-off-by: Alyssa Wilk <[email protected]>
@alyssawilk
Copy link
Contributor Author

CI sorted, ptal!

Copy link
Contributor

@adisuissa adisuissa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall LGTM, left a question about the integer defaults.

}
}

void RuntimeFeatures::restoreDefaults() const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the calls to this method are done when ending tests/scope, however I don't see where the default values for the integer-based flags are set.
What am I missing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just using the same values from where they're defined, which I snagged from the defaults to the getInteger code in call sites.
It's really inelegant but per the TODO I think I can get rid of most if not all of these so hopefully it won't be inelegant long.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nvm, my bad... I missed lines 66-72 in this file where these are initialized.
Thanks!

Copy link
Contributor

@adisuissa adisuissa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At a high level LGTM! One question about upstreaming the absl path.

@@ -40,3 +40,16 @@ index 230bf1e..6e1b9e5 100644


// ABSL_OPTION_USE_INLINE_NAMESPACE
diff --git a/absl/flags/commandlineflag.h b/absl/flags/commandlineflag.h
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this being upstreamed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm negotiating with the absl team about that or pragma changes yep

// TODO(12923): Document the Quiche reloadable flag setup.
#ifdef ENVOY_ENABLE_QUIC
void refreshQuicheReloadableFlags(const Snapshot::EntryMap& flag_map) {
void refreshReloadableFlags(const Snapshot::EntryMap& flag_map) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just so I understand this is now being used for all refresh, right? So it's no longer H3 specific?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

correct, that's why I took quiche out of the name :-)

absl::flat_hash_map<std::string, bool> quiche_flags_override;
for (const auto& it : flag_map) {
if (absl::StartsWith(it.first, quiche::EnvoyQuicheReloadableFlagPrefix) &&
it.second.bool_value_.has_value()) {
quiche_flags_override[it.first.substr(quiche::EnvoyFeaturePrefix.length())] =
it.second.bool_value_.value();
}
if (it.second.bool_value_.has_value() && isRuntimeFeature(it.first)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes someone can set a random name, and it could be read from the snapshot, which is fine. It will only refresh a flag if the flag exists. I'm not sure what Alyssa's crashing comment is referring to?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

remove global runtime: rebase runtime features on ABSL_flags
5 participants