-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
xds: share rds configuration among envoy #8523
Conversation
This pull request has been automatically marked as stale because it has not had activity in the last 7 days. It will be closed in 7 days if no further activity occurs. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions! |
Update: dependency is almost done. Will rebase now and then |
Signed-off-by: Yuchen Dai <silentdai@gmail.com>
Signed-off-by: Yuchen Dai <silentdai@gmail.com>
Signed-off-by: Yuchen Dai <silentdai@gmail.com>
Signed-off-by: Yuchen Dai <silentdai@gmail.com>
/assign @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.
can you use the new(ish) config-provider framework, I think they are doing the same thing. With the ServerFactoryContext PR merged, I think there is no blocker to migrate this into the config-provider framework.
Thanks @stevenzzzz for the quick review! Will update soon |
Signed-off-by: Yuchen Dai <silentdai@gmail.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.
Hi @stevenzzzz I'd like to use the new provider in the follow up PR(s), making the future PR a pure migration. WDYT?
subscription = it->second.lock(); | ||
auto existing_provider = it->second.lock(); | ||
RELEASE_ASSERT(existing_provider != nullptr, ""); | ||
init_manager.add(existing_provider->subscription_->init_target_); |
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.
shared target is used here
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.
How do you know that existing_provider->subscription_->init_target_
hasn't already initialized?
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.
InitManager doesn't need to know. Manager will be notified after Manager::initialize(), no matter target is initialized or not at this line.
The Target impl (which is SharedTargetImpl) maintains the internal states.
If the target need to be initialized only after all managers call initialze(): This behavior should be achieved by another instance of InitManager IMHO :)
Signed-off-by: Yuchen Dai <silentdai@gmail.com>
@lambdai if your PR and the shared config provider do the same thing, then why do this PR? I'd prefer we don't review and merge code that is going to be throw away. |
@htuch I am not sure what config provider you are referring to.
|
@lambdai I'm talking about the base classes of |
ScopedRdsConfigProvider derives from I don't see how If you are referring that rds config user should rely on a |
@lambdai you are correct that The point made above is that It might be simple to "just use a shared pointer", but there are subtleties around this that we explored in depth in those PRs, specifically, what happens when a shared pointer gets destructed on a worker thread vs. main thread, when it happens to be the last thread standing. Also, I'd want to understand why the same init concerns that exist here don't also exist for SRDS. Can you sync with @stevenzzzz and come up with a common pattern/framework that works for both SRDS and RDS? RDS can be referenced by multiple HCMs and SRDS, similarly SRDS might be referenced by multiple HCMs. |
I admit that I didn't take full care of destruction path. My understanding is that my change affect only the lifetime of RDSProvider in master thread and the new TLS magic should work with the new provider.
The bug must exist in SRDS. My observation is that since SRDS delegate the RdsConfigProvider creation to |
There was a previous hard-failure assertion in TLS slot destruction, which limits the destruction of SlotImpl can only happen in mainThread, it's relaxed now I added the "Bookeeper" into the thread_local_impl.h to solve the life-cycle problem of RDS Provider may gets removed by LDS while there are active lambdas in workers dispatcher queue pointing to the provider (which cause undefined behaviors). If we are talking about that, I think it's resolved now. (for RDS and SRDS only).
Once that's done. I think we can ignore this PR and move to the config-provider framework, which instead of sharing a config provider(which contains a configImpl) across listeners, shares a subscription (just like the current RDS, but contains a config impl now) across RDS providers. |
Talked to @stevenzzzz offline
Steps:
|
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.
@lambdai this seems like a reasonable plan. Can you update the PR description to make it clear that the main contribution here is the shared init manager?
Re: shared init manager. I'm still trying to persuade myself this is the right thing. It's simpler than adding a pub-sub or message bus to Envoy, but it also just generally makes reasoning about init watches and handlers that much more complicated. CC @mergeconflict who is the world premier expert on our init manager :)
source/common/router/rds_impl.cc
Outdated
} else { | ||
// Because the RouteConfigProviderManager's weak_ptrs only get cleaned up | ||
// in the RdsRouteConfigSubscription destructor, and the single threaded nature | ||
// of this code, locking the weak_ptr will not fail. | ||
subscription = it->second.lock(); | ||
auto existing_provider = it->second.lock(); | ||
RELEASE_ASSERT(existing_provider != 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.
This should be a regular ASSERT
I think.
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 line below the existing_provider is the deref. Instead of a nullptr deref I prefer to throw a message here. I added a detailed error message in the follow commit.
There is no penalty if the condition is true.
subscription = it->second.lock(); | ||
auto existing_provider = it->second.lock(); | ||
RELEASE_ASSERT(existing_provider != nullptr, ""); | ||
init_manager.add(existing_provider->subscription_->init_target_); |
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.
How do you know that existing_provider->subscription_->init_target_
hasn't already initialized?
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've just reviewed the SharedTarget
portion of this PR. I guess there's a real use case for registering an init target with multiple init managers, so I think I'm ok with this, but I should point out that there are two choices you could have made here and neither is obviously "more correct" than the other:
- What was done here: the target initializes as soon as any of the managers tell it to.
- The other option: the target waits to initialize until all of the managers tell it to.
Maybe there are more options I didn't think of... My point is that there's sort of a rabbit hole here, where you could keep adding new kinds of targets for different use cases. Maybe that's a good thing, I don't know.
Anyway, a few minor nits, but this otherwise looks fine to me.
external_deps = [ | ||
"abseil_memory", | ||
], |
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'd prefer not to add a dependency just so you can use absl::WrapUnique
instead of std::unique_ptr
ctor (sorry @stevenzzzz, I know you suggested it). Is this style more common in the Envoy codebase?
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.
6 occurrences. I like the simplicity of WrapUnique. I can seek to mark abseil_memory as a common deps in targets so that I don't have to introduce this deps.
And eventually we can replace WrapUnique along with better c++ stdlib
source/common/init/target_impl.h
Outdated
@@ -85,5 +87,49 @@ class TargetImpl : public Target, Logger::Loggable<Logger::Id::init> { | |||
const std::shared_ptr<InternalInitalizeFn> fn_; | |||
}; | |||
|
|||
/** | |||
* A specialized Target which can be added by multiple Manager. |
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.
Managers.
source/common/init/target_impl.h
Outdated
// Human-readable name for logging | ||
const std::string name_; | ||
|
||
// Handle to the ManagerImpl's internal watcher, to call when this target is initialized |
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.
Update comment for multiple managers/multiple watchers.
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
This pull request has been automatically marked as stale because it has not had activity in the last 7 days. It will be closed in 7 days if no further activity occurs. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions! |
Back to this PR. Sorry for the late response |
As an analogy to |
Addressing comments... |
Signed-off-by: Yuchen Dai <silentdai@gmail.com>
@lambdai can you update the GH top comment (and maybe even title)? It seems this PR is mostly about introducing a shared init manager. In fact, if you want to split out the shared init manager to a separate dedicated PR, that could be even better, as we could review that and this PR becomes much smaller and self-contained. |
@htuch will do. Now that we reach the consensus such a shared_target is required I can create a new PR referring to this scenario. |
Just to be clear, I don't believe it's required. I think you can build what you need without it, by having multiple targets — one per init manager — and running the initialization logic when the first target is initialized. |
This pull request has been automatically marked as stale because it has not had activity in the last 7 days. It will be closed in 7 days if no further activity occurs. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions! |
Extracted from #8523 @mergeconflict mentioned we can also maintain a collection of TargetImpl(and a initialize_ flag, std::once_flag) at the target owner. I agree it works but a SharedTarget provides stronger cohesion. Follow up: * update #8523 on top of this PR * complete fuzz test #8714 Signed-off-by: Yuchen Dai <silentdai@gmail.com>
This pull request has been automatically closed because it has not had activity in the last 14 days. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions! |
WIP since this PR is on top of unlanded PR #8485 and I will rebase.
Context: Currently if a dynamic rds config can be referenced by multiple HCM and multiple scoped rds config. Envoy is maintaining many copies.
Description:
In this PR envoy will maintain only one copy of rds config for each resource name. Notes the rule that
listener
is ready only if the dependent RDS is ready is respected.BTW there is a bug so the rule is not fully respected. Will fix in future PRs.Fixed the bug where a init manager might see rds config ready when it's not. That's why SharedTargetImpl is introduced.
Fix #8303