runtime: changing runtime features to be ABSL_FLAGs based#19880
runtime: changing runtime features to be ABSL_FLAGs based#19880alyssawilk merged 17 commits intoenvoyproxy:mainfrom
Conversation
Signed-off-by: Alyssa Wilk <[email protected]>
Signed-off-by: Alyssa Wilk <[email protected]>
Signed-off-by: Alyssa Wilk <[email protected]>
Signed-off-by: Alyssa Wilk <[email protected]>
Signed-off-by: Alyssa Wilk <[email protected]>
adisuissa
left a comment
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.")) { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
| quiche_flags_override[it.first.substr(quiche::EnvoyFeaturePrefix.length())] = | ||
| it.second.bool_value_.value(); | ||
| } | ||
| if (it.second.bool_value_.has_value() && isRuntimeFeature(it.first)) { |
There was a problem hiding this comment.
Again for the exceptional case where someone sets a non-conventional runtime flag by RTDS, we should probably have an ENVOY_BUG here.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
|
BTW: will it be possible to add the absl flags usage example into test/common/runtime/BUILD? |
Signed-off-by: Alyssa Wilk <[email protected]>
alyssawilk
left a comment
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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.")) { |
There was a problem hiding this comment.
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?
| quiche_flags_override[it.first.substr(quiche::EnvoyFeaturePrefix.length())] = | ||
| it.second.bool_value_.value(); | ||
| } | ||
| if (it.second.bool_value_.has_value() && isRuntimeFeature(it.first)) { |
There was a problem hiding this comment.
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.
|
"BTW: will it be possible to add the absl flags usage example into test/common/runtime/BUILD?" |
Signed-off-by: Alyssa Wilk <[email protected]>
Signed-off-by: Alyssa Wilk <[email protected]>
|
CC @envoyproxy/dependency-shepherds: Your approval is needed for changes made to |
|
One line patch change /lgtm deps |
Signed-off-by: Alyssa Wilk <[email protected]>
Signed-off-by: Alyssa Wilk <[email protected]>
Signed-off-by: Alyssa Wilk <[email protected]>
Signed-off-by: Alyssa Wilk <[email protected]>
adisuissa
left a comment
There was a problem hiding this comment.
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()) { |
There was a problem hiding this comment.
nit: consider adding:
if (!value.has_value()) {
IS_ENVOY_BUG("Feature not found in ABSL flags...");
}
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
yeah all we need to do is set the flags back between unit tests
|
@envoyproxy/senior-maintainers assignee is @mattklein123 |
Signed-off-by: Alyssa Wilk <[email protected]>
Signed-off-by: Alyssa Wilk <[email protected]>
Signed-off-by: Alyssa Wilk <[email protected]>
Signed-off-by: Alyssa Wilk <[email protected]>
Signed-off-by: Alyssa Wilk <[email protected]>
|
CI sorted, ptal! |
adisuissa
left a comment
There was a problem hiding this comment.
Overall LGTM, left a question about the integer defaults.
| } | ||
| } | ||
|
|
||
| void RuntimeFeatures::restoreDefaults() const { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Nvm, my bad... I missed lines 66-72 in this file where these are initialized.
Thanks!
mattklein123
left a comment
There was a problem hiding this comment.
At a high level LGTM! One question about upstreaming the absl path.
|
|
||
|
|
||
| // ABSL_OPTION_USE_INLINE_NAMESPACE | ||
| diff --git a/absl/flags/commandlineflag.h b/absl/flags/commandlineflag.h |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
Just so I understand this is now being used for all refresh, right? So it's no longer H3 specific?
There was a problem hiding this comment.
correct, that's why I took quiche out of the name :-)
| quiche_flags_override[it.first.substr(quiche::EnvoyFeaturePrefix.length())] = | ||
| it.second.bool_value_.value(); | ||
| } | ||
| if (it.second.bool_value_.has_value() && isRuntimeFeature(it.first)) { |
There was a problem hiding this comment.
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?
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