Skip to content

Commit c86bc76

Browse files
[Internal] DTS: Adds required document id to all DTS operations (#5793)
## Description - The coordinator needs `documentId` to reliably commit or abort stuck transactions—the transaction log doesn’t store document content. - Always sending it enables deterministic replay without request bodies and avoids special cases (especially for create). - Making it mandatory keeps the SDK–backend contract future‑proof and correct. All distributed transaction operations now require an explicit 'id' parameter, consistent with Delete, Replace, and Patch. Previously, Create and Upsert omitted id from the metadata sent to the DTC coordinator (the id was only in the resource body). - Add string id parameter to CreateItem, CreateItemStream, UpsertItem, UpsertItemStream in both the abstract class and the core implementation - Validate id at call site via existing ValidateItemId (fails fast with ArgumentNullException before serialization) - Simplify DistributedTransactionOperation<T>: one constructor (with id), MaterializeResourceAsync just serializes and calls base - Update all tests > Note: To ensure consistency with Create/Upsert operations in other SDK flows, we will implement a long-term solution by extracting the id from the resource body instead of having users provide it explicitly. ## 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 --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 95cc5b1 commit c86bc76

9 files changed

Lines changed: 154 additions & 91 deletions

File tree

Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionOperation.cs

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// ------------------------------------------------------------
1+
// ------------------------------------------------------------
22
// Copyright (c) Microsoft Corporation. All rights reserved.
33
// ------------------------------------------------------------
44

@@ -78,19 +78,6 @@ internal virtual async Task MaterializeResourceAsync(CosmosSerializerCore serial
7878

7979
internal class DistributedTransactionOperation<T> : DistributedTransactionOperation
8080
{
81-
public DistributedTransactionOperation(
82-
Documents.OperationType operationType,
83-
int operationIndex,
84-
string database,
85-
string container,
86-
PartitionKey partitionKey,
87-
T resource,
88-
DistributedTransactionRequestOptions requestOptions = null)
89-
: base(operationType, operationIndex, database, container, partitionKey, id: null, requestOptions)
90-
{
91-
this.Resource = resource;
92-
}
93-
9481
public DistributedTransactionOperation(
9582
Documents.OperationType operationType,
9683
int operationIndex,

Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedTransactionSerializer.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ namespace Microsoft.Azure.Cosmos
66
{
77
using System;
88
using System.Collections.Generic;
9+
using System.Diagnostics;
910
using System.IO;
1011
using System.Text.Json;
1112
using Microsoft.Azure.Documents;
@@ -62,6 +63,9 @@ private static void WriteOperation(Utf8JsonWriter jsonWriter, DistributedTransac
6263
// collectionName
6364
jsonWriter.WriteString("collectionName", operation.Container);
6465

66+
// id
67+
jsonWriter.WriteString("id", operation.Id);
68+
6569
// collectionResourceId
6670
if (operation.CollectionResourceId != null)
6771
{
@@ -74,12 +78,6 @@ private static void WriteOperation(Utf8JsonWriter jsonWriter, DistributedTransac
7478
jsonWriter.WriteString("databaseResourceId", operation.DatabaseResourceId);
7579
}
7680

77-
// id
78-
if (operation.Id != null)
79-
{
80-
jsonWriter.WriteString("id", operation.Id);
81-
}
82-
8381
// partitionKey
8482
if (operation.PartitionKeyJson != null)
8583
{

Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedWriteTransaction.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@ abstract class DistributedWriteTransaction : DistributedTransaction
2424
/// <param name="database">The name of the database containing the container.</param>
2525
/// <param name="collection">The name of the container where the item will be created.</param>
2626
/// <param name="partitionKey">The partition key for the item.</param>
27+
/// <param name="id">The unique identifier of the item to create.</param>
2728
/// <param name="resource">The resource to create.</param>
2829
/// <param name="requestOptions">Options for the create operation.</param>
2930
/// <returns>The current <see cref="DistributedWriteTransaction"/> instance for method chaining.</returns>
3031
public abstract DistributedWriteTransaction CreateItem<T>(
3132
string database,
3233
string collection,
3334
PartitionKey partitionKey,
35+
string id,
3436
T resource,
3537
DistributedTransactionRequestOptions requestOptions = null);
3638

@@ -40,13 +42,15 @@ public abstract DistributedWriteTransaction CreateItem<T>(
4042
/// <param name="database">The name of the database containing the container.</param>
4143
/// <param name="collection">The name of the container where the item will be created.</param>
4244
/// <param name="partitionKey">The partition key for the item.</param>
45+
/// <param name="id">The unique identifier of the item to create.</param>
4346
/// <param name="streamPayload">A <see cref="Stream"/> containing the JSON serialization of the item.</param>
4447
/// <param name="requestOptions">Options for the create operation.</param>
4548
/// <returns>The current <see cref="DistributedWriteTransaction"/> instance for method chaining.</returns>
4649
public abstract DistributedWriteTransaction CreateItemStream(
4750
string database,
4851
string collection,
4952
PartitionKey partitionKey,
53+
string id,
5054
Stream streamPayload,
5155
DistributedTransactionRequestOptions requestOptions = null);
5256

@@ -147,13 +151,15 @@ public abstract DistributedWriteTransaction PatchItemStream(
147151
/// <param name="database">The name of the database containing the container.</param>
148152
/// <param name="collection">The name of the container where the item will be upserted.</param>
149153
/// <param name="partitionKey">The partition key for the item.</param>
154+
/// <param name="id">The unique identifier of the item to upsert.</param>
150155
/// <param name="resource">The resource to upsert.</param>
151156
/// <param name="requestOptions">Options for the upsert operation.</param>
152157
/// <returns>The current <see cref="DistributedWriteTransaction"/> instance for method chaining.</returns>
153158
public abstract DistributedWriteTransaction UpsertItem<T>(
154159
string database,
155160
string collection,
156161
PartitionKey partitionKey,
162+
string id,
157163
T resource,
158164
DistributedTransactionRequestOptions requestOptions = null);
159165

@@ -164,13 +170,15 @@ public abstract DistributedWriteTransaction UpsertItem<T>(
164170
/// <param name="database">The name of the database containing the container.</param>
165171
/// <param name="collection">The name of the container where the item will be upserted.</param>
166172
/// <param name="partitionKey">The partition key for the item.</param>
173+
/// <param name="id">The unique identifier of the item to upsert.</param>
167174
/// <param name="streamPayload">A <see cref="Stream"/> containing the JSON serialization of the item.</param>
168175
/// <param name="requestOptions">Options for the upsert operation.</param>
169176
/// <returns>The current <see cref="DistributedWriteTransaction"/> instance for method chaining.</returns>
170177
public abstract DistributedWriteTransaction UpsertItemStream(
171178
string database,
172179
string collection,
173180
PartitionKey partitionKey,
181+
string id,
174182
Stream streamPayload,
175183
DistributedTransactionRequestOptions requestOptions = null);
176184
}

Microsoft.Azure.Cosmos/src/DistributedTransaction/DistributedWriteTransactionCore.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@ public override DistributedWriteTransaction CreateItem<T>(
2727
string database,
2828
string collection,
2929
PartitionKey partitionKey,
30+
string id,
3031
T resource,
3132
DistributedTransactionRequestOptions requestOptions = null)
3233
{
3334
DistributedWriteTransactionCore.ValidateContainerReference(database, collection);
35+
DistributedWriteTransactionCore.ValidateItemId(id);
3436
DistributedWriteTransactionCore.ValidateResource(resource);
3537

3638
this.operations.Add(
@@ -40,6 +42,7 @@ public override DistributedWriteTransaction CreateItem<T>(
4042
database,
4143
collection,
4244
partitionKey,
45+
id,
4346
resource,
4447
requestOptions));
4548
return this;
@@ -49,10 +52,12 @@ public override DistributedWriteTransaction CreateItemStream(
4952
string database,
5053
string collection,
5154
PartitionKey partitionKey,
55+
string id,
5256
Stream streamPayload,
5357
DistributedTransactionRequestOptions requestOptions = null)
5458
{
5559
DistributedWriteTransactionCore.ValidateContainerReference(database, collection);
60+
DistributedWriteTransactionCore.ValidateItemId(id);
5661
if (streamPayload == null)
5762
{
5863
throw new ArgumentNullException(nameof(streamPayload));
@@ -65,6 +70,7 @@ public override DistributedWriteTransaction CreateItemStream(
6570
database: database,
6671
container: collection,
6772
partitionKey: partitionKey,
73+
id: id,
6874
requestOptions: requestOptions)
6975
{
7076
ResourceStream = streamPayload
@@ -214,10 +220,12 @@ public override DistributedWriteTransaction UpsertItem<T>(
214220
string database,
215221
string collection,
216222
PartitionKey partitionKey,
223+
string id,
217224
T resource,
218225
DistributedTransactionRequestOptions requestOptions = null)
219226
{
220227
DistributedWriteTransactionCore.ValidateContainerReference(database, collection);
228+
DistributedWriteTransactionCore.ValidateItemId(id);
221229
DistributedWriteTransactionCore.ValidateResource(resource);
222230

223231
this.operations.Add(
@@ -227,6 +235,7 @@ public override DistributedWriteTransaction UpsertItem<T>(
227235
database,
228236
collection,
229237
partitionKey,
238+
id,
230239
resource,
231240
requestOptions));
232241
return this;
@@ -236,10 +245,12 @@ public override DistributedWriteTransaction UpsertItemStream(
236245
string database,
237246
string collection,
238247
PartitionKey partitionKey,
248+
string id,
239249
Stream streamPayload,
240250
DistributedTransactionRequestOptions requestOptions = null)
241251
{
242252
DistributedWriteTransactionCore.ValidateContainerReference(database, collection);
253+
DistributedWriteTransactionCore.ValidateItemId(id);
243254
if (streamPayload == null)
244255
{
245256
throw new ArgumentNullException(nameof(streamPayload));
@@ -252,6 +263,7 @@ public override DistributedWriteTransaction UpsertItemStream(
252263
database: database,
253264
container: collection,
254265
partitionKey: partitionKey,
266+
id: id,
255267
requestOptions: requestOptions)
256268
{
257269
ResourceStream = streamPayload

0 commit comments

Comments
 (0)