Skip to content

ChangeFeedEstimator: Adds support for inmemory lease container#5827

Merged
yash2710 merged 8 commits into
mainfrom
users/trivediyash/fix-inmemory-estimator-null-lease
May 20, 2026
Merged

ChangeFeedEstimator: Adds support for inmemory lease container#5827
yash2710 merged 8 commits into
mainfrom
users/trivediyash/fix-inmemory-estimator-null-lease

Conversation

@yash2710
Copy link
Copy Markdown
Contributor

@yash2710 yash2710 commented May 4, 2026

Description

Change feed estimator threw ArgumentNullException when an inmemory lease container was being used. Update validations so in-memory lease containers work with change feed estimator

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines:
Successfully started running 1 pipeline(s).

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

All good!

@NaluTripician
Copy link
Copy Markdown
Contributor

@sdkReviewAgent-2

@xinlian12
Copy link
Copy Markdown
Member

Review complete (29:15)

Posted 2 inline comment(s).

Steps: ✓ context, correctness, cross-sdk, design, history, past-prs, synthesis, test-coverage

@yash2710
Copy link
Copy Markdown
Contributor Author

yash2710 commented May 7, 2026

@sdkReviewAgent-2

@xinlian12
Copy link
Copy Markdown
Member

Review complete (23:29)

Posted 1 inline comment(s).

Steps: ✓ context, correctness, cross-sdk, design, history, past-prs, synthesis, test-coverage

@yash2710
Copy link
Copy Markdown
Contributor Author

yash2710 commented May 7, 2026

@sdkReviewAgent-2

@xinlian12
Copy link
Copy Markdown
Member

Review complete (25:26)

No new comments — existing review coverage is sufficient.

Steps: ✓ context, correctness, cross-sdk, design, history, past-prs, synthesis, test-coverage

@yash2710
Copy link
Copy Markdown
Contributor Author

@sdkReviewAgent-2

@xinlian12
Copy link
Copy Markdown
Member

Review complete (33:30)

No new comments — existing review coverage is sufficient.

Steps: ✓ context, correctness, cross-sdk, design, history, past-prs, synthesis, test-coverage

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.

Overall looks good, thanks @yash2710 , can you please add the changelog entry.

@yash2710
Copy link
Copy Markdown
Contributor Author

Overall looks good, thanks @yash2710 , can you please add the changelog entry.

@copilot add a change log entry for this change

Copy link
Copy Markdown
Contributor

Copilot AI commented May 19, 2026

Overall looks good, thanks @yash2710 , can you please add the changelog entry.

@copilot add a change log entry for this change

Added the changelog entry in changelog.md and pushed it in 54e4fd9.

kushagraThapar
kushagraThapar previously approved these changes May 19, 2026
Comment thread changelog.md Outdated
yash2710 and others added 2 commits May 19, 2026 13:43
- Sync changelog to new Unreleased structure (PR 5827 entry under Features Added)

- Add Assert.IsNotNull to StartAsync_WithInMemoryLeaseContainer_BuildsEstimatorSuccessfully

- Add StartAsync_TriggersDelegate_WithInMemoryLeaseContainer to cover runner->iterator wiring

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@yash2710
Copy link
Copy Markdown
Contributor Author

Addressed in 5884530 (on top of the merge with main):

  • xinlian12 / ananth7592 (runner→iterator coverage): Added Assert.IsNotNull(estimator) to StartAsync_WithInMemoryLeaseContainer_BuildsEstimatorSuccessfully so the build-only test has an explicit assertion, and added a new StartAsync_TriggersDelegate_WithInMemoryLeaseContainer test modeled on StartAsync_TriggersDelegate. It composes the runner with a custom DocumentServiceLeaseStoreManager (non-null LeaseContainer) and leaseContainer: null, mocks GetCurrentStateIterator(...).ReadNextAsync(...), and asserts the estimation delegate receives the mocked remaining work — exercising the runner → iterator wiring at the in-memory lease boundary.
  • kushagraThapar (changelog): Merged main into the branch and moved the entry to the new ### Unreleased#### Features Added section as [5827] ChangeFeedEstimator: Adds support for in-memory lease container. (the prior entry had landed under the already-released 3.61.0-preview.0 section).
  • xinlian12''s earlier suggestions to propagate the null-check to ChangeFeedProcessorCore and to update the ChangeFeedProcessorCoreTests mocks (plus add ApplyBuildConfiguration_ValidatesCustomStoreWithNullLeaseContainer) were already covered in the prior Address comments commits.

All 42 tests in ChangeFeedEstimatorRunnerTests + ChangeFeedProcessorCoreTests + ChangeFeedEstimatorIteratorTests pass locally.

@yash2710 yash2710 enabled auto-merge (squash) May 19, 2026 20:49
kushagraThapar
kushagraThapar previously approved these changes May 19, 2026
ananth7592
ananth7592 previously approved these changes May 19, 2026
Copy link
Copy Markdown
Member

@ananth7592 ananth7592 left a comment

Choose a reason for hiding this comment

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

LGTM, Thanks Yash

@yash2710 yash2710 dismissed stale reviews from ananth7592 and kushagraThapar via a7f5ca7 May 20, 2026 17:51
@yash2710 yash2710 changed the title CFP: Adds support for inmemory lease container to feed estimator ChangeFeedEstimator: Adds support for inmemory lease container May 20, 2026
Added bug fix for ChangeFeedEstimator with in-memory lease container.
Comment thread changelog.md
@yash2710 yash2710 merged commit 3f86916 into main May 20, 2026
33 checks passed
@yash2710 yash2710 deleted the users/trivediyash/fix-inmemory-estimator-null-lease branch May 20, 2026 22:22
Meghana-Palaparthi added a commit that referenced this pull request Jun 2, 2026
## Release 3.61.0

### Version Changes
| Property | Previous | New |
|---|---|---|
| ClientOfficialVersion | 3.60.0 | 3.61.0 |
| ClientPreviewVersion | 3.61.0 | 3.62.0 |
| ClientPreviewSuffixVersion | preview.0 | preview.0 |

### Changelog (3.61.0 GA)

#### Features Added
- [5815](#5815) Read
Consistency Strategy: Adds hub region header for
LastCommittedSingleWriteRegion strategy.
- [5848](#5848)
VectorEmbeddingPolicy: Adds EmbeddingSource block to Embedding model
- [5867](#5867)
Diagnostics: Adds hedging detection API
- [5600](#5600) HPK:
Adds id to partition key when "/id" is the last path in partition key
definition.

#### Bugs Fixed
- [5827](#5827)
ChangeFeedEstimator: Change feed estimator threw ArgumentNullException
when an inmemory lease container was being used.
- [5910](#5910)
Upgraded Direct package to 3.43.2.
- [5910](#5910)
Direct: Fixed RNTBD thread-pool starvation by making
Dispatcher.OnIdleTimer asynchronous.

#### Other Changes
- [5887](#5887)
Direct: Documents that MaxTcpConnectionsPerEndpoint accepts any positive
value.

### Changelog (3.62.0-preview.0 - preview only)

#### Features Added
- [5838](#5838)
EmbeddingGenerator: Adds ICosmosEmbeddingGenerator client-wide
configuration (preview)
- [5911](#5911)
DistributedTransaction: Adds DistributedReadTransaction and
DistributedWriteTransaction APIs (preview)

### API Contract Diff (GA)
```diff
diff --git "a/Microsoft.Azure.Cosmos\\contracts\\API_3.60.0.txt" "b/Microsoft.Azure.Cosmos\\contracts\\API_3.61.0.txt"
index ad39f9b..767b792 100644
--- "a/Microsoft.Azure.Cosmos\\contracts\\API_3.60.0.txt"
+++ "b/Microsoft.Azure.Cosmos\\contracts\\API_3.61.0.txt"
@@ -434,7 +434,10 @@ namespace Microsoft.Azure.Cosmos
         public abstract IReadOnlyList<ValueTuple<string, Uri>> GetContactedRegions();
         public virtual int GetFailedRequestCount();
         public virtual ServerSideCumulativeMetrics GetQueryMetrics();
+        public virtual IReadOnlyList<RequestedRegion> GetRequestedRegions();
+        public virtual IReadOnlyList<string> GetRespondedRegions();
         public virtual Nullable<DateTime> GetStartTimeUtc();
+        public virtual bool HedgingStarted();
         public abstract override string ToString();
     }
     public class CosmosException : Exception, ICloneable
@@ -1033,6 +1036,28 @@ namespace Microsoft.Azure.Cosmos
         public const string WestUS2 = "West US 2";
         public const string WestUS3 = "West US 3";
     }
+    public struct RequestedRegion : IEquatable<RequestedRegion>
+    {
+        public RequestedRegion(string regionName, RequestedRegionReason reason);
+        public RequestedRegionReason Reason { get; }
+        public string RegionName { get; }
+        public bool Equals(RequestedRegion other);
+        public override bool Equals(object obj);
+        public override int GetHashCode();
+        public override string ToString();
+    }
+    public enum RequestedRegionReason : byte
+    {
+        CircuitBreakerProbe = (byte)6,
+        Hedging = (byte)4,
+        Initial = (byte)1,
+        OperationRetry = (byte)2,
+        RegionFailover = (byte)5,
+        TransportRetry = (byte)3,
+        Unknown = (byte)0,
+    }
     public abstract class RequestHandler
     {
         protected RequestHandler();

```

### API Contract Diff (Preview)
```diff
diff --git "a/Microsoft.Azure.Cosmos\\contracts\\API_3.61.0-preview.0.txt" "b/Microsoft.Azure.Cosmos\\contracts\\API_3.62.0-preview.0.txt"
index efd0a07..02f51c1 100644
--- "a/Microsoft.Azure.Cosmos\\contracts\\API_3.61.0-preview.0.txt"
+++ "b/Microsoft.Azure.Cosmos\\contracts\\API_3.62.0-preview.0.txt"
@@ -367,6 +367,7 @@ namespace Microsoft.Azure.Cosmos
         public CosmosClient(string connectionString, CosmosClientOptions clientOptions=null);
         public CosmosClient(string accountEndpoint, string authKeyOrResourceToken, CosmosClientOptions clientOptions=null);
         public virtual CosmosClientOptions ClientOptions { get; }
+        public virtual ICosmosEmbeddingGenerator EmbeddingGenerator { get; }
         public virtual Uri Endpoint { get; }
         public virtual CosmosResponseFactory ResponseFactory { get; }
         public static Task<CosmosClient> CreateAndInitializeAsync(string accountEndpoint, AzureKeyCredential authKeyOrResourceTokenCredential, IReadOnlyList<ValueTuple<string, string>> containers, CosmosClientOptions cosmosClientOptions=null, CancellationToken cancellationToken=default(CancellationToken));
@@ -378,6 +379,8 @@ namespace Microsoft.Azure.Cosmos
         public virtual Task<DatabaseResponse> CreateDatabaseIfNotExistsAsync(string id, ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken));
         public virtual Task<DatabaseResponse> CreateDatabaseIfNotExistsAsync(string id, Nullable<int> throughput=default(Nullable<int>), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken));
         public virtual Task<ResponseMessage> CreateDatabaseStreamAsync(DatabaseProperties databaseProperties, Nullable<int> throughput=default(Nullable<int>), RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken));
+        public virtual DistributedReadTransaction CreateDistributedReadTransaction();
+        public virtual DistributedWriteTransaction CreateDistributedWriteTransaction();
         public void Dispose();
         protected virtual void Dispose(bool disposing);
         public virtual Container GetContainer(string databaseId, string containerId);
@@ -401,6 +404,7 @@ namespace Microsoft.Azure.Cosmos
         public Nullable<ConsistencyLevel> ConsistencyLevel { get; set; }
         public CosmosClientTelemetryOptions CosmosClientTelemetryOptions { get; set; }
         public Collection<RequestHandler> CustomHandlers { get; }
+        public ICosmosEmbeddingGenerator EmbeddingGenerator { get; set; }
         public Nullable<bool> EnableContentResponseOnWrite { get; set; }
         public bool EnableRemoteRegionPreferredForSessionRetry { get; set; }
         public bool EnableTcpConnectionEndpointRediscovery { get; set; }
@@ -511,9 +515,19 @@ namespace Microsoft.Azure.Cosmos
         public abstract IReadOnlyList<ValueTuple<string, Uri>> GetContactedRegions();
         public virtual int GetFailedRequestCount();
         public virtual ServerSideCumulativeMetrics GetQueryMetrics();
+        public virtual IReadOnlyList<RequestedRegion> GetRequestedRegions();
+        public virtual IReadOnlyList<string> GetRespondedRegions();
         public virtual Nullable<DateTime> GetStartTimeUtc();
+        public virtual bool HedgingStarted();
         public abstract override string ToString();
     }
+    public sealed class CosmosEmbeddingResult
+    {
+        public CosmosEmbeddingResult(IReadOnlyList<ReadOnlyMemory<float>> vectors, Nullable<int> totalTokens=default(Nullable<int>), Nullable<TimeSpan> latency=default(Nullable<TimeSpan>));
+        public Nullable<TimeSpan> Latency { get; }
+        public Nullable<int> TotalTokens { get; }
+        public IReadOnlyList<ReadOnlyMemory<float>> Vectors { get; }
+    }
     public class CosmosException : Exception, ICloneable
     {
         public CosmosException(string message, HttpStatusCode statusCode, int subStatusCode, string activityId, double requestCharge);
@@ -668,6 +682,69 @@ namespace Microsoft.Azure.Cosmos
         DotProduct = 2,
         Euclidean = 0,
     }
+    public abstract class DistributedReadTransaction : DistributedTransaction
+    {
+        protected DistributedReadTransaction();
+        public abstract DistributedReadTransaction ReadItem(Container container, PartitionKey partitionKey, string id, DistributedTransactionRequestOptions requestOptions=null);
+    }
+    public abstract class DistributedTransaction
+    {
+        protected DistributedTransaction();
+        public abstract Task<DistributedTransactionResponse> CommitTransactionAsync(CancellationToken cancellationToken=default(CancellationToken));
+    }
+    public class DistributedTransactionOperationResult
+    {
+        public virtual string ETag { get; }
+        public virtual int Index { get; }
+        public virtual bool IsSuccessStatusCode { get; }
+        public virtual double RequestCharge { get; }
+        public virtual Stream ResourceStream { get; }
+        public virtual HttpStatusCode StatusCode { get; }
+    }
+    public class DistributedTransactionOperationResult<T> : DistributedTransactionOperationResult
+    {
+        protected DistributedTransactionOperationResult();
+        public virtual T Resource { get; set; }
+    }
+    public class DistributedTransactionRequestOptions : RequestOptions
+    {
+        public DistributedTransactionRequestOptions();
+        public string SessionToken { get; set; }
+    }
+    public class DistributedTransactionResponse : IDisposable, IEnumerable, IEnumerable<DistributedTransactionOperationResult>, IReadOnlyCollection<DistributedTransactionOperationResult>, IReadOnlyList<DistributedTransactionOperationResult>
+    {
+        protected DistributedTransactionResponse();
+        public virtual string ActivityId { get; }
+        public virtual int Count { get; }
+        public virtual CosmosDiagnostics Diagnostics { get; }
+        public virtual string DiagnosticString { get; }
+        public virtual string ErrorMessage { get; }
+        public virtual Headers Headers { get; }
+        public virtual Guid IdempotencyToken { get; }
+        public virtual bool IsRetriable { get; }
+        public virtual bool IsSuccessStatusCode { get; }
+        public virtual DistributedTransactionOperationResult this[int index] { get; }
+        public virtual double RequestCharge { get; }
+        public virtual HttpStatusCode StatusCode { get; }
+        public void Dispose();
+        protected virtual void Dispose(bool disposing);
+        public virtual IEnumerator<DistributedTransactionOperationResult> GetEnumerator();
+        public virtual DistributedTransactionOperationResult<T> GetOperationResultAtIndex<T>(int index);
+        IEnumerator System.Collections.IEnumerable.GetEnumerator();
+    }
+    public abstract class DistributedWriteTransaction : DistributedTransaction
+    {
+        protected DistributedWriteTransaction();
+        public abstract DistributedWriteTransaction CreateItemStream(Container container, PartitionKey partitionKey, string id, Stream streamPayload, DistributedTransactionRequestOptions requestOptions=null);
+        public abstract DistributedWriteTransaction CreateItem<T>(Container container, PartitionKey partitionKey, string id, T resource, DistributedTransactionRequestOptions requestOptions=null);
+        public abstract DistributedWriteTransaction DeleteItem(Container container, PartitionKey partitionKey, string id, DistributedTransactionRequestOptions requestOptions=null);
+        public abstract DistributedWriteTransaction PatchItem(Container container, PartitionKey partitionKey, string id, IReadOnlyList<PatchOperation> patchOperations, DistributedTransactionRequestOptions requestOptions=null);
+        public abstract DistributedWriteTransaction PatchItemStream(Container container, PartitionKey partitionKey, string id, Stream streamPayload, DistributedTransactionRequestOptions requestOptions=null);
+        public abstract DistributedWriteTransaction ReplaceItemStream(Container container, PartitionKey partitionKey, string id, Stream streamPayload, DistributedTransactionRequestOptions requestOptions=null);
+        public abstract DistributedWriteTransaction ReplaceItem<T>(Container container, PartitionKey partitionKey, string id, T resource, DistributedTransactionRequestOptions requestOptions=null);
+        public abstract DistributedWriteTransaction UpsertItemStream(Container container, PartitionKey partitionKey, string id, Stream streamPayload, DistributedTransactionRequestOptions requestOptions=null);
+        public abstract DistributedWriteTransaction UpsertItem<T>(Container container, PartitionKey partitionKey, string id, T resource, DistributedTransactionRequestOptions requestOptions=null);
+    }
     public class Embedding : IEquatable<Embedding>
     {
         public Embedding();
@@ -809,6 +886,10 @@ namespace Microsoft.Azure.Cosmos
         IEnumerator System.Collections.IEnumerable.GetEnumerator();
         public virtual bool TryGetValue(string headerName, out string value);
     }
+    public interface ICosmosEmbeddingGenerator
+    {
+        Task<CosmosEmbeddingResult> GenerateEmbeddingsAsync(IReadOnlyList<string> texts, string endpoint, string deploymentName, int dimensions, CancellationToken cancellationToken=default(CancellationToken));
+    }
     public sealed class IncludedPath
     {
         public IncludedPath();
@@ -1157,6 +1238,28 @@ namespace Microsoft.Azure.Cosmos
         public const string WestUS2 = "West US 2";
         public const string WestUS3 = "West US 3";
     }
+    public struct RequestedRegion : IEquatable<RequestedRegion>
+    {
+        public RequestedRegion(string regionName, RequestedRegionReason reason);
+        public RequestedRegionReason Reason { get; }
+        public string RegionName { get; }
+        public bool Equals(RequestedRegion other);
+        public override bool Equals(object obj);
+        public override int GetHashCode();
+        public override string ToString();
+    }
+    public enum RequestedRegionReason : byte
+    {
+        CircuitBreakerProbe = (byte)6,
+        Hedging = (byte)4,
+        Initial = (byte)1,
+        OperationRetry = (byte)2,
+        RegionFailover = (byte)5,
+        TransportRetry = (byte)3,
+        Unknown = (byte)0,
+    }
     public abstract class RequestHandler
     {
         protected RequestHandler();
@@ -1526,6 +1629,7 @@ namespace Microsoft.Azure.Cosmos.Fluent
         public CosmosClientBuilder WithContentResponseOnWrite(bool contentResponseOnWrite);
         public CosmosClientBuilder WithCustomAccountEndpoints(IEnumerable<Uri> customAccountEndpoints);
         public CosmosClientBuilder WithCustomSerializer(CosmosSerializer cosmosJsonSerializer);
+        public CosmosClientBuilder WithEmbeddingGenerator(ICosmosEmbeddingGenerator embeddingGenerator);
         public CosmosClientBuilder WithEnableRemoteRegionPreferredForSessionRetry(bool enableRemoteRegionPreferredForSessionRetry);
         public CosmosClientBuilder WithFaultInjection(IFaultInjector faultInjector);
         public CosmosClientBuilder WithHttpClientFactory(Func<HttpClient> httpClientFactory);

```

### Checklist
- [X] Changelog entries reviewed by team
- [ ] API contract diff reviewed by Kiran and Kirill
- [ ] Preview APIs reviewed (email sent to
azurecosmossdkdotnet@microsoft.com)
- [ ] Kiran sign-off obtained

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

6 participants