Cosmos: Add Binary Encoding Support for Thinclient Mode - #5284
Arooshi Avasthy (aavasthy) wants to merge 8 commits into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved moderate findings require wire-level response verification, emulator forwarding coverage, and related documentation updates.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR enables Cosmos binary-response negotiation over Gateway 2.0/thin-client requests using RNTBD token 0x00C4.
Changes:
- Adds serialization-format token encoding and forwarding.
- Updates emulator handling and transport/E2E tests.
- Updates Gateway 2.0 and binary-encoding documentation.
File summaries
| File | Summary | Review findings |
|---|---|---|
sdk/cosmos/azure_data_cosmos/tests/gateway_v2_tests/gateway_v2_e2e.rs |
Adds Gateway 2.0 binary-encoding scenarios. | Moderate (3 votes): Assert the raw 0x80 binary preamble; deserialization alone passes with text responses. Moderate (1 vote): Verify TransportKind::GatewayV2, not merely activity IDs. Nit (1 vote): Clarify that the accept-list does not guarantee a binary response. Moderate (1 vote): Validate the outbound token or raw backend response before page processing. |
sdk/cosmos/azure_data_cosmos_driver/src/in_memory_emulator/gateway_v2.rs |
Handles the new token in the emulator. | Moderate (1 vote): Preserve the binary-format flag through decode_request or add a Gateway 2.0 raw-response test. |
sdk/cosmos/azure_data_cosmos_driver/src/driver/transport/rntbd/tokens.rs |
Defines token 0x00C4 and its mapping. |
No final findings. |
sdk/cosmos/azure_data_cosmos_driver/src/driver/transport/rntbd/request.rs |
Tests token serialization round trips. | No final findings. |
sdk/cosmos/azure_data_cosmos_driver/src/driver/transport/gateway_v2_dispatch.rs |
Forwards and parses the serialization-format header. | No final findings. |
sdk/cosmos/azure_data_cosmos_driver/docs/GATEWAY_V2_SPEC.md |
Documents the Gateway 2.0 negotiation contract. | Nit (2 votes): Add corresponding entries to both affected crate changelogs. |
sdk/cosmos/azure_data_cosmos_driver/docs/BINARY_ENCODING_SPEC.md |
Updates binary-encoding feature status. | Nit (1 vote): Remove “+ negotiation” from the deferred-status text. Nit (1 vote): Add release-note entries to both affected crate changelogs. |
sdk/cosmos/azure_data_cosmos_driver/docs/BINARY_ENCODING_HLD.md |
Updates remaining work and parity status. | No final findings. |
Review details
Suppressed comments (7)
sdk/cosmos/azure_data_cosmos/tests/gateway_v2_tests/gateway_v2_e2e.rs:779
- The assertion compares deserialized
RichBinaryDocvalues; it never compares the response bytes, andinto_modelaccepts text as well as binary. This wording overstates what the test proves—either call it a decoded-value equality check or inspect the raw body before deserializing if byte-for-byte fidelity is required.
/// Writes a **rich, deeply-nested** document (see [`RichBinaryDoc`]) over
/// Gateway 2.0 with binary encoding enabled, then reads it back and asserts it
/// is byte-for-byte equal.
sdk/cosmos/azure_data_cosmos/tests/gateway_v2_tests/gateway_v2_e2e.rs:751
- An activity ID is present on both Gateway V1 and Gateway 2.0 responses, so this loop does not verify that the query pages used the transport under test. A regression that routes the query through the standard gateway would still pass; use the same
assert_transport_kind(..., TransportKind::GatewayV2)check used by the existing Gateway 2.0 query tests.
assert!(
!page.diagnostics().activity_id().as_str().is_empty(),
"every binary-negotiated Gateway 2.0 query page must surface an activity-id",
);
sdk/cosmos/azure_data_cosmos/tests/gateway_v2_tests/gateway_v2_e2e.rs:702
JsonText,CosmosBinaryis an accept-list, and the driver contract explicitly allows the service to answer a query in text. Therefore this comment's claim that the accept-list 'so the feed returns binary' is not guaranteed; describe binary as a possible negotiated response, or make the test assert the raw response only when a wire capture confirms it.
/// Queries advertise a `JsonText,CosmosBinary` accept-list, so the feed returns
/// binary. Exercises the query leg: the same `0x00C4` token rides the request
/// and the binary feed decodes item-by-item back to the typed model.
sdk/cosmos/azure_data_cosmos/tests/gateway_v2_tests/gateway_v2_e2e.rs:752
- The page/item assertions can all succeed with a text response if the new token is missing, and the query pipeline may re-encode its synthesized page afterward. This therefore does not prove that the service negotiated binary over the thin-client hop; validate the outbound
0x00C4token and/or the raw backend response before page processing.
assert!(
!page.diagnostics().activity_id().as_str().is_empty(),
"every binary-negotiated Gateway 2.0 query page must surface an activity-id",
);
for item in page.items() {
sdk/cosmos/azure_data_cosmos_driver/docs/BINARY_ENCODING_SPEC.md:71
- The delivery-status follow-up later in this document still says
query request-body encoding + negotiationis deferred. This update makes only query request-body encoding remain deferred, so please remove+ negotiationthere; otherwise the document contradicts the new status shown here.
| `query_items` | — (text spec) | decode | ✅ done (response negotiated, standard gateway + Gateway 2.0) |
sdk/cosmos/azure_data_cosmos_driver/docs/BINARY_ENCODING_SPEC.md:88
- This is a customer-visible transport behavior change, but the current Unreleased entries in
azure_data_cosmos_driver/CHANGELOG.md:17andazure_data_cosmos/CHANGELOG.md:18still say Gateway 2.0 does not carry the negotiation header, and no replacement entries are added. Add release-note bullets for the affected driver and SDK crates before documenting the feature as complete.
Response negotiation is honored on **both** the standard gateway and the
Gateway 2.0 / thin-client path. On Gateway 2.0 the request is re-encoded as an
RNTBD metadata token list; the `x-ms-cosmos-supported-serialization-formats`
header is forwarded as the RNTBD `SupportedSerializationFormats` token
(`0x00C4`, Byte flags: `JsonText = 0x01`, `CosmosBinary = 0x02`,
`HybridRow = 0x04`), so the negotiation survives thin-client wrapping and the
service returns binary just as it does on the standard gateway.
sdk/cosmos/azure_data_cosmos_driver/src/in_memory_emulator/gateway_v2.rs:386
- The hosted Gateway V2 emulator accepts this token and then discards it, so
decode_requestreconstructs an HTTP request withoutx-ms-cosmos-supported-serialization-formatsand always produces text responses for this path. The existing binary-response tests cover direct gateway requests, not this thin-client wrapper, leaving the new Gateway V2 negotiation untested locally; preserve the CosmosBinary bit through the wrapper or add an explicit Gateway V2 raw-response test.
RntbdRequestToken::SupportedSerializationFormats => {
// Emulator doesn't model binary negotiation: validate the wire
// type, ignore the value, always reply text.
expect_byte(kind, token.value)?;
- Files reviewed: 8/8 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
|
|
||
| let read_resp = container.read_item(&pk_value, &item_id, None).await?; | ||
| assert_transport_kind(&read_resp.diagnostics(), TransportKind::GatewayV2); | ||
| let read_item: GwV2TestItem = read_resp.into_model()?; |
There was a problem hiding this comment.
Added a dedicated test, gateway_v2_binary_encoding_wire_format_differential , that reads the raw response body and asserts the 0x80 binary preamble (and contrasts it with a text body from a binary-disabled client on the same item).
| @@ -345,6 +345,8 @@ Per Q3 resolution, the Gateway 2.0 proxy requires the header names `x-ms-thincli | |||
|
|
|||
| **Tenant identification (RNTBD token, not HTTP header)**: the proxy identifies the target Cosmos account from the existing RNTBD `GlobalDatabaseAccountName` token (`0x00CE`, `String`, optional) carried inside the RNTBD metadata stream on **every** Gateway 2.0 request. No Gateway-2.0-specific HTTP headers are introduced for account or regional-account identification — the RNTBD token is the canonical carrier and matches the proxy wire contract. The value is the global database account name (e.g., `myacct` from `myacct.documents.azure.com`), parsed once from the account endpoint URL at client construction. | |||
|
|
|||
| **Binary-response negotiation (RNTBD token, not HTTP header)**: the client advertises which serialization formats it accepts in responses via the `x-ms-cosmos-supported-serialization-formats` HTTP header (a string accept-list, e.g. `JsonText,CosmosBinary`). On the thin-client path this header is forwarded into the RNTBD metadata stream as the `SupportedSerializationFormats` token (`0x00C4`, `Byte`), where the value is the flags byte the backend parses that accept-list into: `JsonText = 0x01`, `CosmosBinary = 0x02`, `HybridRow = 0x04`, bitwise-OR-combinable (so `JsonText,CosmosBinary` → `0x03`). The token is omitted entirely when the header is absent or resolves to no known format (byte `0`), so behavior is unchanged when binary encoding is off. This is what lets point item ops (`create` / `read` / `replace` / `upsert`) and `query_items` receive `0x80`-prefixed binary responses on a Gateway 2.0 account, at parity with the standard gateway. The ID and byte values are pinned against the server catalog `RntbdRequestTokenIdentifiers_SupportedSerializationFormats` / `RntbdSupportedSerializationFormats`. | |||
There was a problem hiding this comment.
updated
# Conflicts: # sdk/cosmos/docs/specs/0014-binary-encoding-high-level-design.md
After merging main, the live thin-client binary query test raced the proxy's query-routing propagation window (404/1003 OwnerResourceNotFound) because it still used pre-refactor direct create_item/query_items calls. Route seed writes through create_seed_item (retries the transient 401 MAC-signature window) and wrap the query drain in retry_query_owner_not_found, matching main's refactored test infra. Binary-specific decode assertions are preserved. All 3 live binary e2e tests pass against the thin-client account. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/azp run rust - cosmos - weekly |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
Above need to be corrected in the PR description. Binary encoding is enabled by default. |
|
/azp run rust - cosmos - weekly |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
Tomas Varon (tvaron3)
left a comment
There was a problem hiding this comment.
LGTM, just some none blocking comments
| metadata.query_version = Some(expect_small_string(kind, token.value)?) | ||
| } | ||
| RntbdRequestToken::SupportedSerializationFormats => { | ||
| // Emulator doesn't model binary negotiation: validate the wire |
There was a problem hiding this comment.
non blocking but I think it's worth adding especially because this will be the default behavior and will help further testing in other sdks
There was a problem hiding this comment.
the emulator now captures the SupportedSerializationFormats token and re-emits the negotiation header, so the shared dispatch returns a binary response (as it will be the default). Added a unit test covering it.
| let seen_ids = retry_query_owner_not_found(|| async { | ||
| let query = Query::from("SELECT * FROM c"); | ||
| let mut pages = container | ||
| .query_items::<GwV2TestItem>(query, FeedScope::partition(pk_value.clone()), None) |
There was a problem hiding this comment.
test coverage for cross partition queries would also be useful
There was a problem hiding this comment.
Added gateway_v2_binary_encoding_cross_partition_query_round_trip — a binary-enabled full_container() query that seeds items across multiple physical partitions and asserts every item returns once and decodes.
789f9fa to
5d78a95
Compare
|
/azp run rust - cosmos - weekly |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
Binary encoding is turned on with the
x-ms-cosmos-supported-serialization-formatsheader. This works on the standardgateway, but not on the Gateway 2.0 / thin-client path. There, requests are repackaged into RNTBD tokens, and this header had no matching token — so it was dropped and the service always replied in plain text.
This change adds that missing token (
SupportedSerializationFormats,0x00C4) and forwards the header to it on the Gateway 2.0 path, the same waySupportedQueryFeaturesalready works. Binary encoding stays on by default.