Skip to content
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

CM: Add on-demand cluster discovery functionality #15857

Closed
wants to merge 45 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
0652500
CM, ODCDS: Add on-demand cluster discovery functionality
krnowak Mar 19, 2021
050c490
CM: Fix a typo
krnowak Apr 1, 2021
d5f74fb
CDS helper: Make name const
krnowak Apr 1, 2021
805ef37
test: Add tests for ODCDS functionality in CM
krnowak Apr 6, 2021
8cac4ac
ODCDS: Allow passing a resource locator
krnowak Apr 7, 2021
3c4cfa5
CM, test: Refactor to move discovery functionality to handle
krnowak Apr 8, 2021
75a0c46
CM, test: Fix formatting
krnowak Apr 8, 2021
a53ea84
Merge remote-tracking branch 'origin/main' into krnowak/odcds-cm-only
krnowak Apr 8, 2021
a718e3b
CM: Fix build issues
krnowak Apr 8, 2021
cbe797e
CM: Fix linking issues
krnowak Apr 8, 2021
bc1d0df
test: Fix build issues
krnowak Apr 8, 2021
b32112f
CM, test: Address review issues
krnowak Apr 9, 2021
4e52486
CM: test: Address review issues
krnowak Apr 12, 2021
7ea51f0
CM, test: Improve coverage a bit
krnowak Apr 13, 2021
fad4410
CM, test: Address review issues
krnowak Apr 14, 2021
4f854b5
CM: Address review issues
krnowak Apr 15, 2021
bb2e17c
test: Address review issues
krnowak Apr 20, 2021
caad394
Merge remote-tracking branch 'origin/main' into krnowak/odcds-cm-only
krnowak Apr 20, 2021
722562c
test: Fix build
krnowak Apr 20, 2021
2fd85dc
CM: Refactor cluster discovery manager
krnowak Apr 22, 2021
61a07d2
refactor of the discovery manager
krnowak May 5, 2021
1002639
refactor
krnowak May 5, 2021
c4eef58
CM: Add missing cluster status
krnowak May 5, 2021
161d896
Merge remote-tracking branch 'origin/main' into krnowak/odcds-cm-only
krnowak May 5, 2021
6058b3d
grpc: initial * hack
krnowak May 6, 2021
857f696
document the purpose of the map
krnowak May 6, 2021
d138b40
fix build
krnowak May 6, 2021
d694f4b
fixup! grpc: initial * hack
krnowak May 6, 2021
11031da
test with notifier
krnowak May 7, 2021
00cc3b3
fix format
krnowak May 7, 2021
1d11e12
Merge remote-tracking branch 'origin/main' into krnowak/odcds-cm-only
krnowak May 11, 2021
f912324
Merge remote-tracking branch 'origin/main' into krnowak/odcds-cm-only
krnowak May 18, 2021
ccc45c5
add invoker to dict
krnowak May 18, 2021
8464e01
fix comments
krnowak May 18, 2021
3c5ec84
delta-xds: Fix initial requests
krnowak May 22, 2021
1db17a0
more comment fixes
krnowak May 22, 2021
60394a4
delta-xds: add resource type
krnowak May 22, 2021
a513cb6
docs: update xds-protocol docs about explicit wildcard mode
krnowak May 22, 2021
7b3f78b
docs: further updates in xds protocol
krnowak May 22, 2021
bb3eb45
tests: fix clang-tidy, use nicemock
krnowak May 22, 2021
124179a
test: Add another case to discovery manager tests
krnowak May 25, 2021
788680a
test: Improve coverage around notifications
krnowak May 25, 2021
04f8f43
CM, test: Try to improve coverage around detection of discovery in pr…
krnowak May 25, 2021
411e72d
test: Improve coverage for explicit wildcard subscription mode
krnowak May 25, 2021
f6ee71a
config, test: Fixes for explicit wildcard mode and coverage improvements
krnowak May 25, 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
24 changes: 17 additions & 7 deletions docs/root/api-docs/xds_protocol.rst
Original file line number Diff line number Diff line change
Expand Up @@ -427,13 +427,18 @@ names becomes empty, that means that the client is no longer interested in any r
specified type.

For :ref:`Listener <envoy_v3_api_msg_config.listener.v3.Listener>` and :ref:`Cluster <envoy_v3_api_msg_config.cluster.v3.Cluster>` resource
types, there is also a "wildcard" mode, which is triggered when the initial request on the stream
for that resource type contains no resource names. In this case, the server should use
types, there is also a "wildcard" mode, which comes in two flavors. First flavor, implicit, is triggered when the initial request on the stream
for that resource type contains no resource names. Second flavor, explicit, is triggered when a request
(not necessarily an initial one on the stream) for that resource type contains (among other names) a special name "*".
For wildcard requests, the server should use
site-specific business logic to determine the full set of resources that the client is interested
in, typically based on the client's :ref:`node <envoy_v3_api_msg_config.core.v3.Node>` identification. Note
that once a stream has entered wildcard mode for a given resource type, there is no way to change
the stream out of wildcard mode; resource names specified in any subsequent request on the stream
will be ignored.
in, typically based on the client's :ref:`node <envoy_v3_api_msg_config.core.v3.Node>` identification.
The client can opt out from the wildcard mode by unsubscribing from the "*" resource name. Note that
opting back into the wildcard mode can only be done with a request containing the "*" resource name,
thus switching into the explicit wildcard mode. Also note that if client wants to express
an interest in a resource name after sending an empty initial request on the stream, the client
needs to switch to the explicit wildcard mode, otherwise resource names specified in subsequent
request on the stream will be ignored.

Client Behavior
"""""""""""""""
Expand Down Expand Up @@ -535,6 +540,8 @@ being requested by the client, and if one of those resources springs into existe
server must send an update to the client informing it of the new resource. Clients that initially
see a resource that does not exist must be prepared for the resource to be created at any time.

.. _xds_protocol_unsubscribing_from_resources:

Unsubscribing From Resources
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand All @@ -553,7 +560,10 @@ Note that for :ref:`Listener <envoy_v3_api_msg_config.listener.v3.Listener>` and
resource types where the stream is in "wildcard" mode (see :ref:`How the client specifies what
resources to return <xds_protocol_resource_hints>` for details), the set of resources being
subscribed to is determined by the server instead of the client, so there is no mechanism
for the client to unsubscribe from resources.
for the client to unsubscribe from resources. The only resources that the client could unsubscribe
from are the resources that the client explicitly expressed the interest in before. Note that
the server may still send the resource to the client if the resource was also a part of the set
of resources determined by the server from the wildcard subscription.

Requesting Multiple Resources on a Single Stream
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
1 change: 1 addition & 0 deletions docs/root/version_history/current.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Incompatible Behavior Changes
* grpc_bridge_filter: the filter no longer collects grpc stats in favor of the existing grpc stats filter.
The behavior can be reverted by changing runtime key ``envoy.reloadable_features.grpc_bridge_stats_disabled``.
* tracing: update Apache SkyWalking tracer version to be compatible with 8.4.0 data collect protocol. This change will introduce incompatibility with SkyWalking 8.3.0.
* xds: added the explicit wildcard mode, which allows subscribing to specific resource names on top of a wildcard subscription. This means that the resource name ``*`` is reserved. This may mean that in some cases the initial wildcard subscription request on the stream will not be empty, but will have a non-empty list of resources with a special name among them. See :ref:`wildcard mode description <xds_protocol_resource_hints>` and :ref:`unsubscribing from resources <xds_protocol_unsubscribing_from_resources>` for details.

Minor Behavior Changes
----------------------
Expand Down
85 changes: 84 additions & 1 deletion include/envoy/upstream/cluster_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ namespace Envoy {
namespace Upstream {

/**
* ClusterUpdateCallbacks provide a way to exposes Cluster lifecycle events in the
Copy link
Member

Choose a reason for hiding this comment

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

One thing that you'll need to add as we finalize on this PR is version history and also probably some docs like https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/on_demand_updates_filter.html. Maybe also update https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds_protocol to reference (places where CDS and VHDS crop up are a good start).

* ClusterUpdateCallbacks provide a way to expose Cluster lifecycle events in the
* ClusterManager.
*/
class ClusterUpdateCallbacks {
Expand Down Expand Up @@ -72,6 +72,76 @@ class ClusterUpdateCallbacksHandle {

using ClusterUpdateCallbacksHandlePtr = std::unique_ptr<ClusterUpdateCallbacksHandle>;

/**
* Status enum for the result of an attempted cluster discovery.
*/
enum class ClusterDiscoveryStatus {
/**
* The discovery process timed out. This means that we haven't yet received any reply from
* on-demand CDS about it.
*/
Timeout,
/**
* The discovery process has concluded and on-demand CDS has no such cluster.
*/
Missing,
/**
* Cluster found and currently available through ClusterManager.
*/
Available,
};

/**
* ClusterDiscoveryCallback is a callback called at the end of the on-demand cluster discovery
* process. The status of the discovery is sent as a parameter.
*/
using ClusterDiscoveryCallback = std::function<void(ClusterDiscoveryStatus)>;
using ClusterDiscoveryCallbackPtr = std::unique_ptr<ClusterDiscoveryCallback>;

/**
* ClusterDiscoveryCallbackHandle is a RAII wrapper for a ClusterDiscoveryCallback. Deleting the
* ClusterDiscoveryCallbackHandle will remove the callbacks from ClusterManager.
*/
class ClusterDiscoveryCallbackHandle {
public:
virtual ~ClusterDiscoveryCallbackHandle() = default;
};

using ClusterDiscoveryCallbackHandlePtr = std::unique_ptr<ClusterDiscoveryCallbackHandle>;

/**
* A handle to an on-demand CDS.
*/
class OdCdsApiHandle {
public:
virtual ~OdCdsApiHandle() = default;

/**
* Request an on-demand discovery of a cluster with a passed name. This ODCDS may be used to
* perform the discovery process in the main thread if there is no discovery going on for this
* cluster. When the requested cluster is added and warmed up, the passed callback will be invoked
* in the same thread that invoked this function.
*
* The returned handle can be destroyed to prevent the callback to be invoked. Note that the
* handle can only be destroyed in the same thread that invoked the function. Destroying the
* handle might not stop the discovery process, though. As soon as the callback is invoked,
* destroying the handle does nothing. It is a responsibility of the caller to make sure that the
* objects captured in the callback outlive the callback.
*
* This function is thread-safe.
*
* @param name is the name of the cluster to be discovered.
* @param callback will be called when the discovery is finished.
* @param timeout describes how long the operation may take before failing.
* @return ClusterDiscoveryCallbackHandlePtr the discovery process handle.
*/
virtual ClusterDiscoveryCallbackHandlePtr
requestOnDemandClusterDiscovery(absl::string_view name, ClusterDiscoveryCallbackPtr callback,
std::chrono::milliseconds timeout) PURE;
};

using OdCdsApiHandlePtr = std::unique_ptr<OdCdsApiHandle>;

class ClusterManagerFactory;

// These are per-cluster per-thread, so not "global" stats.
Expand Down Expand Up @@ -309,6 +379,19 @@ class ClusterManager {
virtual const ClusterRequestResponseSizeStatNames&
clusterRequestResponseSizeStatNames() const PURE;
virtual const ClusterTimeoutBudgetStatNames& clusterTimeoutBudgetStatNames() const PURE;

/**
* Allocates an on-demand CDS API provider from configuration proto or locator.
Copy link
Member

Choose a reason for hiding this comment

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

What are the semantics when we are using CDS and have both regular CDS wildcard subscription and OCDS on the same stream?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for pointing this out. To rehash:

  • Once CDS subscription enters the wildcard mode, it should ignore requests for specific cluster names.
  • ODCDS currently always sends requests with specific cluster names.

So, it looks like those two currently can't work together in one stream. Now, I don't know if this ignoring of requests with specific resource names while being in wildcard mode is enforced on Envoy side or this is an expectation from the config server. I don't know what is the rationale behind this restriction, so I'd ask if lifting this restriction would be an acceptable idea.

Copy link
Member

Choose a reason for hiding this comment

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

I think @markdroth added that restriction in https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds_protocol. I also agree it would be good to be able to combine wildcard and on-demand CDS here on the same stream. Thoughts @markdroth?

Copy link
Contributor

Choose a reason for hiding this comment

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

As currently defined, it's not possible to have both a wildcard subscription and subscription to specific resource names on the same stream. Note, however, that wildcard subscriptions are intended to go away as part of the new naming scheme, so one option here would be to simply say that you can't do both unless you migrate to the new naming scheme.

If that's not feasible, we could look into making some tranport protocol change to support this. For example, maybe the wildcard request could be represented by resource_names: "*" instead of leaving resource_names completely unset. But we'd need to carefully look through the spec and make sure that there are no non-obvious implications of doing that. I think it should be okay, because it doesn't change the two things that are really special about responses from wildcard subscriptions: the fact that the server must send all existing resources in every response and the fact that if a resource is no longer present in a response, the client can assume it's been deleted on the server.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As currently defined, it's not possible to have both a wildcard subscription and subscription to specific resource names on the same stream. Note, however, that wildcard subscriptions are intended to go away as part of the new naming scheme, so one option here would be to simply say that you can't do both unless you migrate to the new naming scheme.

Is there a reason why both wildcard subscription and subscription to specific resource names are impossible on the same stream? I can see the conflict between SotW wildcard subscription and subscription to specific resource names (SotW update would likely delete the clusters we got on-demand). Such conflict wouldn't exist with delta wildcard subscription. I suppose I could limit the on-demand CDS to be usable only with delta gRPC.

About the naming scheme - I need to read about this, so can't say much about it. Just wanted to say that I dunno if cluster names coming from cluster_header action checking the Host HTTP header fit into a new naming scheme.

If that's not feasible, we could look into making some tranport protocol change to support this. For example, maybe the wildcard request could be represented by resource_names: "*" instead of leaving resource_names completely unset. But we'd need to carefully look through the spec and make sure that there are no non-obvious implications of doing that. I think it should be okay, because it doesn't change the two things that are really special about responses from wildcard subscriptions: the fact that the server must send all existing resources in every response and the fact that if a resource is no longer present in a response, the client can assume it's been deleted on the server.

Wouldn't changing the wildcard request representation to resource_names: "*" be a breaking change for the existing config servers?

Copy link
Member

Choose a reason for hiding this comment

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

SG. Let's update https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds_protocol as part of this proposal then.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If CDS is configured and ODCDS is not configured, what initial request will contain in resource names? An empty list or "*"? What I'm asking here is how do we decide when to send an empty list vs. "*". Is this decided statically (like for CDS we will always send "*", because we will have ODCDS, but for EDS we will send an empty list, because there is no such thing like ODEDS?

I suppose that this "static" decision would be the way to go, because CDS is usually configured statically, while ODCDS might be configured much later (for example, ODCDS could be a part of some route configuration received from RDS). I'm probably wrong somewhere, so please correct me if it's the case. Thanks.

Copy link
Contributor

Choose a reason for hiding this comment

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

If we require this to be explicitly configured, then we need to figure out how to handle the case where someone configures CDS to leave the field unset instead of setting it to * but then later configures ODCDS. It seems better to figure this out automatically, so that it's not possible to configure this incorrectly.

Note that currently, wildcard mode is determined based on the first request for that resource type on a given stream, and there is no way to subsequently switch in or out of wildcard mode for that resource type on that stream. So if the first CDS request on a stream has the resource_names field unset, then the stream is in wildcard mode for CDS resources, and the field is ignored in all subsequent CDS requests on that stream. Conversely, if the first CDS request on a stream has the resource_names field set, then the stream is in non-wildcard mode for CDS resources, and the field is honored in all subsequent CDS requests on that stream; after that, if a request is sent with the field unset, that is interpretted as unsubscribing from all CDS resources instead of being interpretted as a wildcard request. For details, see:

https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds_protocol#how-the-client-specifies-what-resources-to-return
https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds_protocol#unsubscribing-from-resources

I think that what we should do here is to change the spec to make it possible to switch out of wildcard code after the first request on a stream. However, it will still be impossible to switch into wildcard mode once in non-wildcard mode, because otherwise there will be no way to unsubscribe from the last resource of a given type. In other words:

  • If the first request for a given resource type on a stream has the resource_names field unset, then it's in wildcard mode.
  • If a subsequent request for that resource type on the stream has the resource_names field set, then the stream switches to non-wildcard mode. If the client wants to retain the original wildcard subscription, then the resource_names field must include *; if it includes only other resource names but no *, that means to remove from the wildcard subscription and add whatever other subscriptions are specified.
  • Once a stream has seen at least one non-wildcard subscription for a given resource type, a subsequent request for that same resource type with the resource_names field unset means to unsubscribe to everything; it does not indicate a wildcard subscription. Clients wishing to unsubscribe to non-wildcard subscriptions but retain a wildcard subscription must continue to send * for the duration of the stream.

Note that in terms of the spec, I think we can say that servers should be prepared to accept * to mean wildcard even at the start of a stream, even when there are no non-wildcard subscriptions. But we don't ever actually need to send that in Envoy, so in practice there won't be any backward-compatibility problems.

In terms of the Envoy implementation, I think we can figure out what to send automatically at the moment that we send each request. We just need to track a bool indicating whether the stream has previously seen a non-wildcard subscription for a given resource type. If it hasn't, then we can leave the field unset; if it has, then we send * instead.

FWIW, note that EDS never makes wildcard requests. The only resource types that make wildcard requests are LDS, CDS, and (I think) SRDS.

Copy link
Member

Choose a reason for hiding this comment

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

SG, I think your third case doesn't apply with delta xDS though.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, for delta xDS, instead of the resource_names field being unset, it would be sending resource_names_unsubscribe for all current subscriptions. But I think the same concept would apply: once the set of subscribed resource locators goes back to being the empty set, that indicates unsubscribing to all, not a wildcard subscription.

*
* @param odcds_config is a configuration proto. Used when odcds_resources_locator is a nullopt.
* @param odcds_resources_locator is a locator for ODCDS. Used over odcds_config if not a nullopt.
* @param validation_visitor
* @return OdCdsApiHandlePtr the ODCDS handle.
*/
virtual OdCdsApiHandlePtr
allocateOdCdsApi(const envoy::config::core::v3::ConfigSource& odcds_config,
OptRef<xds::core::v3::ResourceLocator> odcds_resources_locator,
ProtobufMessage::ValidationVisitor& validation_visitor) PURE;
};

using ClusterManagerPtr = std::unique_ptr<ClusterManager>;
Expand Down
91 changes: 69 additions & 22 deletions source/common/config/delta_subscription_state.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,23 @@ DeltaSubscriptionState::DeltaSubscriptionState(std::string type_url,
[this](const auto& expired) {
Protobuf::RepeatedPtrField<std::string> removed_resources;
for (const auto& resource : expired) {
setResourceWaitingForServer(resource);
removed_resources.Add(std::string(resource));
if (setResourceWaitingForServer(resource)) {
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),
type_url_(std::move(type_url)),
mode_(wildcard ? WildcardMode::Implicit : WildcardMode::Disabled), 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);
addResourceWaitingForServer(a, ResourceType::ExplicitlyRequested);
// 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.
Expand All @@ -53,6 +55,31 @@ void DeltaSubscriptionState::updateSubscriptionInterest(
names_added_.erase(r);
names_removed_.insert(r);
}
switch (mode_) {
case WildcardMode::Implicit:
if (names_removed_.find("*") != names_removed_.end()) {
// we explicitly cancel the wildcard subscription
mode_ = WildcardMode::Disabled;
} else if (!names_added_.empty()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm. I'm not sure about this: an explicit mode requires presence of a * in the names_added list; we only check if that list isn't empty. If another call to updateSubscriptionInterest is made before a request to the server is made, we may end up in a wrong state: for example if names_removed is empty in that call, we'll end up in WildcardMode::Disabled state.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm. I'm not sure about this: an explicit mode requires presence of a * in the names_added list;

Only on the initial request (after the stream reset, for example), or on the first request where we decide to opt-in into the wildcard mode. When switching from implicit to explicit mode, no need to send * since the server should already know that we are in the wildcard mode. I would say that with this PR, the server should not ignore delta requests with a non-empty resource_names_subscribe list from a client that opted-in into wildcard mode.

I'll need to go through the documentation I wrote to make sure that it's clear.

we only check if that list isn't empty. If another call to updateSubscriptionInterest is made before a request to the server is made, we may end up in a wrong state: for example if names_removed is empty in that call, we'll end up in WildcardMode::Disabled state.

Sorry, I'm not following here. There isn't any logic that resets us to WildcardMode::Disabled on an empty names_removed_ list. Could you clarify?

// switch to explicit mode if we requested some extra names
mode_ = WildcardMode::Explicit;
}
break;

case WildcardMode::Explicit:
if (names_removed_.find("*") != names_removed_.end()) {
// we explicitly cancel the wildcard subscription
mode_ = WildcardMode::Disabled;
}
break;

case WildcardMode::Disabled:
if (names_added_.find("*") != names_added_.end()) {
// we switch into an explicit wildcard subscription
mode_ = WildcardMode::Explicit;
}
break;
}
}

// Not having sent any requests yet counts as an "update pending" since you're supposed to resend
Expand Down Expand Up @@ -124,7 +151,7 @@ void DeltaSubscriptionState::handleGoodResponse(
{
const auto scoped_update = ttl_.scopedTtlUpdate();
for (const auto& resource : message.resources()) {
addResourceState(resource);
addResourceState(resource, ResourceType::ReceivedFromServer);
}
}

Expand All @@ -140,9 +167,7 @@ void DeltaSubscriptionState::handleGoodResponse(
// initial_resource_versions messages, but will remind us to explicitly tell the server "I'm
// cancelling my subscription" when we lose interest.
for (const auto& resource_name : message.removed_resources()) {
if (resource_names_.find(resource_name) != resource_names_.end()) {
setResourceWaitingForServer(resource_name);
}
setResourceWaitingForServer(resource_name);
}
ENVOY_LOG(debug, "Delta config for {} accepted with {} resources added, {} removed", type_url_,
message.resources().size(), message.removed_resources().size());
Expand Down Expand Up @@ -177,16 +202,20 @@ DeltaSubscriptionState::getNextRequestAckless() {
if (!resource_state.waitingForServer()) {
(*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_) {
// Add resource names to resource_names_subscribe only if this is not a wildcard subscription
// request or if we requested this resource explicitly (so we are actually in explicit
// wildcard mode).
if (mode_ == WildcardMode::Disabled ||
resource_state.type() == ResourceType::ExplicitlyRequested) {
names_added_.insert(resource_name);
}
}
// Wildcard subscription initial requests must have no resource_names_subscribe.
if (wildcard_) {
names_added_.clear();
// We are not clearing the names_added_ set. If we are in implicit wildcard subscription mode,
// then the set should already be empty. If we are in explicit wildcard mode then the set will
// contain the names we explicitly requested, but we need to add * to the list to make sure it's
// sent too.
if (mode_ == WildcardMode::Explicit) {
names_added_.insert("*");
}
names_removed_.clear();
}
Expand Down Expand Up @@ -214,26 +243,44 @@ DeltaSubscriptionState::getNextRequestWithAck(const UpdateAck& ack) {
}

void DeltaSubscriptionState::addResourceState(
const envoy::service::discovery::v3::Resource& resource) {
const envoy::service::discovery::v3::Resource& resource, ResourceType type) {
krnowak marked this conversation as resolved.
Show resolved Hide resolved
if (resource.has_ttl()) {
ttl_.add(std::chrono::milliseconds(DurationUtil::durationToMilliseconds(resource.ttl())),
resource.name());
} else {
ttl_.clear(resource.name());
}

resource_state_[resource.name()] = ResourceState(resource);
resource_names_.insert(resource.name());
if (auto it = resource_state_.find(resource.name()); it != resource_state_.end()) {
auto old_type = it->second.type();
it->second = ResourceState(resource, effectiveResourceType(old_type, type));
} else {
resource_state_.insert({resource.name(), ResourceState(resource, type)});
}
}

bool DeltaSubscriptionState::setResourceWaitingForServer(const std::string& resource_name) {
krnowak marked this conversation as resolved.
Show resolved Hide resolved
auto itr = resource_state_.find(resource_name);
if (itr == resource_state_.end()) {
return false;
}
auto old_type = itr->second.type();
itr->second = ResourceState(old_type);
return true;
}

void DeltaSubscriptionState::setResourceWaitingForServer(const std::string& resource_name) {
resource_state_[resource_name] = ResourceState();
resource_names_.insert(resource_name);
void DeltaSubscriptionState::addResourceWaitingForServer(const std::string& resource_name,
krnowak marked this conversation as resolved.
Show resolved Hide resolved
ResourceType type) {
if (auto it = resource_state_.find(resource_name); it != resource_state_.end()) {
auto old_type = it->second.type();
it->second = ResourceState(effectiveResourceType(old_type, type));
} else {
resource_state_.insert({resource_name, ResourceState(type)});
}
}

void DeltaSubscriptionState::removeResourceState(const std::string& resource_name) {
resource_state_.erase(resource_name);
resource_names_.erase(resource_name);
}

} // namespace Config
Expand Down
Loading