Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
719661c
docs: Add an explicit wildcard mode
krnowak Jun 7, 2021
9b84dfd
config: Implement explicit wildcard mode in delta gRPC
krnowak Jun 7, 2021
a5f4ed7
test: Add tests for explicit wildcar mode in delta gRPC
krnowak Jun 7, 2021
d78ec97
docs: Reword wildcard mode docs
krnowak Jun 7, 2021
2e8ba91
config: Fix build
krnowak Jun 7, 2021
f4ca629
config: Fix build
krnowak Jun 8, 2021
c008bcc
Merge remote-tracking branch 'origin/main' into krnowak/explicit-wild…
krnowak Jul 2, 2021
3fa6a37
Drop the entry in version history
krnowak Jul 2, 2021
3914615
Always send an asterisk as a wildcard subscription
krnowak Jul 6, 2021
1d53769
Better comments, drop redundant tests
krnowak Jul 6, 2021
fa83949
Add constants for wildcards
krnowak Jul 6, 2021
6748f53
Test fixes
krnowak Jul 6, 2021
02e5370
Merge remote-tracking branch 'origin/main' into krnowak/explicit-wild…
krnowak Jul 7, 2021
872e737
Revert "Test fixes"
krnowak Jul 8, 2021
502de45
Revert "Better comments, drop redundant tests"
krnowak Jul 8, 2021
4ba0fc4
Revert "Always send an asterisk as a wildcard subscription"
krnowak Jul 8, 2021
4e34600
New attempt at implementing new wildcard subscriptions
krnowak Jul 8, 2021
98c408b
Merge remote-tracking branch 'origin/main' into krnowak/explicit-wild…
krnowak Jul 14, 2021
18e9f70
Formatting fixes
krnowak Jul 14, 2021
d6e273f
Update wildcard handling in xds delta subscription state
krnowak Jul 14, 2021
db884fe
Change Wildcard into constexpr string view
krnowak Jul 16, 2021
9339a0f
Merge remote-tracking branch 'origin/main' into krnowak/explicit-wild…
krnowak Aug 10, 2021
cb44431
Rework legacy wildcard subscription handling
krnowak Aug 5, 2021
fcc53aa
Merge remote-tracking branch 'origin/main' into krnowak/explicit-wild…
krnowak Aug 12, 2021
9a1855d
Fix typos
krnowak Aug 12, 2021
e71317f
Rename function to placate clang tidy
krnowak Aug 12, 2021
cb93839
Try to improve coverage
krnowak Aug 13, 2021
b7dee94
Factor out legacy wildcard checks to a separate function
krnowak Aug 13, 2021
61361b8
Document the tests
krnowak Aug 13, 2021
63bba65
Fix formatting
krnowak Aug 13, 2021
da12ed5
Merge remote-tracking branch 'origin/main' into krnowak/explicit-wild…
krnowak Aug 31, 2021
e249133
Fix build after merge
krnowak Aug 31, 2021
88587b0
Get rid of containerContains
krnowak Aug 31, 2021
da99fc7
Fix formatting
krnowak Sep 1, 2021
d9f0803
Document the resource state machine
krnowak Sep 1, 2021
74532e9
Ignore resources that we did not request
krnowak Sep 3, 2021
3d55048
Add a test for ignoring superfluous resources
krnowak Sep 3, 2021
f8b758f
Fix formatting
krnowak Sep 3, 2021
fd76a69
Drop one assert
krnowak Sep 9, 2021
4d20de1
Try to preserve the legacy wildcard status on ineffective unsubscript…
krnowak Sep 9, 2021
ab65125
Expand a bit more on the ambiguous resource category in comments
krnowak Sep 17, 2021
c726d04
Update the comments in the xds_mux variant too
krnowak Sep 20, 2021
f9d846d
Constify some variables
krnowak Sep 28, 2021
bae8643
Drop unused member
krnowak Sep 28, 2021
d10ec21
Gate the explicit wildcard resource support
krnowak Oct 1, 2021
508fc9c
Merge remote-tracking branch 'origin/main' into krnowak/explicit-wild…
krnowak Oct 1, 2021
726d655
Fixes
krnowak Oct 1, 2021
649af01
Build fixes
krnowak Oct 4, 2021
6f4724b
Test the old implementation too
krnowak Oct 4, 2021
7d78a00
docs: Add a note about xds changes
krnowak Oct 4, 2021
93ec187
Fix clang tidy
krnowak Oct 5, 2021
52a66ee
Run ADS integration tests together with old and new DSS
krnowak Oct 5, 2021
044bceb
Add DSS to dictionary
krnowak Oct 5, 2021
559e50a
Merge remote-tracking branch 'origin/main' into krnowak/explicit-wild…
krnowak Oct 5, 2021
9a34a6b
Fix version history after merge
krnowak Oct 5, 2021
5e9a130
Merge remote-tracking branch 'origin/main' into krnowak/explicit-wild…
krnowak Oct 19, 2021
36b872b
test: Fix redis ADS integration test params
krnowak Oct 19, 2021
5a2aac2
Merge remote-tracking branch 'origin/main' into krnowak/explicit-wild…
krnowak Oct 20, 2021
55dfdfa
Shard the redis integration test to avoid timeouts
krnowak Oct 20, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions source/common/config/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@ envoy_cc_library(
hdrs = ["watch_map.h"],
deps = [
":decoded_resource_lib",
":utility_lib",
":xds_resource_lib",
"//envoy/config:subscription_interface",
"//source/common/common:assert_lib",
Expand Down
128 changes: 94 additions & 34 deletions source/common/config/delta_subscription_state.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,37 +14,46 @@ namespace Config {
DeltaSubscriptionState::DeltaSubscriptionState(std::string type_url,
UntypedConfigUpdateCallbacks& watch_map,
const LocalInfo::LocalInfo& local_info,
Event::Dispatcher& dispatcher, const bool wildcard)
Event::Dispatcher& dispatcher)
// TODO(snowp): Hard coding VHDS here is temporary until we can move it away from relying on
// empty resources as updates.
: supports_heartbeats_(type_url != "envoy.config.route.v3.VirtualHost"),
ttl_(
[this](const auto& expired) {
Protobuf::RepeatedPtrField<std::string> removed_resources;
for (const auto& resource : expired) {
setResourceWaitingForServer(resource);
removed_resources.Add(std::string(resource));
if (auto maybe_resource = getRequestedResourceState(resource);
maybe_resource.has_value()) {
maybe_resource->setAsWaitingForServer();
removed_resources.Add(std::string(resource));
} else if (auto erased_count = wildcard_resource_state_.erase(resource);
erased_count > 0) {
removed_resources.Add(std::string(resource));
}
}

watch_map_.onConfigUpdate({}, removed_resources, "");
},
dispatcher, dispatcher.timeSource()),
type_url_(std::move(type_url)), wildcard_(wildcard), watch_map_(watch_map),
local_info_(local_info), dispatcher_(dispatcher) {}
type_url_(std::move(type_url)), watch_map_(watch_map), local_info_(local_info),
dispatcher_(dispatcher) {}

void DeltaSubscriptionState::updateSubscriptionInterest(
const absl::flat_hash_set<std::string>& cur_added,
const absl::flat_hash_set<std::string>& cur_removed) {
for (const auto& a : cur_added) {
setResourceWaitingForServer(a);
// This adds a resource state that is waiting for the server for more information. This also may
// be a wildcard resource, which is fine too.
requested_resource_state_.insert_or_assign(a, ResourceState::waitingForServer());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there a way for a resource to transition back from subscribed to wildcard?

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.

Hm, not directly I think. Should the client be smart and move the resource from subscribed to wildcard or should the client rely on the server detecting that the client unsubscribed from a resource that is also a part of the wildcard subscription and thus resending the resource? I wonder if this scenario should be clarified in the xds-protocol document.

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.

Transitioning from subscribed to wildcard is also tricky. Consider the following scenarios:

  1. Client subscribes to wildcard.
  2. Server sends resource r as a part of wildcard subscription.
  3. Client subscribes to r. At this point the client may know that r also came from wildcard subscription.
  4. Client unsubscribes from r, but the client can still keep r as a resource coming from wildcard subscription.

Another one:

  1. Client subscribes to r and wildcard.
  2. Server sends resource r as a part of both wildcard subscription and explicit subscription to r.
  3. Client unsubscribes from r. At this point the client does not know that r is also a part of the wildcard subscription.

So I'd say that the client should rather rely on the server. Basically the server should notice that the client is subscribed to the resource that is also a part of the wildcard resource set, so when the client unsubscribes from such a resource, the server should resend the resource, so the client could get it as a part of the wildcard subscription set.

Other option would be to extend the discovery response with a boolean stating whether the resource is a part of the wildcard resource set too. This could be used by the client to transition the resource from subscribed to wildcard when unsubscribing from the resource.

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 think that the transport protocol actually provides enough information for the client to know that the server is sending the resource as part of a wildcard subscription. Your second scenario is a good example of this. Here's another one:

  1. Client sends a request subscribing to wildcard.
  2. Client sends a request subscribing to both wildcard and r.
  3. Client receives the server's response to its initial request subscribing to wildcard, which includes r.

Because the client did not receive the response to the first request until after it sent the second request, it has no way to know whether the response it's seeing is a result of the first request or the second, so it cannot assume that r is present because of the wildcard request.

I think the right answer here is to just say that when a client is sending a wildcard subscription, it should never delete resources from its cache when it unsubscribes from a non-wildcard resource on the same stream; instead, it should rely on the resource being removed from the server's next response to do that. (Note that in LDS and CDS, the only two resource types where wildcards are used, the server is required to send all resources that the client is subscribed to in every response, so if a resource is present in one response and absent in the next response, that's a clear indication that the client no longer needs the resource.)

wildcard_resource_state_.erase(a);
// If interest in a resource is removed-then-added (all before a discovery request
// can be sent), we must treat it as a "new" addition: our user may have forgotten its
// copy of the resource after instructing us to remove it, and need to be reminded of it.
names_removed_.erase(a);
names_added_.insert(a);
}
for (const auto& r : cur_removed) {
removeResourceState(r);
requested_resource_state_.erase(r);
// Ideally, when interest in a resource is added-then-removed in between requests,
// we would avoid putting a superfluous "unsubscribe [resource that was never subscribed]"
// in the request. However, the removed-then-added case *does* need to go in the request,
Expand All @@ -53,6 +62,28 @@ void DeltaSubscriptionState::updateSubscriptionInterest(
names_added_.erase(r);
names_removed_.insert(r);
}
// If we unsubscribe from wildcard resource, drop all the resources that came from wildcard from
// cache.
if (cur_removed.contains(Wildcard)) {
wildcard_resource_state_.clear();
}
// Check if this is a legacy wildcard subscription request. If we repeatedly call this function
// with empty cur_added and cur_removed, we keep the legacy wildcard subscription.
if (is_legacy_wildcard_) {

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.

@adisuissa: I remember you added an explicit wildcard mode in delta state; was there are a reason you decided against determining it implicitly, like below?

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.

If I were to guess, then I would say that before my changes the wildcard mode was an immutable trait of delta state, and we knew at a time of delta state creation whether it's going to be in wildcard mode or not, so why not leverage that knowledge? That certainly led to a simpler code and less work being done when updating a subscription interest. And now the wildcard mode is not an immutable trait, so determining the mode at a creation time does not buy us much any more.

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.

IIRC we needed that when a client reconnects on a stream. In wildcard mode the client sends a wildcard/empty-list, and in non-wildcard mode it sends all the resource names.
Looking here it was needed to support this case:

Note that for “wildcard” subscriptions (see How the client specifies what resources to return for details),
the request must either specify “*” in the resource_names_subscribe field or (legacy behavior)
the request must have no resources in both resource_names_subscribe and resource_names_unsubscribe.

is_legacy_wildcard_ = cur_added.empty() && cur_removed.empty();
} else {
// This is a legacy wildcard subscription if we have not expressed interest in any resources so

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Does this only happen on the first request? Or can it reset the legacy wildcard status on stream reconnect?

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.

Hm, turns out to be a tricky question.

So at first I thought that there should be a chance of going back to legacy wildcard subscription in scenario like:

  1. On stream establishment, we send an empty request (legacy wildcard subscription)
  2. The we subscribe to some a resource named r (so we technically switch to explicit wildcard subscription).
  3. Some time later we unsubscribe from the resource r, so at this point we are left with the wildcard subscription only.
  4. Stream failure happens, so we re-establish the connection. Should we send the empty request (legacy wildcard subscription) or a request that subscribes to * (explicit wildcard subscription)?

I tried to achieve sending the empty request, but the code actually will send * explicitly (because requested_resource_state_ will already contain *, so it won't be empty, thus making is_legacy_wildcard_ false in

is_legacy_wildcard_ = !any_request_sent_yet_in_current_stream_ &&
requested_resource_state_.empty() && names_removed_.empty();
). I could try to fix the detection (and add more tests checking it).

But, now imagine another case:

  1. On stream establishment, we send an empty request (legacy wildcard subscription)
  2. The we subscribe to some a resource named r (so we technically switch to explicit wildcard subscription).
  3. Some time later we unsubscribe from the resource r, so at this point we are left with the wildcard subscription only.
  4. Some time later we unsubscribe from the wildcard resource. So we are subscribed to nothing at all.
  5. Stream failure happens, so we re-establish the connection. What request we send? It can't be empty, because this will be a legacy wildcard request. I suppose we shouldn't be sending any requests now.

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.

So at first I thought that there should be a chance of going back to legacy wildcard subscription in scenario like:

  1. On stream establishment, we send an empty request (legacy wildcard subscription)
  2. The we subscribe to some a resource named r (so we technically switch to explicit wildcard subscription).
  3. Some time later we unsubscribe from the resource r, so at this point we are left with the wildcard subscription only.
  4. Stream failure happens, so we re-establish the connection. Should we send the empty request (legacy wildcard subscription) or a request that subscribes to * (explicit wildcard subscription)?

When the stream fails, the state of having previously subscribed to a non-wildcard resource on the stream should reset to false. So when the new stream starts, if the only subscription is the wildcard subscription, I think the right behavior is to send the empty resource names list (legacy subscription).

But, now imagine another case:

  1. On stream establishment, we send an empty request (legacy wildcard subscription)
  2. The we subscribe to some a resource named r (so we technically switch to explicit wildcard subscription).
  3. Some time later we unsubscribe from the resource r, so at this point we are left with the wildcard subscription only.
  4. Some time later we unsubscribe from the wildcard resource. So we are subscribed to nothing at all.
  5. Stream failure happens, so we re-establish the connection. What request we send? It can't be empty, because this will be a legacy wildcard request. I suppose we shouldn't be sending any requests now.

If we are not subscribed to any resources for a given resource type, then when the stream is re-established, we shouldn't send any request for that resource type.

// far. Nor we tried to remove any resources.
is_legacy_wildcard_ = !any_request_sent_yet_in_current_stream_ &&
requested_resource_state_.empty() && names_removed_.empty();
if (is_legacy_wildcard_) {
// Inserting wildcard to requested resource as waiting for server, which means that wildcard
// resource has no version and should never get one actually. As such, it won't be listed in
// initial_resource_versions field.
requested_resource_state_.insert_or_assign(Wildcard, ResourceState::waitingForServer());
names_added_.emplace(Wildcard);
}
}
}

// Not having sent any requests yet counts as an "update pending" since you're supposed to resend
Expand Down Expand Up @@ -81,13 +112,21 @@ bool DeltaSubscriptionState::isHeartbeatResponse(
!Runtime::runtimeFeatureEnabled("envoy.reloadable_features.vhds_heartbeats")) {
return false;
}
const auto itr = resource_state_.find(resource.name());
if (itr == resource_state_.end()) {
if (resource.has_resource()) {
return false;
}

return !resource.has_resource() && !itr->second.waitingForServer() &&
resource.version() == itr->second.version();
if (const auto maybe_resource = getRequestedResourceState(resource.name());
maybe_resource.has_value()) {
return !maybe_resource->isWaitingForServer() && resource.version() == maybe_resource->version();
}

if (const auto itr = wildcard_resource_state_.find(resource.name());
itr != wildcard_resource_state_.end()) {
return resource.version() == itr->second;
}

return false;
}

void DeltaSubscriptionState::handleGoodResponse(
Expand Down Expand Up @@ -124,7 +163,7 @@ void DeltaSubscriptionState::handleGoodResponse(
{
const auto scoped_update = ttl_.scopedTtlUpdate();
for (const auto& resource : message.resources()) {
addResourceState(resource);
addResourceStateFromServer(resource);
}
}

Expand All @@ -138,10 +177,14 @@ void DeltaSubscriptionState::handleGoodResponse(
//
// So, leave the version map entry present but blank. It will be left out of
// initial_resource_versions messages, but will remind us to explicitly tell the server "I'm
// cancelling my subscription" when we lose interest.
// cancelling my subscription" when we lose interest. In case of resources received as a part of
// the wildcard subscription, we just drop them.
for (const auto& resource_name : message.removed_resources()) {
if (resource_names_.find(resource_name) != resource_names_.end()) {
setResourceWaitingForServer(resource_name);
if (auto maybe_resource = getRequestedResourceState(resource_name);
maybe_resource.has_value()) {
maybe_resource->setAsWaitingForServer();
} else {
wildcard_resource_state_.erase(resource_name);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would it be possible to add some invariants (ASSERTs) showing that wildcard resource state and request resource state are disjoint?

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.

Sure, I can add them.

}
}
ENVOY_LOG(debug, "Delta config for {} accepted with {} resources added, {} removed", type_url_,
Expand Down Expand Up @@ -170,22 +213,25 @@ DeltaSubscriptionState::getNextRequestAckless() {
// initial_resource_versions "must be populated for first request in a stream".
// Also, since this might be a new server, we must explicitly state *all* of our subscription
// interest.
for (auto const& [resource_name, resource_state] : resource_state_) {
for (auto const& [resource_name, resource_state] : requested_resource_state_) {
// Populate initial_resource_versions with the resource versions we currently have.
// Resources we are interested in, but are still waiting to get any version of from the
// server, do not belong in initial_resource_versions. (But do belong in new subscriptions!)
if (!resource_state.waitingForServer()) {
if (!resource_state.isWaitingForServer()) {
(*request.mutable_initial_resource_versions())[resource_name] = resource_state.version();
}
// As mentioned above, fill resource_names_subscribe with everything, including names we
// have yet to receive any resource for unless this is a wildcard subscription, for which
// the first request on a stream must be without any resource names.
if (!wildcard_) {
names_added_.insert(resource_name);
}
// We are going over a list of resources that we are interested in, so add them to
// resource_names_subscribe.
names_added_.insert(resource_name);
}
for (auto const& [resource_name, resource_version] : wildcard_resource_state_) {
// Populate initial_resource_versions with the resource versions we currently have.
(*request.mutable_initial_resource_versions())[resource_name] = resource_version;
// We are not adding these resources to resource_names_subscribe.
}
// Wildcard subscription initial requests must have no resource_names_subscribe.
if (wildcard_) {
// If this is a legacy wildcard request, then make sure that the resource_names_subscribe is
// empty.
if (is_legacy_wildcard_) {
names_added_.clear();
}
names_removed_.clear();
Expand Down Expand Up @@ -213,7 +259,7 @@ DeltaSubscriptionState::getNextRequestWithAck(const UpdateAck& ack) {
return request;
}

void DeltaSubscriptionState::addResourceState(
void DeltaSubscriptionState::addResourceStateFromServer(
const envoy::service::discovery::v3::Resource& resource) {
if (resource.has_ttl()) {
ttl_.add(std::chrono::milliseconds(DurationUtil::durationToMilliseconds(resource.ttl())),
Expand All @@ -222,18 +268,32 @@ void DeltaSubscriptionState::addResourceState(
ttl_.clear(resource.name());
}

resource_state_[resource.name()] = ResourceState(resource);
resource_names_.insert(resource.name());
if (auto maybe_resource = getRequestedResourceState(resource.name());
maybe_resource.has_value()) {
// It is a resource that we requested.
maybe_resource->setVersion(resource.version());
} else {
// It is a resource that is a part of our wildcard request.
wildcard_resource_state_.insert({resource.name(), resource.version()});
}
}

void DeltaSubscriptionState::setResourceWaitingForServer(const std::string& resource_name) {
resource_state_[resource_name] = ResourceState();
resource_names_.insert(resource_name);
OptRef<DeltaSubscriptionState::ResourceState>
DeltaSubscriptionState::getRequestedResourceState(absl::string_view resource_name) {
auto itr = requested_resource_state_.find(resource_name);
if (itr == requested_resource_state_.end()) {
return {};
}
return {itr->second};
}

void DeltaSubscriptionState::removeResourceState(const std::string& resource_name) {
resource_state_.erase(resource_name);
resource_names_.erase(resource_name);
OptRef<const DeltaSubscriptionState::ResourceState>
DeltaSubscriptionState::getRequestedResourceState(absl::string_view resource_name) const {
auto itr = requested_resource_state_.find(resource_name);
if (itr == requested_resource_state_.end()) {
return {};
}
return {itr->second};
}

} // namespace Config
Expand Down
33 changes: 15 additions & 18 deletions source/common/config/delta_subscription_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ namespace Config {
class DeltaSubscriptionState : public Logger::Loggable<Logger::Id::config> {
public:
DeltaSubscriptionState(std::string type_url, UntypedConfigUpdateCallbacks& watch_map,
const LocalInfo::LocalInfo& local_info, Event::Dispatcher& dispatcher,
const bool wildcard);
const LocalInfo::LocalInfo& local_info, Event::Dispatcher& dispatcher);

// Update which resources we're interested in subscribing to.
void updateSubscriptionInterest(const absl::flat_hash_set<std::string>& cur_added,
Expand Down Expand Up @@ -61,15 +60,17 @@ class DeltaSubscriptionState : public Logger::Loggable<Logger::Id::config> {

class ResourceState {
public:
ResourceState(const envoy::service::discovery::v3::Resource& resource)
: version_(resource.version()) {}

// Builds a ResourceState in the waitingForServer state.
ResourceState() = default;
// Self-documenting alias of default constructor.
static ResourceState waitingForServer() { return ResourceState(); }

// If true, we currently have no version of this resource - we are waiting for the server to
// provide us with one.
bool waitingForServer() const { return version_ == absl::nullopt; }
bool isWaitingForServer() const { return version_ == absl::nullopt; }

void setAsWaitingForServer() { version_ = absl::nullopt; }
void setVersion(absl::string_view version) { version_ = std::string(version); }

// Must not be called if waitingForServer() == true.
std::string version() const {
Expand All @@ -81,38 +82,34 @@ class DeltaSubscriptionState : public Logger::Loggable<Logger::Id::config> {
absl::optional<std::string> version_;
};

// Use these helpers to ensure resource_state_ and resource_names_ get updated together.
void addResourceState(const envoy::service::discovery::v3::Resource& resource);
void setResourceWaitingForServer(const std::string& resource_name);
void removeResourceState(const std::string& resource_name);

void populateDiscoveryRequest(envoy::service::discovery::v3::DeltaDiscoveryResponse& request);
void addResourceStateFromServer(const envoy::service::discovery::v3::Resource& resource);
OptRef<ResourceState> getRequestedResourceState(absl::string_view resource_name);
OptRef<const ResourceState> getRequestedResourceState(absl::string_view resource_name) const;

// A map from resource name to per-resource version. The keys of this map are exactly the resource
// names we are currently interested in. Those in the waitingForServer state currently don't have
// any version for that resource: we need to inform the server if we lose interest in them, but we
// also need to *not* include them in the initial_resource_versions map upon a reconnect.
absl::node_hash_map<std::string, ResourceState> resource_state_;
absl::node_hash_map<std::string, ResourceState> requested_resource_state_;
// A map from resource name to per-resource version. The keys of this map are resource names we
// have received as a part of the wildcard subscription.
absl::node_hash_map<std::string, std::string> wildcard_resource_state_;

// Not all xDS resources supports heartbeats due to there being specific information encoded in
// an empty response, which is indistinguishable from a heartbeat in some cases. For now we just
// disable heartbeats for these resources (currently only VHDS).
const bool supports_heartbeats_;
TtlManager ttl_;
// The keys of resource_versions_. Only tracked separately because std::map does not provide an
// iterator into just its keys.
absl::flat_hash_set<std::string> resource_names_;

const std::string type_url_;
// Is the subscription is for a wildcard request.
const bool wildcard_;
UntypedConfigUpdateCallbacks& watch_map_;
const LocalInfo::LocalInfo& local_info_;
Event::Dispatcher& dispatcher_;
std::chrono::milliseconds init_fetch_timeout_;

bool any_request_sent_yet_in_current_stream_{};
bool must_send_discovery_request_{};
bool is_legacy_wildcard_{};

// Tracks changes in our subscription interest since the previous DeltaDiscoveryRequest we sent.
// TODO: Can't use absl::flat_hash_set due to ordering issues in gTest expectation matching.
Expand Down
11 changes: 5 additions & 6 deletions source/common/config/new_grpc_mux_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ GrpcMuxWatchPtr NewGrpcMuxImpl::addWatch(const std::string& type_url,
auto entry = subscriptions_.find(type_url);
if (entry == subscriptions_.end()) {
// We don't yet have a subscription for type_url! Make one!
addSubscription(type_url, options.use_namespace_matching_, resources.empty());
addSubscription(type_url, options.use_namespace_matching_);
return addWatch(type_url, resources, callbacks, resource_decoder, options);
}

Expand Down Expand Up @@ -207,11 +207,10 @@ void NewGrpcMuxImpl::removeWatch(const std::string& type_url, Watch* watch) {
entry->second->watch_map_.removeWatch(watch);
}

void NewGrpcMuxImpl::addSubscription(const std::string& type_url, const bool use_namespace_matching,
const bool wildcard) {
subscriptions_.emplace(type_url, std::make_unique<SubscriptionStuff>(type_url, local_info_,
use_namespace_matching,
dispatcher_, wildcard));
void NewGrpcMuxImpl::addSubscription(const std::string& type_url,
const bool use_namespace_matching) {
subscriptions_.emplace(type_url, std::make_unique<SubscriptionStuff>(
type_url, local_info_, use_namespace_matching, dispatcher_));
subscription_ordering_.emplace_back(type_url);
}

Expand Down
8 changes: 3 additions & 5 deletions source/common/config/new_grpc_mux_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,9 @@ class NewGrpcMuxImpl

struct SubscriptionStuff {
SubscriptionStuff(const std::string& type_url, const LocalInfo::LocalInfo& local_info,
const bool use_namespace_matching, Event::Dispatcher& dispatcher,
const bool wildcard)
const bool use_namespace_matching, Event::Dispatcher& dispatcher)
: watch_map_(use_namespace_matching),
sub_state_(type_url, watch_map_, local_info, dispatcher, wildcard) {}
sub_state_(type_url, watch_map_, local_info, dispatcher) {}

WatchMap watch_map_;
DeltaSubscriptionState sub_state_;
Expand Down Expand Up @@ -130,8 +129,7 @@ class NewGrpcMuxImpl
const SubscriptionOptions& options);

// Adds a subscription for the type_url to the subscriptions map and order list.
void addSubscription(const std::string& type_url, bool use_namespace_matching,
const bool wildcard);
void addSubscription(const std::string& type_url, bool use_namespace_matching);

void trySendDiscoveryRequests();

Expand Down
2 changes: 2 additions & 0 deletions source/common/config/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
namespace Envoy {
namespace Config {

constexpr absl::string_view Wildcard = "*";

/**
* Constant Api Type Values, used by envoy::config::core::v3::ApiConfigSource.
*/
Expand Down
3 changes: 2 additions & 1 deletion source/common/config/watch_map.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "source/common/common/cleanup.h"
#include "source/common/common/utility.h"
#include "source/common/config/decoded_resource_impl.h"
#include "source/common/config/utility.h"
#include "source/common/config/xds_resource.h"

namespace Envoy {
Expand Down Expand Up @@ -49,7 +50,7 @@ void WatchMap::removeDeferredWatches() {
AddedRemoved
WatchMap::updateWatchInterest(Watch* watch,
const absl::flat_hash_set<std::string>& update_to_these_names) {
if (update_to_these_names.empty()) {
if (update_to_these_names.empty() || update_to_these_names.contains(Wildcard)) {
wildcard_watches_.insert(watch);
} else {
wildcard_watches_.erase(watch);
Expand Down
Loading