-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Add perFilerConfigObject to store pre-processed per-route-config #3128
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
Conversation
@mattklein123 @rodaine, could you help to review this? Thanks |
include/envoy/router/router.h
Outdated
/** | ||
* All pre-processed per route config objects should be derived from this class. | ||
*/ | ||
class PerRouteConfigObject { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this can be named "PerRouteConfig", or maybe "RouteSpecificConfig". Or "RouteSpecificFilterConfig".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would vote for "RouteSpecificFilterConfig" but I don't feel that strongly about it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now, this is going to be painful isn't it? Every filter will now have two config objects in some sense. a preprocessed per-route thing and a filter level config object. I guess thats okay, given that the perf benefit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Every filter is basically doing this today, and it's entirely optional, so it seems OK to me?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fair enough.
include/envoy/server/filter_config.h
Outdated
*/ | ||
virtual Router::PerRouteConfigObjectConstSharedPtr | ||
createPerRouteConfigObject(const Protobuf::Message& config) { | ||
UNREFERENCED_PARAMETER(config); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style: delete this line, change previous line to createPerRouteConfigObject(const Protobuf::Message&) {
source/common/router/config_impl.cc
Outdated
@@ -895,5 +916,10 @@ const Protobuf::Message* PerFilterConfigs::get(const std::string& name) const { | |||
return cfg == configs_.end() ? nullptr : cfg->second.get(); | |||
} | |||
|
|||
const PerRouteConfigObject* PerFilterConfigs::get_object(const std::string& name) const { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: methods get camelCase, not snake_case
source/common/router/config_impl.cc
Outdated
@@ -895,5 +916,10 @@ const Protobuf::Message* PerFilterConfigs::get(const std::string& name) const { | |||
return cfg == configs_.end() ? nullptr : cfg->second.get(); | |||
} | |||
|
|||
const PerRouteConfigObject* PerFilterConfigs::get_object(const std::string& name) const { | |||
auto obj = objects_.find(name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I'd name this "it" or "iterator"
Router::PerRouteConfigObjectConstSharedPtr | ||
createPerRouteConfigObject(const Protobuf::Message& config) override { | ||
const auto data = dynamic_cast<const ProtobufWkt::Timestamp&>(config); | ||
auto obj = new TestFilterConfigObject; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::make_shared();
include/envoy/router/router.h
Outdated
* the given filter name. If not per-filter config for the filter, or the filter factory | ||
* doesn't create such object, nullptr is returned. | ||
*/ | ||
virtual const PerRouteConfigObject* perFilterConfigObject(const std::string& name) const PURE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should add an additional template method:
template <typename Derived>
const Derived* perFilterConfigObjectTyped(const std::string& name) const {
const PerRouteConfigObject* obj = perFilterConfigObject(name);
if (obj == nullptr) {
return obj;
}
ASSERT(dynamic_cast<const Derived*>(obj) != nullptr);
return static_cast<const Derived*>(obj);
}
Or similar.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah we have basically this for TLS. +1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general love this approach. Thanks @qiwzhang. cc @rodaine @rshriram this will allow you to trivially improve the perf of your filters? Should we wait until this lands?
I actually wonder if we should just kill the other functions and only have this type of config. If the filter just wants the proto they can just store it in a wrapper? WDYT?
include/envoy/router/router.h
Outdated
/** | ||
* All pre-processed per route config objects should be derived from this class. | ||
*/ | ||
class PerRouteConfigObject { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would vote for "RouteSpecificFilterConfig" but I don't feel that strongly about it.
include/envoy/router/router.h
Outdated
* the given filter name. If not per-filter config for the filter, or the filter factory | ||
* doesn't create such object, nullptr is returned. | ||
*/ | ||
virtual const PerRouteConfigObject* perFilterConfigObject(const std::string& name) const PURE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah we have basically this for TLS. +1
source/common/router/config_impl.cc
Outdated
const PerRouteConfigObject* | ||
RouteEntryImplBase::WeightedClusterEntry::perFilterConfigObject(const std::string& name) const { | ||
const PerRouteConfigObject* cfg = per_filter_configs_.get_object(name); | ||
if (cfg != nullptr) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Use ternary operator here. Can you fix other cases of this? I think @rodaine might have another one that I didn't comment on.
As an aside, I don't think this solution will work for Lua which is more complicated. This is because the Lua code must be JITd per-thread and can't be constant global like this data. I will think about about the Lua case but I think we can probably proceed with this as it's blocking a few things. |
@mattklein123 for Lua, could the config object own a TLS slot, and store the Lua state in that? Might have to make this non-const, but that wouldn't substantially change the approach. |
+1 to that. |
include/envoy/router/router.h
Outdated
* the given filter name. If not per-filter config for the filter, or the filter factory | ||
* doesn't create such object, nullptr is returned. | ||
*/ | ||
virtual const PerRouteConfigObject* perFilterConfigObject(const std::string& name) const PURE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IF we are going down the path of merging with perFilterConfig, I would kill this function and change signature of the other..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is nice.. as noted earlier, just change the signature of the existing function (perFilterConfig) to return the wrapper object you have instead of proto message.
yeah +1 to @rshriram suggestion. We can hold on the fault/buffer PRs until we finish this.
Yes, that should work. The only downside to that approach is we would need a TLS slot per route which might be inefficient in the case where we have lots of routes, but, I think that's probably an edge case. IMO I would just leave the signature as const for now since the wrapper really does have to be const. If the implementation does non-const/mutable things under the hood in special cases that's probably OK... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome! Agreed with @rshriram and @mattklein123, let's just replace the existing perFilterConfig methods.
include/envoy/router/router.h
Outdated
* the given filter name. If not per-filter config for the filter, or the filter factory | ||
* doesn't create such object, nullptr is returned. | ||
*/ | ||
virtual const PerRouteConfigObject* perFilterConfigObject(const std::string& name) const PURE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
Thanks for the review. Here is my plan:
Router::RouteSpecificFilterConfigConstSharedPtr
templated function as Thanks |
PTAL |
I will review tomorrow, but @ggreenway @rshriram I don't think this by itself will work for Lua. The reason being that in order to use TLS and more complicated things, the factory will need access to the FactoryContext. @ggreenway this bring us back full circle and I think we will need to provide the full FactoryContext all the way to the route config, or think of some other cleaner callback way of getting it in. I would still suggest we just get this in as is and we can figure out the Lua (and maybe WebSocket case after). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, just some small nits. I think we can sort out the Lua case later.
include/envoy/router/router.h
Outdated
*/ | ||
virtual const Protobuf::Message* perFilterConfig(const std::string& name) const PURE; | ||
template <class Derived> const Derived* perFilterConfigTyped(const std::string& name) const { | ||
const RouteSpecificFilterConfig* obj = perFilterConfig(name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This entire function can be simplified to:
return dynamic_cast<const Derived*>(perFilterConfig(name));
We need to do the real dynamic_cast for correctness, and it's fine to dynamic_cast nullptr. Same for the other spots.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
include/envoy/router/router.h
Outdated
* filter name configured for this virtual host. If none is present, | ||
* nullptr is returned. | ||
* @return const RouteSpecificFilterConfig* the per-filter config pre-processed object for | ||
* the given filter name. If not per-filter config for the filter, or the filter factory |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"If there is no per-filter config..." (same below)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
include/envoy/router/router.h
Outdated
* nullptr is returned. | ||
* @return const RouteSpecificFilterConfig* the per-filter config pre-processed object for | ||
* the given filter name. If not per-filter config for the filter, or the filter factory | ||
* doesn't create such object, nullptr is returned. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"or the filter factory returns nullptr, ..." (same below)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
include/envoy/server/filter_config.h
Outdated
* empty proto. By default, this method returns the same value as createEmptyConfigProto, | ||
* and can be optionally overridden in implementations. | ||
* @return RouteSpecificFilterConfigConstSharedPtr allow the filter to pre-process per route | ||
* config. Returned object will be stored in the filter chain config. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"will be stored in the loaded route configuration."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
include/envoy/server/filter_config.h
Outdated
return createEmptyConfigProto(); | ||
virtual Router::RouteSpecificFilterConfigConstSharedPtr | ||
createRouteSpecificFilterConfig(const ProtobufWkt::Struct&) { | ||
return Router::RouteSpecificFilterConfigConstSharedPtr{}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: return {}
or return nullptr
should work
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
Signed-off-by: Wayne Zhang <qiwzhang@google.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks. @ggreenway if this works for you can you approve/merge?
Question: can we get similar functionality for tcp ? |
I don’t know what gets carried through from SNI filter chain match. |
@rshriram for tcp, routes will be defined by the FilterChainMatch, so each route will have a completely independent filter-chain (with config) anyways, so no additional work will be needed. |
virtual ProtobufTypes::MessagePtr createEmptyRouteConfigProto() { | ||
return createEmptyConfigProto(); | ||
virtual Router::RouteSpecificFilterConfigConstSharedPtr | ||
createRouteSpecificFilterConfig(const ProtobufWkt::Struct&) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would strongly -1 to exposing Struct to filter interface and in favor of wrapping them in an interface like #3109 did. Hiding Struct from filter interface is important when we see scaling issues with the config and want to migrate to Any (via oneof) in future, as mentioned in https://github.com/envoyproxy/data-plane-api/issues/540#issuecomment-373250850 . cc @htuch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1000
Description:
Add a new function in filter_factory to allow filter to generate a pre-processed object for per-filter-config.
In VHost, Route, and RouteEntry, add a new function perFilterConfigObject to retrieve this pre-processed object.
Risk Level: Low