rds: split subscription out from RdsRouteConfigProviderImpl#3960
rds: split subscription out from RdsRouteConfigProviderImpl#3960lizan merged 6 commits intoenvoyproxy:masterfrom
Conversation
Signed-off-by: Lizan Zhou <zlizan@google.com>
|
Thanks to integration test added by @qiwzhang (#3955) and @PiotrSikora (#3957). This PR is still WIP: need cleanup interface between Let me know if you think this is a right approach. @htuch @mattklein123 @PiotrSikora @qiwzhang @mandarjog |
|
|
||
| Router::ConfigConstSharedPtr RdsRouteConfigProviderImpl::config() { | ||
| return tls_->getTyped<ThreadLocalConfig>().config_; | ||
| route_config_provider_manager_.route_config_subscriptions_.erase(manager_identifier_); |
There was a problem hiding this comment.
how do you avoid the race here?
an updated lister's rdsrouteconfig object is being deleted and tries to tell the routeconfig manager that it should be removed..
At the same time, an RDS update arrives and the subscription manager is updating all the routeconfig objects.
There was a problem hiding this comment.
There is not race. ProviderImpl and RdsSubscription are separated. ProviderImpl is tightly associated with ListenerImpl, one per one. But RdsSubscription is shared, across ListenerImpl, the ones with different names, or old one with the new one with config change. A freed ProviderImpl will not free RdsSubscription if it is shared by others.
BTW, here provider_manager should be called subscripton_manager.
There was a problem hiding this comment.
BTW, here provider_manager should be called subscripton_manager.
provider_manager still manage providers (both static and rds), it doesn't own each rds provider but manages them through subscriptions.
|
@lizan I think this approach should work. This is the smallest change I can think of to fix this crash. BTW, provider should be changed to use unique_ptr instead of shared_ptr. |
|
I think eventually we will need to break FactoryContext into small pieces and locking them down, but for now this should be good. shared_ptr is used for a couple reasons (weak_ptr, config_dump), I will see if it can be unique_ptr as follow up. |
Signed-off-by: Lizan Zhou <zlizan@google.com>
Signed-off-by: Lizan Zhou <zlizan@google.com>
Signed-off-by: Lizan Zhou <zlizan@google.com>
Signed-off-by: Lizan Zhou <zlizan@google.com>
| : subscription_(std::move(subscription)), factory_context_(factory_context), | ||
| tls_(factory_context.threadLocal().allocateSlot()) { | ||
| ConfigConstSharedPtr initial_config; | ||
| if (subscription_->config_info_.has_value()) { |
There was a problem hiding this comment.
can we define a getter function? not to use subscription member directly.
| tls_->set([initial_config](Event::Dispatcher&) -> ThreadLocal::ThreadLocalObjectSharedPtr { | ||
| return std::make_shared<ThreadLocalConfig>(initial_config); | ||
| }); | ||
| subscription_->route_config_providers_.insert(this); |
There was a problem hiding this comment.
Subscription defines a OnUpdate interface, it only stores the interface pointers.
It should be
subscription_->add_update_callback(this);
There was a problem hiding this comment.
No it won't work, you will need a pointer to RdsRouteConfigProviderImpl to implement std::vector<RouteConfigProviderSharedPtr> getRdsRouteConfigProviders() in RouteConfigProviderManagerImpl
All the onUpdate is only used in rds_impl and should not be used elsewhere, would rather keep this in private.
| // of this code, locking the weak_ptr will not fail. | ||
| Router::RouteConfigProviderSharedPtr new_provider = it->second.lock(); | ||
| ASSERT(new_provider); | ||
| Router::RouteConfigProviderSharedPtr new_provider{ |
There was a problem hiding this comment.
It will involving interface changes and make them consistent with StaticRouteConfigProviders, and update HttpConnectionManager too.
There was a problem hiding this comment.
IMHO, it is important to use unique_ptr to show Provider object is not shared, only HttpConnectionManager owns it.
| std::unordered_set<RdsRouteConfigProviderImpl*> route_config_providers_; | ||
|
|
||
| friend class RouteConfigProviderManagerImpl; | ||
| friend class RdsRouteConfigProviderImpl; |
There was a problem hiding this comment.
can we not use friend class? define some getter functions.
There was a problem hiding this comment.
What is the point of define more getter functions? No other classes should access them via getter than friend classes listed here.
| SystemTime last_updated_; | ||
| absl::optional<LastConfigInfo> config_info_; | ||
| envoy::api::v2::RouteConfiguration route_config_proto_; | ||
| std::unordered_set<RdsRouteConfigProviderImpl*> route_config_providers_; |
There was a problem hiding this comment.
Not to store the whole provider object, define OnUpdate interface, only store the interface pointers.
There was a problem hiding this comment.
ditto above, you need pointer to ProviderImpl to get shared_ptr of it.
There was a problem hiding this comment.
This is what I mean:
class RdsSubscriptionCallback {
public:
virtual onConfigUpdate() PURE;
};
Here RdsConfigSubscription uses it as:
std::unordered_set<RdsSubscriptionCallback*> update_callbacks_;
ProviderImpl will also implement such Callback
There was a problem hiding this comment.
Yes I know what you meant, then you won't be able to do https://github.com/lizan/envoy/blob/6d433fef72eebc37b63acef63fd492cbbb9ff659/source/common/router/rds_impl.cc#L204 without dynamic_cast.
It doesn't make sense to make an interface which is completely implementation detail.
source/common/router/rds_impl.h
Outdated
| const std::string route_config_name_; | ||
|
|
||
| friend class RouteConfigProviderManagerImpl; | ||
| friend class RdsRouteConfigSubscription; |
There was a problem hiding this comment.
Why does Subscription need to access ProviderImpl private members?
There was a problem hiding this comment.
mostly for for onConfigUpdate since no class other than Subscription should call it. will make it public and remove friend here.
There was a problem hiding this comment.
If we use SubscriptionCallback interface, this friend class will not be needed.
|
Talked to @lizan offline. This approach/fix LGTM. Please let me know when the rest of the comments are finished and I can take a look. Thank you for fixing! |
| sendDiscoveryResponse<envoy::api::v2::RouteConfiguration>( | ||
| Config::TypeUrl::get().RouteConfiguration, {buildRouteConfig("route_config_0", "cluster_0")}, | ||
| "2"); | ||
| } |
There was a problem hiding this comment.
can we make "makeSingleRequest()" call here
| sendDiscoveryResponse<envoy::api::v2::RouteConfiguration>( | ||
| Config::TypeUrl::get().RouteConfiguration, {buildRouteConfig("route_config_0", "cluster_1")}, | ||
| "2"); | ||
| } |
There was a problem hiding this comment.
can we add "makeSingleRequest()" call here to make sure it works?
May have to wait for rds update success.
Signed-off-by: Lizan Zhou <zlizan@google.com>
|
LGTM for the quick fix. Maybe a follow up PR to do the follow clean up:
|
|
@mattklein123 and/or @htuch PTAL. @qiwzhang I'm looking at how large it will be to do unique_ptr, within or not in this PR. Though not convinced that we need to do 2. |
htuch
left a comment
There was a problem hiding this comment.
LGTM; nice find and fix to all involved.
mattklein123
left a comment
There was a problem hiding this comment.
LGTM, thanks for fixing this.
Signed-off-by: Lizan Zhou zlizan@google.com
Description:
RdsRouteConfigProviderImplholds reference toFactoryContext(implemented byListenerImpl), which could be freed after LDS update (#3953), this PR split subscription part out and makeRdsRouteConfigProviderImplowned by each Listener. The subscription class holds references to all live providers to provide RDS update.Risk Level: Low
Testing: unit test, integration test
Docs Changes:
Release Notes:
Fixes #3953