Skip to content

Commit bd8965e

Browse files
Tests: Refactors 409 dedup tests to use (PK, id) uniqueness simulation
Replaces order-dependent callCount-based mock with ConcurrentDictionary tracking (PartitionKey, id) composite keys. The mock now returns 409 only when the same (PK, id) pair is seen again, faithfully simulating Cosmos DB's per-logical-partition id uniqueness constraint regardless of call order. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 19922d2 commit bd8965e

1 file changed

Lines changed: 40 additions & 15 deletions

File tree

Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ChangeFeed/DocumentServiceLeaseManagerCosmosTests.cs

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Microsoft.Azure.Cosmos.ChangeFeed.Tests
66
{
77
using System;
8+
using System.Collections.Concurrent;
89
using System.IO;
910
using System.Net;
1011
using System.Threading;
@@ -329,9 +330,10 @@ public async Task CreatesPartitionKeyBasedLeaseWithLegacyGuidPartitionKeyWhenOpt
329330
}
330331

331332
/// <summary>
332-
/// Verifies the full 409 dedup chain: first call succeeds (200), second concurrent call for the
333-
/// same lease token gets 409 Conflict and returns null. This proves the deterministic PK value
334-
/// ensures both creates land in the same logical partition, triggering the id-uniqueness conflict.
333+
/// Verifies the 409 dedup chain by simulating Cosmos DB's uniqueness constraint:
334+
/// a duplicate (PartitionKey, id) combination returns 409 Conflict regardless of call order.
335+
/// This proves the deterministic PK value ensures both creates land in the same logical
336+
/// partition and trigger the id-uniqueness conflict.
335337
/// </summary>
336338
[TestMethod]
337339
public async Task CreateLeaseIfNotExistAsync_FirstSucceeds_SecondReturns409_PKRange()
@@ -350,7 +352,8 @@ public async Task CreateLeaseIfNotExistAsync_FirstSucceeds_SecondReturns409_PKRa
350352
MaxExclusive = "FF"
351353
};
352354

353-
int callCount = 0;
355+
// Track created (PartitionKey, id) pairs to simulate Cosmos DB's per-logical-partition id uniqueness.
356+
ConcurrentDictionary<string, byte> createdPkIdPairs = new ConcurrentDictionary<string, byte>();
354357
Mock<ContainerInternal> mockedContainer = new Mock<ContainerInternal>();
355358
mockedContainer.Setup(c => c.CreateItemStreamAsync(
356359
It.IsAny<Stream>(),
@@ -359,9 +362,20 @@ public async Task CreateLeaseIfNotExistAsync_FirstSucceeds_SecondReturns409_PKRa
359362
It.IsAny<CancellationToken>()))
360363
.ReturnsAsync((Stream stream, PartitionKey partitionKey, ItemRequestOptions opts, CancellationToken token) =>
361364
{
362-
return Interlocked.Increment(ref callCount) == 1
363-
? new ResponseMessage(HttpStatusCode.OK) { Content = stream }
364-
: new ResponseMessage(HttpStatusCode.Conflict);
365+
// Read the lease id from the stream to form the composite key
366+
stream.Position = 0;
367+
using StreamReader reader = new StreamReader(stream, leaveOpen: true);
368+
string json = reader.ReadToEnd();
369+
stream.Position = 0;
370+
string leaseId = Newtonsoft.Json.Linq.JObject.Parse(json)["id"].ToString();
371+
372+
string compositeKey = $"{partitionKey}:{leaseId}";
373+
if (createdPkIdPairs.TryAdd(compositeKey, 0))
374+
{
375+
return new ResponseMessage(HttpStatusCode.OK) { Content = stream };
376+
}
377+
378+
return new ResponseMessage(HttpStatusCode.Conflict);
365379
});
366380

367381
DocumentServiceLeaseManagerCosmos leaseManager = new DocumentServiceLeaseManagerCosmos(
@@ -375,9 +389,9 @@ public async Task CreateLeaseIfNotExistAsync_FirstSucceeds_SecondReturns409_PKRa
375389
DocumentServiceLease firstResult = await leaseManager.CreateLeaseIfNotExistAsync(partitionKeyRange, continuation);
376390
Assert.IsNotNull(firstResult, "First create should succeed with 200.");
377391

378-
// Second call: host B races for the same lease token, gets 409 (dedup)
392+
// Second call: host B races for the same lease token — same (PK, id) triggers 409
379393
DocumentServiceLease secondResult = await leaseManager.CreateLeaseIfNotExistAsync(partitionKeyRange, continuation);
380-
Assert.IsNull(secondResult, "Second create should return null due to 409 Conflict (dedup).");
394+
Assert.IsNull(secondResult, "Second create should return null due to 409 Conflict (same PK + id dedup).");
381395
}
382396

383397
[TestMethod]
@@ -392,7 +406,8 @@ public async Task CreateLeaseIfNotExistAsync_FirstSucceeds_SecondReturns409_EPK(
392406

393407
FeedRangeEpk feedRangeEpk = new FeedRangeEpk(new Documents.Routing.Range<string>("AA", "BB", true, false));
394408

395-
int callCount = 0;
409+
// Track created (PartitionKey, id) pairs to simulate Cosmos DB's per-logical-partition id uniqueness.
410+
ConcurrentDictionary<string, byte> createdPkIdPairs = new ConcurrentDictionary<string, byte>();
396411
Mock<ContainerInternal> mockedContainer = new Mock<ContainerInternal>();
397412
mockedContainer.Setup(c => c.CreateItemStreamAsync(
398413
It.IsAny<Stream>(),
@@ -401,9 +416,19 @@ public async Task CreateLeaseIfNotExistAsync_FirstSucceeds_SecondReturns409_EPK(
401416
It.IsAny<CancellationToken>()))
402417
.ReturnsAsync((Stream stream, PartitionKey partitionKey, ItemRequestOptions opts, CancellationToken token) =>
403418
{
404-
return Interlocked.Increment(ref callCount) == 1
405-
? new ResponseMessage(HttpStatusCode.OK) { Content = stream }
406-
: new ResponseMessage(HttpStatusCode.Conflict);
419+
stream.Position = 0;
420+
using StreamReader reader = new StreamReader(stream, leaveOpen: true);
421+
string json = reader.ReadToEnd();
422+
stream.Position = 0;
423+
string leaseId = Newtonsoft.Json.Linq.JObject.Parse(json)["id"].ToString();
424+
425+
string compositeKey = $"{partitionKey}:{leaseId}";
426+
if (createdPkIdPairs.TryAdd(compositeKey, 0))
427+
{
428+
return new ResponseMessage(HttpStatusCode.OK) { Content = stream };
429+
}
430+
431+
return new ResponseMessage(HttpStatusCode.Conflict);
407432
});
408433

409434
DocumentServiceLeaseManagerCosmos leaseManager = new DocumentServiceLeaseManagerCosmos(
@@ -417,9 +442,9 @@ public async Task CreateLeaseIfNotExistAsync_FirstSucceeds_SecondReturns409_EPK(
417442
DocumentServiceLease firstResult = await leaseManager.CreateLeaseIfNotExistAsync(feedRangeEpk, continuation);
418443
Assert.IsNotNull(firstResult, "First create should succeed with 200.");
419444

420-
// Second call: host B races for the same lease token, gets 409 (dedup)
445+
// Second call: host B races for the same lease token — same (PK, id) triggers 409
421446
DocumentServiceLease secondResult = await leaseManager.CreateLeaseIfNotExistAsync(feedRangeEpk, continuation);
422-
Assert.IsNull(secondResult, "Second create should return null due to 409 Conflict (dedup).");
447+
Assert.IsNull(secondResult, "Second create should return null due to 409 Conflict (same PK + id dedup).");
423448
}
424449

425450
/// <summary>

0 commit comments

Comments
 (0)