Skip to content

[Internal] DTS: Adds session-token support and typed response deserialization for transactions#5885

Merged
Meghana-Palaparthi merged 9 commits into
mainfrom
users/Meghana-Palaparthi/dtx_gap_fixes
May 27, 2026
Merged

[Internal] DTS: Adds session-token support and typed response deserialization for transactions#5885
Meghana-Palaparthi merged 9 commits into
mainfrom
users/Meghana-Palaparthi/dtx_gap_fixes

Conversation

@Meghana-Palaparthi
Copy link
Copy Markdown
Contributor

Pull Request Template

Description

This pull request introduces several improvements and new features to the distributed transactions API in the Azure Cosmos DB SDK, with a focus on enhancing typed deserialization of operation results, streamlining session token handling, and improving documentation and usability. The most important changes are outlined below:

Typed Deserialization and Result Handling:

  • Added a generic DistributedTransactionOperationResult<T> class, enabling easy access to deserialized resources from distributed transaction operations. This is returned by the new GetOperationResultAtIndex<T>(int index) method on DistributedTransactionResponse, which safely deserializes the resource body into the requested type without affecting the underlying stream. [1] [2]
  • Introduced a CreateSnapshot utility method to produce independent copies of resource streams, ensuring that repeated deserialization or direct stream access remain safe and side-effect free.

Session Token Management:

  • Added a SessionToken property to DistributedTransactionRequestOptions, allowing each operation in a distributed transaction to specify its own session token for partition-level consistency. This is now set when constructing a DistributedTransactionOperation. [1] [2]
  • Updated documentation to clarify how session tokens should be obtained and used for distributed transactions.

API Usability and Documentation:

  • Improved XML documentation for distributed read transactions, clarifying snapshot isolation semantics and providing guidance on result deserialization with the new APIs.
  • Added remarks to ResourceStream property documentation to clarify ownership and correct usage patterns.

Internal Consistency and Plumbing:

  • Ensured that the serializer is consistently propagated and available for deserialization in all relevant operation result and response objects. [1] [2] [3]

Minor Improvements:

  • Made the CommitTransactionAsync method's CancellationToken parameter optional for improved developer ergonomics.

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • [] New feature (non-breaking change which adds functionality)
  • [] Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • [] This change requires a documentation update

Closing issues

To automatically close an issue: closes #IssueNumber

Changelog

  • I have added a changelog entry under ### Unreleased in changelog.md
    for the user-facing impact of this change.
  • No changelog entry is required because this PR is one of:
    documentation-only, test-only, CI / build-only, or a pure internal refactor
    with no observable customer impact.

If the second box is checked, briefly justify here:

Feature not public yet

Meghana-Palaparthi and others added 2 commits May 18, 2026 23:54
Additive pre-public preview changes to the distributed transaction client surface.
No existing public API is broken.

Per-operation session token
- Adds `DistributedTransactionRequestOptions.SessionToken`, propagated into `DistributedTransactionOperation` (whitespace-only normalized to null) and serialized per-operation in the request body, matching the  coordinator's wire contract.
- Tokens are frozen at request-build time and re-sent unchanged on retries to preserve idempotency.

Typed response deserialization
- Adds `DistributedTransactionResponse.GetOperationResultAtIndex<T>()`  and the typed `DistributedTransactionOperationResult<T>` subclass,  mirroring `TransactionalBatchResponse`.
- Threads `CosmosSerializerCore` through both the normal and error-recovery result paths; throws `InvalidOperationException` if typed deserialization is requested without a serializer.

Safe stream lifetime
- `CosmosSerializer.FromStream<T>` disposes its input. Adds `CreateSnapshot(Stream)` which returns a fresh stream over the same bytes (zero-copy for plain `MemoryStream`; copy fallback otherwise), so the underlying `ResourceStream` survives repeated typed reads and direct indexer access.

Docs and minor cleanup
- Documents `ResourceStream` ownership/lifetime.
- Adds isolation and typed-deserialization notes on
  `DistributedReadTransaction`.
- Minor cleanup in `DistributedWriteTransactionCore`.

Tests
- Per-op session token wiring (present, absent, whitespace, multi-operation) in `DistributedTransactionCommitterTests`.
- Typed deserialization, stream-lifetime safety, post-dispose behavior, and the null-serializer guard in `DistributedTransactionResponseTests`.

Co-Authored-By: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 19, 2026

📝 Changelog reminder (non-blocking)

This PR touches shipped source but does not appear to update the
corresponding changelog.md.

Touched (and missing entry for):

  • Microsoft.Azure.Cosmos/src/** ⇒ expected an entry in changelog.md (### Unreleased)

How to decide

Use the rubric in .github/copilot-instructions.md ("Changelog
classifier") or in CONTRIBUTING.md ("Changelog entry"). Quick
version:

  • Customer-observable change (behavior, perf, memory, API) ⇒ add an entry, even if the PR is [Internal].
  • Test-only / CI-only / doc-only / pure-internal-refactor with zero customer-observable effect ⇒ no entry, add the no-changelog-needed label to silence this check.
  • Unsure? Default to adding a one-line entry under #### Other Changes. Reviewer will adjust.

This check is non-blocking — merge is not gated on it. The
reviewer is responsible for the final classification.

NaluTripician
NaluTripician previously approved these changes May 19, 2026
Copy link
Copy Markdown
Contributor

@NaluTripician NaluTripician left a comment

Choose a reason for hiding this comment

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

Looks good — approving. Implementation of the outbound session-token plumbing and the typed GetOperationResultAtIndex<T> / DistributedTransactionOperationResult<T> is correct, well-scoped, and well-tested. Nothing blocking from me.

A few non-blocking nits / follow-ups to consider:

  • Read-op session-token coverage. All four new committer tests use OperationType.Create, but the PR description leads with Read consistency as the primary use case. A single DistributedReadTransactionCore test that sets SessionToken on a Read op and asserts it reaches the request body would close the most visible coverage gap.
  • Deserialization-failure path on GetOperationResultAtIndex<T>. Happy path, no-body, no-serializer, OOB index, post-Dispose, and called-twice are all covered, but there's no test (or <exception> doc) for the case where the body JSON cannot be deserialized to T. Worth pinning the exception type with one test and an <exception> element on the XML doc.
  • Dispose hygiene in the new DistributedTransactionResponseTests. Most of the new tests create a DistributedTransactionResponse and never dispose it, which is inconsistent with the dispose-specific tests in the same file and with IDisposable hygiene in general. Prefer using DistributedTransactionResponse response = ... across all the new ones.
  • TestDocument attribute mismatch. The test type uses [System.Text.Json.Serialization.JsonPropertyName], but the runtime serializer is CosmosJsonDotNetSerializer (Newtonsoft). The attributes are silently ignored — tests pass only because property casing happens to match. Either drop the attributes or switch to [Newtonsoft.Json.JsonProperty] so the intent is honest.
  • DistributedTransactionOperationResult<T>.Resource setter. Currently { get; set; }. Every other property on the base class is { get; internal set; } — tightening this to internal set would match the existing pattern.
  • Redundant ThrowIfDisposed() in GetOperationResultAtIndex<T>. The indexer call on the next line already does this check. Harmless, but worth either removing or leaving a one-line comment noting the outer call is deliberate.
  • SessionToken doc on DistributedTransactionRequestOptions. Doc requires {partitionKeyRangeId}:{lsn} format, but the SDK doesn't validate — would be helpful to call out that an invalid format will surface as a server-side error.

None of these block. Nice work.

Copy link
Copy Markdown
Member

@kushagraThapar kushagraThapar left a comment

Choose a reason for hiding this comment

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

Hi @Meghana-Palaparthi — thanks for this thorough PR. The implementation is correct, the snapshot-based multi-call story on GetOperationResultAtIndex<T> is genuinely better than the peer batch behavior, and @NaluTripician's prior approval covers the bulk of the correctness side cleanly.

I wanted to layer in one extra angle that we're explicitly trying to call out on every Cosmos PR going forward: supportability and long-term breaking-change risk for surfaces that lock in once #if INTERNAL lifts. Below are the three highest-leverage things that I think are worth thinking about before this preview eventually GAs — all of them are non-blocking, and several are one-line fixes today that would otherwise be breaking changes after release.

  1. M1 — DTS / non-DTS session-token plumbing convergence (inline on DistributedTransactionRequestOptions.cs) — single highest 2-3-year cost decision in this PR.
  2. M2 + M3 — public surface of DistributedTransactionOperationResult diverges from TransactionalBatchOperationResult peer; SubStatusCodeValue is the only public uint sub-status in the SDK (inline on DistributedTransactionOperationResult.cs).
  3. M4 — XML doc claims V1 session-token wire format; design-doc canonical is V2 (vector) (inline on DistributedTransactionRequestOptions.cs).

Leaving this as a COMMENT review rather than an approval — @NaluTripician already has the correctness side covered; I'm just adding the supportability lens to the public record.

(We also have 8 minor + 9 nit items in our internal review notes — happy to surface any inline if helpful. Mainly: a typed IEnumerable<DistributedTransactionOperationResult<T>> projection, a [JsonExtensionData] forward-compat catch-all, decoupling C# names from wire field names via a private DTO, and a proposed extension to cosmosdb-design-docs/16-session-tokens.md covering DTS as the first surface using body-based session tokens.)

Meghana-Palaparthi and others added 2 commits May 20, 2026 11:10
Co-Authored-By: Copilot <223556219+Copilot@users.noreply.github.com>
Co-Authored-By: Copilot <223556219+Copilot@users.noreply.github.com>
kushagraThapar
kushagraThapar previously approved these changes May 22, 2026
Copy link
Copy Markdown
Member

@kushagraThapar kushagraThapar left a comment

Choose a reason for hiding this comment

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

Thanks @Meghana-Palaparthi — really appreciate the quick turnaround on the doc fix and for opening #5896 to cleanly handle the public-surface / manual-deserialization work in a separate PR. Approving. 🙏

jeet1995
jeet1995 previously approved these changes May 26, 2026
@Meghana-Palaparthi
Copy link
Copy Markdown
Contributor Author

@copilot resolve the merge conflicts in this pull request

Copilot AI dismissed stale reviews from jeet1995 and kushagraThapar via 9974454 May 26, 2026 23:25
Copy link
Copy Markdown
Contributor

Copilot AI commented May 26, 2026

@copilot resolve the merge conflicts in this pull request

Resolved the merge conflicts and merged latest main into this branch. Conflict resolution is in commit 9635a54, with a follow-up compile fix in 9974454.

@Meghana-Palaparthi Meghana-Palaparthi enabled auto-merge (squash) May 27, 2026 01:52
@Meghana-Palaparthi Meghana-Palaparthi merged commit 6c2a2b8 into main May 27, 2026
33 checks passed
@Meghana-Palaparthi Meghana-Palaparthi deleted the users/Meghana-Palaparthi/dtx_gap_fixes branch May 27, 2026 11:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants