Skip to content

odsds: fix repeated starts and warming/non-warming overlap - #46473

Open
kyessenov wants to merge 3 commits into
envoyproxy:mainfrom
kyessenov:fix_sds_repeated_start2
Open

odsds: fix repeated starts and warming/non-warming overlap#46473
kyessenov wants to merge 3 commits into
envoyproxy:mainfrom
kyessenov:fix_sds_repeated_start2

Conversation

@kyessenov

Copy link
Copy Markdown
Contributor

Change-Id: Ibb6a47a6d43e2366d4c49b7425e99ce874e90448

Commit Message: Fix repeated gRPC subscription starts in OdSDS caused when the secret is subscribed from multiple places. Fixed overlap of the secret providers between warming and non-warming users (a warming user waits for the secret, a non-warming doesn't).
Additional Description:
Risk Level: low (only OdSDS is impacted, e.g. on_demand TLS cert provider)
Testing: updated
Docs Changes: none
Release Notes: yes

Change-Id: Ibb6a47a6d43e2366d4c49b7425e99ce874e90448
Signed-off-by: Kuat Yessenov <kuat@google.com>
@kyessenov kyessenov changed the title odcds: fix repeated starts and warming/non-warming overlap odsds: fix repeated starts and warming/non-warming overlap Jul 30, 2026
Change-Id: I61e7a2cc6937889ffc6de9fc78485eb6b1fb122d
Signed-off-by: Kuat Yessenov <kuat@google.com>

@adisuissa adisuissa left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks, overall LGTM, although it is unclear to me why the provider is dependent on the warming status.

OptRef<Init::Manager> init_manager, bool warm) {
const std::string map_key =
absl::StrCat(MessageUtil::hash(sds_config_source), ".", config_name);
absl::StrCat(MessageUtil::hash(sds_config_source), ".", config_name, warm);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It is not clear to me why is the warming status part of the provider key. It seems to be a wrong move, as the provider should not change regardless of the warming state.
Please add a comment explaining this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The init target belongs the provider. If warm set, then the init target is marked ready when a secret is fetched, otherwise, the init target is marked ready immediately.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added a comment. The alternate way is to add two init targets per provider. Do you want that option to be implemented?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't understand why the warming and non-warming have 2 different init-targets, and not just one.
This is my understanding so feel free to correct me: When a resource is added it may be added in an "active-state" if all of its dependencies are ready, or in a "warming-state" as it waits for its dependencies. Once its dependencies are ready, it will become "active", and notify anything that was dependent on it.
The resource should be the same, regardless of its warming/active state.

It may be that "warm" in this case has a different meaning.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This for the "prefetch mode". A parent resource (listener) initiates the on-demand request because we anticipate an on-demand request on the data path. So it needs to register into the parent resource init manager as if it were warming, but it does not block the parent resource init manager, since it's on-demand.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry, I might be missing something in the design here.
Is the issue that a listener can have both on-demand and non-on-demand secret paths? If so, I assume the listener only waits for the non-on-demand secrets, correct?
Also the on-demand request should only unblock the data-path request, no? why does it need to have an init-manager?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is a bit tricky, consider the following setup:

Listener 1 { using regular SDS secret X }
Listener 2 { using on-demand discovery using a discovered name X }
Listener 3 { using on-demand discovery with a prefetched name X }

The behavior is as follows:

Listener When does SDS for X start What is blocked on SDS for X fetch
Listener 1 LDS load LDS start
Listener 2 Connection accepted Connection proceeds
Listener 3 LDS load Connection proceeds

To handle listener 3, we use its init manager to start the fetch for SDS. But we do not block, so cannot re-use the subscription from the listener 1.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The requesting but non-blocking is an interesting use-case. Do you have links to where/when this was added? I want to learn more about the design there in order to give a better response.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

EXPECT_CALL(*subscription_factory_.subscription_, start(_));
sds_api.start();
// Validate that starting twice only calls subscription start once.
sds_api.start();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

How does this validate, only via logs?
In this case it would be better to use mocks to make sure that it is only invoked once. Otherwise, this can be removed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

There is an expectation mock on line 104.

config_source, "abc.com", secret_context.server_context_, {}, true);

EXPECT_NE(secret_provider2, secret_provider3);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm assuming that there isn't an integration test that can test the regression, but if you are aware of one, consider add it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The only visible effect of the first bug is the noise about the init fetch failures. We can try to insert an assert into gRPC subscription, but that might trigger under other circumstances.

Change-Id: I12bb189a4da6c982e9f89707aeb74086c52ce6a0
Signed-off-by: Kuat Yessenov <kuat@google.com>
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.

2 participants