Skip to content
250 changes: 250 additions & 0 deletions A40-csds-support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
xDS Configuration Dump via Client Status Discovery Service in gRPC
----
* Author(s): lidizheng
* Approver: markdroth
* Status: In-Review
* Implemented in: All languages
Comment thread
markdroth marked this conversation as resolved.
Outdated
* Last updated: 2021-02-16
* Discussion at: https://groups.google.com/g/grpc-io/c/zL45YyxtJ08

## Abstract

[Client Status Discovery
Service](https://github.com/envoyproxy/envoy/blob/main/api/envoy/service/status/v3/csds.proto)
(CSDS) is a service that exposes xDS config of a given client. It’s commonly
used to query control planes for the synced xDS config of a particular sidecar
proxy. However, it can also be used to query an xDS-compliant application for
its received xDS configuration. This doc proposes a solution to implement a CSDS
servicer in gRPC, so our users can debug their service mesh easily.
Comment thread
sergiitk marked this conversation as resolved.
Outdated


## Background

Envoy [started](https://github.com/envoyproxy/envoy/pull/9383) the CSDS
development process in Dec 2019. The CSDS API has been available since xDS v2,
and it’s under active development. But, Envoy proxies do not support or
understand this service, only the control plane does. Envoy already provides
config dump via its [admin
interface](https://www.envoyproxy.io/docs/envoy/latest/operations/admin) for
years. There is no visible plan for Envoy to support serving CSDS directly as a
Comment thread
markdroth marked this conversation as resolved.
proxy.
Comment thread
sergiitk marked this conversation as resolved.
Outdated


### Related Proposals:
* [A38 - Admin Interface API](https://github.com/grpc/proposal/pull/218)
Comment thread
sergiitk marked this conversation as resolved.

## Proposal

### CSDS in gRPC
The goal of adding a CSDS servicer to gRPC is to enable programmatic access to
the operating xDS configs of a running gRPC application. To make this more
clear, an xDS-compliant application receives many xDS configs, which may be
rejected or ignored or obsoleted. **The gRPC CSDS servicer should always return
the currently accepted xDS configs.**

### Config Status in gRPC
Comment thread
markdroth marked this conversation as resolved.
Outdated

One critical field in CSDS responses is the config status. The config status is
Comment thread
sergiitk marked this conversation as resolved.
Outdated
an additional signal to help users debug, which indicates **the synchronization
state of the given xDS resources with the control plane**. CSDS designed config
statuses from a control plane point of view, and it only works at the
granularity of xDS config type.

This gRFC proposes adding following status enum to
([config_dump.proto](https://github.com/envoyproxy/envoy/blob/main/api/envoy/admin/v3/config_dump.proto).
Comment thread
lidizheng marked this conversation as resolved.
Outdated
`ClientResourceStatus` better represents the viewpoint of xDS clients and it can
be as specific as per individual xDS resources.

```protobuf
// Resource status from the view of a xDS client, which tells the synchronization
// status between the xDS client and the xDS server.
enum ClientResourceStatus {
// Resource status is not available/unknown.
UNKNOWN = 0;

// Client requested this resource but hasn't received any update from management
// server. The client will not fail requests, but will queue them until update
// arrives or the client times out waiting for the resource.
REQUESTED = 1;

// This resource has been requested by the client but has either not been
// delivered by the server or was previously delivered by the server and then
// subsequently removed from resources provided by the server. For more
// information, please refer to the :ref:`"Knowing When a Requested Resource
// Does Not Exist" <xds_protocol_resource_not_existed>` section.
DOES_NOT_EXIST = 2;

// Client received this resource and replied with ACK.
ACKED = 3;

// Client received this resource and replied with NACK.
NACKED = 4;
}
```

### ADS Parsing Logic Update: Continue After First Error

Current gRPC ADS parsing logic is when the response parser observes an error in
the ADS response, it fails the entire message and aborts parsing.

To improve the debuggability of gRPC, gRPC needs to populate **the reason** and
**affected resources** when rejecting an update response. This information
requires the ADS parser to continue parsing past the first error. Here are the
expected behavior under scenarios:

* If the entire message won’t parse: no need to record anything in CSDS, since
we don’t know what type of xDS config or what resources are being updated;
* If one resource won’t parse: don’t abort the parsing, record the error in the

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 handling is still a bit ambiguous. I guess we would only record the first error, so the parsing does not need to continue. But would attach the error message to all previously accepted resources that the current response is attempting to update.

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.

Can you specify the cases that are ambiguous here? I guess the wording here is not accurate enough, if one resource (e.g., a cluster) has validation error, the parsing should continue; or one of its field has parsing error, e.g. a config in Any failed to deserialize, the parsing should continue. In other word, the parsing should continue when possible.

If you need a reference PR, please see grpc/grpc#25329.

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.

Looks C-core is concatenating errors together if there are more than one error resources. I don't see much benefit of doing so, other than showing a giant error message while may still not showing all errors (e.g., multiple validation errors for a single resource). Anyway, I am fine with what's being implemented in C-core.

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.

I think the important part is peeking into the ADS response to see what set of resources is being updated, then pasting the error details to their error_state field. I'm fine with one error, as long as they are logged in the right place in the CSDS response, so it's actionable for users.

error string. The error string will be attached to other resources that are
Comment thread
sergiitk marked this conversation as resolved.
Outdated
successfully recognized;
* If one resource has validation error: record the error, and attach the error
string to all resources including itself.
Comment thread
sergiitk marked this conversation as resolved.
Outdated

Here is an example of NACK information handling with new ADS changes:

```
# Imagine we have endpoint A, B, C
EDS -> {A, B, C}, version 1
gRPC -> ACK
CSDS -> {Endpoint A, version 1}, {Endpoint B, version 1}, {Endpoint C, version 1}
Comment thread
lidizheng marked this conversation as resolved.
Outdated

# The newer endpoint B contains parsing error
EDS -> {A, B}, version 2
gRPC -> NACK, Failed to parse endpoint B
CSDS -> {Endpoint A, version 1, rejected version 2, rejected reason: Failed to parse endpoint B } {Endpoint B, version 1, rejected version 2, rejected reason: Failed to parse endpoint B} {Endpoint C, version 1}
Comment thread
lidizheng marked this conversation as resolved.
Outdated

# Accepted update will clean error states
EDS -> {B, C} version 3
gRPC -> ACK
CSDS -> {Endpoint A, version 1, rejected version 2, rejected reason: Failed to parse endpoint B } {Endpoint B, version 3}, {Endpoint C, version 3}
```


### xDS Config Error State

When an ADS response is rejected, gRPC should provide debug information via
CSDS. This is done via `UpdateFailureState` within the `config_dump.proto`,
Comment thread
lidizheng marked this conversation as resolved.
which includes the version, timestamp, and the reason of the rejected update:

```proto
Comment thread
markdroth marked this conversation as resolved.
Outdated
message UpdateFailureState {
...

// Time of the latest failed update attempt.
google.protobuf.Timestamp last_update_attempt = 2;

// Details about the last failed update attempt.
string details = 3;

// This is the version of the rejected resource.
string version_info = 4;
}
```

### CSDS Servicer Design

Ideally, the CSDS servicer should be reusable by our users, instead of just an
internal tool for the gRPC library. So, the CSDS servicer should be implemented
in wrapper languages and Java/Go. There are three major parts in the servicer
design:

* Cache resources in the global XdsClient;
* Collect additional metadata about the cached resource (update timestamp,
version, etc.);
* Assemble cached resources into the gigantic config dump response.
Comment thread
sergiitk marked this conversation as resolved.

For Java and Go, it’s straightforward that this feature can be implemented
bottom-up in the same language, and it’s possible to avoid an additional copy of
the message itself. But Core needs to transport the collected xDS configs across
the language boundary. The API needs to convert proto messages into C-compatible
types (e.g. `char *`, either bytes or JSON).

#### Detail: No xDS v2 Support

CSDS was meant to be a RPC service made for xDS management servers before this
proposal. But the service itself has the potential to serve client status on xDS
clients. However, during development, we found several constraints about the
existing service protocol, hence we merged several updates (see above) to
improve the CSDS service to meet our standard. The updates are made to xDS v3
and xDS v4 (alpha) only, since xDS v2 is in deprecated state. This doc proposes
to not support xDS v2 for CSDS.

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.

What to do if the underlying client is speaking v2?
Return an error? What error code to use? FailedPrecondition?

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.

By underlying client, do you mean the gPRC application? I thought gRPC processes all xDS config into v3, so the dumped xDS config is agnostic to v2/v3. If the external CSDS client is only speaking v2, they won't be able to invoke the v3 method, because the fully-qualified-method-name includes the service name which has "v3" in it. So, the v2 external CSDS client will get UNIMPLEMENTED by default, in theory.

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.

Here's what TD does for v3 (currently only v2 supported):

❯ csds-client -request_file request.yaml -api_version v3
2021/03/01 18:20:55 rpc error: code = Unimplemented desc = The GRPC target is not implemented on the server, host: trafficdirector.googleapis.com, method: /envoy.service.status.v3.ClientStatusDiscoveryService/StreamClientStatus.

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.

Nice, so in practicce, the old client will also get UNIMPLEMENTED.

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.

Note that the CSDS RPC service name is different between v2 and v3, because the major version is part of the proto package name. So I think all we need to do here is to implement only the v3 service, not the v2 service. Then clients will get UNIMPLEMENTED if they try to use the v2 service, just like they would for any other unimplemented RPC service.

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.

I think it's fine to send v2 protos inside of the Any fields in v3 CSDS. In xDS terms, the transport protocol version and the data model version do not have to be the same. I don't think we need to do any conversion here.

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.

Do you have an example that where the v2 resources might slip through

It's not one type of resource. All the resources in xds v2 are v2. E.g. v2.Cluster vs v3.Cluster, v2.Endpoint vs v3.Endpoint.

In xDS terms, the transport protocol version and the data model version do not have to be the same.

That's right.
The difference would be on the csds client side. If we send v2 protos as Any in the csds responses, the csds client needs to depend on v2 protos (not that they will be used, but their types need to be registered) so the client can unmarshal those Any.

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.

I think it's fine for CSDS client to need v2 protos if the client is using v2. That's independent of the fact that we're only supporting v3 of the CSDS RPC service itself.

This is no different than ADS -- it's possible for a client to use v3 of ADS but v2 of the resource protos, or vice versa. In fact, in Envoy, those are two independent knobs. In gRPC, we just simplified it and avoided one of the knobs by depending on the fact that we didn't happen to use any fields in v2 that were being removed in v3, so we could just unconditionally treat all of the resource protos as v3.

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.

This SGTM (and is actually easier to do :))

Let's update the gRFC to clarify this? This doesn't sound accurate:

This doc proposes to not support xDS v2 for CSDS.

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.

Updated to

For xDS CSDS v2 (the RPC service itself), this doc proposes to not support, so
we only provide v3 CSDS service and CSDS client requesting for v2 service will
get UNIMPLEMENTED error.

There are edge cases that v2 and v3 xDS resources are mixed together. This doc
proposes to keep them as-is in the CSDS responses. So, the CSDS service can
accurately reflect the xDS configs received from the control plane.

The section title also updated to xDS v2/v3/v4, since it's hard to describe our handling of v2 in the title.


#### Detail: Cache Lifecycle
Comment thread
sergiitk marked this conversation as resolved.
Outdated

gRPC doesn't use xDS messages directly, but interprets them into language-native
class/struct. The lifecycle of the cached xDS messages should be identical to
the interpreted structure in each stack, so there is no memory management logic
change needed.

#### Detail: Expose xDS Config As Is

To date, gRPC supports the majority of xDS config but not all. Alternatively, we
could only dump the config that gRPC understands. However, doing so will create
a behavior difference between the gRPC and the control plane. It might be
confusing to users that some fields are missing. Also, during the configuration
dump, Envoy also dumps its entire configuration even if it doesn’t understand
some of the configuration fields (think of fields for Envoy extensions). So, for
correctness and simplicity, we should **cache and expose the xDS config the way
gRPC receives them**.

#### Detail: Node Matching

The node matching mechanism is not designed for gRPC’s use case, but for
querying the control plane. This doc proposes to ignore the Node matching query.
Comment thread
lidizheng marked this conversation as resolved.
Outdated


## Alternatives

### Solution: Config Dump By Attaching An HTTP Server

Although it is the most straightforward way of implementing an admin interface,
Java doesn’t have a good enough built-in web server and it is challenging to
introduce yet another dependency into Core. Exposing application states via HTTP
will be challenging from engineering perspective.

### Solution: Config Dump via Channelz

Though only a subset of xDS resources is applicable to a channel, xDS configs
(listeners, routes, clusters, endpoints) are global resources to a gRPC
application. One unsolved problem is that if we choose to use CSDS to expose xDS
configs, **the users won’t have the ability to query the set of effective xDS
configs for a channel**. The CSDS is not designed for finer granularity than
individual processes.

If we decided to support sharing XdsClient across multiple channels, the
Comment thread
markdroth marked this conversation as resolved.
Outdated
boundary of each channel’s xDS config will be blurry. Injecting xDS info into a
channel tracing service won’t be the right direction in the long term.

### Solution: Config Dump via File

This approach saves the active xDS configuration onto a memory-based file system
location, like `sysctl`, or Envoy Runtime. However, due to the lack of two-way
communication and the complexity of the new machinery, this solution could cost
more engineering resources but yield less functionality.


## Implementation

### CSDS Proto Updates

* [envoy#13121]: the config synchronization status was defined from the xDS
management server point of view, this PR adds a set of ENUM to present the
config synchronization status from a client-side view.
* [envoy#14689]: the CSDS focused on dumping the in-effective xDS configs, but
we can do better to improve gRPC’s debuggability. This PR adds fields to
config dump protos to allow CSDS to return information about the rejected
update.
* [envoy#14900]: this PR adds two additional status to client configs, the
REQUESTED and the DOES_NOT_EXIST (read more).
Comment thread
lidizheng marked this conversation as resolved.
Outdated

### CSDS Implementation

* Core: https://github.com/grpc/grpc/pull/25038
* Golang: To be linked
* Java: To be linked

[envoy#13121]: https://github.com/envoyproxy/envoy/pull/13121

[envoy#14689]: https://github.com/envoyproxy/envoy/pull/14689

[envoy#14900]:https://github.com/envoyproxy/envoy/pull/14900