Azure / Azure/azure-sdk-for-rust

[Cosmos] Binary Encoding Negotiation on the Gateway 2.0 / Thin-Client Path

Open
#5,209 0 comments 1 reaction 1 assignee Claimed by @aavasthy View on GitHub
binary-encoding Cosmos
Dominant language
Rust
Stars
884
Forks
365
Avg merge
2d 19h
Merged PRs (30d)
109

Description

## Summary

Cosmos binary JSON encoding is negotiated with the `x-ms-cosmos-supported-serialization-formats` HTTP header. That header is honored on the **standard gateway** path only. On the **Gateway 2.0 / thin-client** path the driver re-encodes the request as an RNTBD metadata token list, and there is no `SupportedSerializationFormats` token in the catalog — so the negotiation is silently dropped and the service responds with **text**.

This is pending item **#1** in [`BINARY_ENCODING_HLD.md` — Pending work][hld-pending], and divergence **#3** in the Rust vs .NET parity table of the same document.

## Why it matters (customer-visible, not benign)

Binary encoding exists in large part to fix the integral-`Double` → integer divergence (#5028).
When negotiation is dropped:

- The service returns text; `serde_json` parses an integral value as `f64`. A wide integer that round-trips correctly on the standard gateway can then fail typed deserialization on a thin-client account — **same code, same document, different account configuration**.
- The RU and bandwidth saving is forfeited with **no signal**. There is no way to distinguish "binary negotiated" from "binary received" other than counting bytes on the wire.
- The failure is silent and account-shape-dependent, which makes it hard to diagnose in the field.

The gap applies to every operation that negotiates a binary response on Gateway 2.0: point item ops (`create` / `read` / `replace` / `upsert`) and `query`.

> Note: the request-side encode is **not** affected — `apply_request_binary_encoding` transcodes the
> body before RNTBD framing, and the frame carries those bytes verbatim. This issue is specifically
> about **response negotiation**.

## Current behavior

- `CosmosDriver::apply_response_negotiation` ([`driver/cosmos_driver.rs`][driver]) sets `request_headers.supported_serialization_formats` to `"CosmosBinary"` for point ops, or `"JsonText,CosmosBinary"` for query.

- On Gateway 2.0, [`gateway_v2_dispatch.rs`][dispatch] builds the RNTBD metadata token list from a fixed set of HTTP headers. `SUPPORTED_SERIALIZATION_FORMATS` is not among them, and the outer HTTP request is a POST whose body is the RNTBD frame — so the header never reaches the backend.

- Result: the backend applies its default (text) and the response carries no `0x80` preamble, so `is_binary` correctly routes it to the text path. Everything "works", just without binary.

## Proposed fix

Mirror the existing `SupportedQueryFeatures` (`0x00FF`) forwarding pattern:

1. **Add the request token** to [`transport/rntbd/tokens.rs`][tokens]:
- New `RntbdRequestToken::SupportedSerializationFormats` variant carrying its wire ID and token
type, wired into both `TryFrom` and `value()`.
- A `Token::supported_serialization_formats(..)` constructor.
2. **Forward the header** in [`transport/gateway_v2_dispatch.rs`][dispatch]: read
`request_header_names::SUPPORTED_SERIALIZATION_FORMATS`, skip empty values, and push the token.
Keep it adjacent to the existing `SupportedQueryFeatures` / `QueryVersion` forwarding block.
3. **Confirm the value encoding** the proxy expects — string accept-list
(`"JsonText,CosmosBinary"`) versus an enum byte. This must be pinned against the proxy's contract,
not inferred: an earlier String-typed `ReadConsistencyStrategy` prototype caused the proxy to
hang, so a wrong token type is not a benign failure.
4. **Emulator support**: teach `in_memory_emulator/gateway_v2.rs` to read and honor the new token, so
the existing binary-encoding tests can run against the hosted Gateway 2.0 path without a live
account.
5. **Docs**: remove the "Gateway 2.0 limitation (follow-up)" callout from
[`BINARY_ENCODING_SPEC.md` §2][spec], drop pending item #1 and parity divergence #3 from
[`BINARY_ENCODING_HLD.md`][hld], and add the token row to the RNTBD token catalog in
[`GATEWAY_V2_SPEC.md`][gw2-spec].

## Open questions

- **Token ID and type.** Must be confirmed against the reference implementations (.NET `RntbdConstants.RntbdRequestTokenIdentifiers.SupportedSerializationFormats`, Java
`RntbdConstants.RntbdRequestHeader.SupportedSerializationFormats`) before any value is committed to `tokens.rs`. Do not guess.

- **Is the token in `thinClientProxyExcludedSet`?** If the proxy explicitly excludes it, the fix is a proxy-side change and this issue becomes a service dependency rather than an SDK one.

- **Older proxy versions.** Does an unknown or unsupported token cause a hard failure, or is it skipped? If it can fail, emission may need to be gated on a capability signal.

- **Query accept-list semantics on Gateway 2.0.** Does the proxy honor `"JsonText,CosmosBinary"` per page the way the standard gateway does?

## Acceptance criteria

- [ ] With binary encoding enabled against a Gateway 2.0 account, point item ops (`create` / `read` / `replace` / `upsert`) receive `0x80`-prefixed binary responses.
- [ ] With binary encoding enabled against a Gateway 2.0 account, `query_items` receives binary feed pages: single-partition, passthrough cross-partition, streaming `ORDER BY`, and `OFFSET` / `LIMIT`.
- [ ] Unit test: the RNTBD metadata token list built for a binary-negotiated operation contains the `SupportedSerializationFormats` token with the expected value, and omits it when binary encoding is disabled.
- [ ] Unit test: a round-trip through `RntbdRequestFrame::read` / `write` preserves the token.
- [ ] Emulator-backed integration test covering the Gateway 2.0 plus binary encoding combination.
- [ ] The existing wide-integer / `u64` fidelity assertions pass on the Gateway 2.0 path — see
[`BINARY_ENCODING_U64_MAX_ANALYSIS.md`][u64] for the known-impossible cases to keep excluded.
- [ ] [`binary_encoding_roundtrip_fuzz`][fuzzer] runs green with the Gateway 2.0 transport
configuration added to its config matrix.
- [ ] Behavior is unchanged when binary encoding is off — no new token on the wire.
- [ ] Docs updated: spec, HLD pending list, HLD parity table, and the Gateway 2.0 token catalog.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.