ChangeFeedProcessor: Fixes first-change skip during initial startup by anchoring StartTime#5617
Merged
kirankumarkolli merged 14 commits intomasterfrom Apr 8, 2026
Conversation
Addresses #5268 by setting an implicit StartTime during first StartAsync when no explicit start options are set. Also adds unit coverage for StartTime option behavior and an emulator regression test for immediate writes after StartAsync.
…r-startasync-5268
…r-startasync-5268
…r-startasync-5268
Member
|
Please update Title with initialization state also |
…r-startasync-5268
Address review feedback to handle case where more than 10 docs could be processed in the callback. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
NaluTripician
commented
Mar 4, 2026
Contributor
Author
NaluTripician
left a comment
There was a problem hiding this comment.
Re: DynamicTests.cs line 116 — @kirankumarkolli
Good catch — the == 10 check is fragile. If the callback fires with a batch that crosses 10 (e.g., count goes from 8→12), the ManualResetEvent would never be set and the test would time out. Changed to >= 10 to be defensive. Pushed in a4dec3f.
Contributor
Author
|
Updated the PR title to include initialization state context as requested. |
…r-startasync-5268
kirankumarkolli
previously approved these changes
Mar 11, 2026
…r-startasync-5268
…r-startasync-5268
…r-startasync-5268
…r-startasync-5268 Resolve conflict in AvailabilityStrategyUnitTests.cs by keeping master's version. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…r-startasync-5268
Praveen-Msft
approved these changes
Mar 31, 2026
…r-startasync-5268
kirankumarkolli
approved these changes
Apr 8, 2026
4 tasks
microsoft-github-policy-service Bot
pushed a commit
that referenced
this pull request
Apr 25, 2026
## Release 3.59.0 ### Version Changes - ClientOfficialVersion: 3.58.0 → 3.59.0 - ClientPreviewVersion: 3.59.0 → 3.60.0 - ClientPreviewSuffixVersion: preview.0 → preview.0 ### Changelog (3.59.0 GA) #### Added - [5579](#5579) Change Feed Processor: Adds Lease container export support - [5709](#5709) Performance: Adds caching for URL-encoded AAD authorization signature - [5731](#5731) DNS dot-suffix: Adds TCP DNS dot-suffix for Direct mode to avoid Kubernetes ndots latency - [5755](#5755) Exceptionless: Adds enabling exception less 400 status code - [5756](#5756) Exceptionless: Adds enabling exception less 404/1002 status code - [5757](#5757) Exceptionless: Adds enabling exception less 403 - [5779](#5779) Direct: Adds Direct package version bump to 3.42.4 - [5786](#5786) Region Availability: Adds missing regions from Direct 3.42.4 - [5788](#5788) Socket Handler: Adds HTTP/2 PING keep-alive to detect broken connections in pool #### Fixed - [5553](#5553) NativeDLLs: Fixes Conditionally include win-x64 native DLLs based on RuntimeIdentifier - [5588](#5588) LINQ: Fixes memory leak from Expression.Compile() in all call sites - [5617](#5617) ChangeFeedProcessor: Fixes first-change skip during initial startup by anchoring StartTime - [5636](#5636) CosmosClientBuilder: Fixes self-referencing loop in GetSerializedConfiguration with STJ TypeInfoResolver - [5748](#5748) Routing: Fixes GetOverlappingRanges CPU overhead from repeated JSON deserialization - [5807](#5807) ChangeFeedProcessor: Fixes lease de-duplication for /partitionKey-partitioned lease containers ### Changelog (3.60.0-preview.0) #### Added - [5804](#5804) SemanticReranking: Adds Configurable Request Timeout #### Fixed - [5783](#5783) Container: Fixes SemanticRerankAsync TypeLoadException in derived classes ### API Contract Diff (GA) ```diff diff --git "a/Microsoft.Azure.Cosmos\\contracts\\API_3.58.0.txt" "b/Microsoft.Azure.Cosmos\\contracts\\API_3.59.0.txt" index 1b74a69..6fa9352 100644 --- "a/Microsoft.Azure.Cosmos\\contracts\\API_3.58.0.txt" +++ "b/Microsoft.Azure.Cosmos\\contracts\\API_3.59.0.txt" @@ -60,6 +60,7 @@ namespace Microsoft.Azure.Cosmos public ChangeFeedProcessor Build(); public ChangeFeedProcessorBuilder WithErrorNotification(Container.ChangeFeedMonitorErrorDelegate errorDelegate); public virtual ChangeFeedProcessorBuilder WithInMemoryLeaseContainer(); + public virtual ChangeFeedProcessorBuilder WithInMemoryLeaseContainer(MemoryStream leaseState); public ChangeFeedProcessorBuilder WithInstanceName(string instanceName); public ChangeFeedProcessorBuilder WithLeaseAcquireNotification(Container.ChangeFeedMonitorLeaseAcquireDelegate acquireDelegate); public ChangeFeedProcessorBuilder WithLeaseConfiguration(Nullable<TimeSpan> acquireInterval=default(Nullable<TimeSpan>), Nullable<TimeSpan> expirationInterval=default(Nullable<TimeSpan>), Nullable<TimeSpan> renewInterval=default(Nullable<TimeSpan>)); @@ -956,6 +957,7 @@ namespace Microsoft.Azure.Cosmos public const string NorwayWest = "Norway West"; public const string PolandCentral = "Poland Central"; public const string QatarCentral = "Qatar Central"; + public const string SaudiArabiaEast = "Saudi Arabia East"; public const string SingaporeCentral = "Singapore Central"; public const string SingaporeNorth = "Singapore North"; public const string SouthAfricaNorth = "South Africa North"; @@ -963,6 +965,7 @@ namespace Microsoft.Azure.Cosmos public const string SouthCentralUS = "South Central US"; public const string SouthCentralUS2 = "South Central US 2"; public const string SoutheastAsia = "Southeast Asia"; + public const string SoutheastAsia3 = "Southeast Asia 3"; public const string SoutheastUS = "Southeast US"; public const string SoutheastUS3 = "Southeast US 3"; public const string SoutheastUS5 = "Southeast US 5"; @@ -990,6 +993,7 @@ namespace Microsoft.Azure.Cosmos public const string USSecWest = "USSec West"; public const string USSecWestCentral = "USSec West Central"; public const string WestCentralUS = "West Central US"; + public const string WestCentralUSFRE = "West Central US FRE"; public const string WestEurope = "West Europe"; public const string WestIndia = "West India"; public const string WestUS = "West US"; ``` ### API Contract Diff (Preview) ```diff diff --git "a/Microsoft.Azure.Cosmos\\contracts\\API_3.59.0-preview.0.txt" "b/Microsoft.Azure.Cosmos\\contracts\\API_3.60.0-preview.0.txt" index 1ae52c0..58df10f 100644 --- "a/Microsoft.Azure.Cosmos\\contracts\\API_3.59.0-preview.0.txt" +++ "b/Microsoft.Azure.Cosmos\\contracts\\API_3.60.0-preview.0.txt" @@ -91,6 +91,7 @@ namespace Microsoft.Azure.Cosmos public ChangeFeedProcessor Build(); public ChangeFeedProcessorBuilder WithErrorNotification(Container.ChangeFeedMonitorErrorDelegate errorDelegate); public virtual ChangeFeedProcessorBuilder WithInMemoryLeaseContainer(); + public virtual ChangeFeedProcessorBuilder WithInMemoryLeaseContainer(MemoryStream leaseState); public ChangeFeedProcessorBuilder WithInstanceName(string instanceName); public ChangeFeedProcessorBuilder WithLeaseAcquireNotification(Container.ChangeFeedMonitorLeaseAcquireDelegate acquireDelegate); public ChangeFeedProcessorBuilder WithLeaseConfiguration(Nullable<TimeSpan> acquireInterval=default(Nullable<TimeSpan>), Nullable<TimeSpan> expirationInterval=default(Nullable<TimeSpan>), Nullable<TimeSpan> renewInterval=default(Nullable<TimeSpan>)); @@ -302,7 +303,7 @@ namespace Microsoft.Azure.Cosmos public abstract Task<ResponseMessage> ReplaceItemStreamAsync(Stream streamPayload, string id, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); public abstract Task<ThroughputResponse> ReplaceThroughputAsync(ThroughputProperties throughputProperties, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); public abstract Task<ThroughputResponse> ReplaceThroughputAsync(int throughput, RequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); - public abstract Task<SemanticRerankResult> SemanticRerankAsync(string rerankContext, IEnumerable<string> documents, IDictionary<string, object> options=null, CancellationToken cancellationToken=default(CancellationToken)); + public virtual Task<SemanticRerankResult> SemanticRerankAsync(string rerankContext, IEnumerable<string> documents, IDictionary<string, object> options=null, CancellationToken cancellationToken=default(CancellationToken)); public abstract Task<ItemResponse<T>> UpsertItemAsync<T>(T item, Nullable<PartitionKey> partitionKey=default(Nullable<PartitionKey>), ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); public abstract Task<ResponseMessage> UpsertItemStreamAsync(Stream streamPayload, PartitionKey partitionKey, ItemRequestOptions requestOptions=null, CancellationToken cancellationToken=default(CancellationToken)); public delegate Task ChangeFeedHandlerWithManualCheckpoint<T>(ChangeFeedProcessorContext context, IReadOnlyCollection<T> changes, Func<Task> checkpointAsync, CancellationToken cancellationToken); @@ -407,6 +408,7 @@ namespace Microsoft.Azure.Cosmos public int GatewayModeMaxConnectionLimit { get; set; } public Func<HttpClient> HttpClientFactory { get; set; } public Nullable<TimeSpan> IdleTcpConnectionTimeout { get; set; } + public TimeSpan InferenceRequestTimeout { get; set; } public bool LimitToEndpoint { get; set; } public Nullable<int> MaxRequestsPerTcpConnection { get; set; } public Nullable<int> MaxRetryAttemptsOnRateLimitedRequests { get; set; } @@ -1092,6 +1094,7 @@ namespace Microsoft.Azure.Cosmos public const string NorwayWest = "Norway West"; public const string PolandCentral = "Poland Central"; public const string QatarCentral = "Qatar Central"; + public const string SaudiArabiaEast = "Saudi Arabia East"; public const string SingaporeCentral = "Singapore Central"; public const string SingaporeNorth = "Singapore North"; public const string SouthAfricaNorth = "South Africa North"; @@ -1099,6 +1102,7 @@ namespace Microsoft.Azure.Cosmos public const string SouthCentralUS = "South Central US"; public const string SouthCentralUS2 = "South Central US 2"; public const string SoutheastAsia = "Southeast Asia"; + public const string SoutheastAsia3 = "Southeast Asia 3"; public const string SoutheastUS = "Southeast US"; public const string SoutheastUS3 = "Southeast US 3"; public const string SoutheastUS5 = "Southeast US 5"; @@ -1126,6 +1130,7 @@ namespace Microsoft.Azure.Cosmos public const string USSecWest = "USSec West"; public const string USSecWestCentral = "USSec West Central"; public const string WestCentralUS = "West Central US"; + public const string WestCentralUSFRE = "West Central US FRE"; public const string WestEurope = "West Europe"; public const string WestIndia = "West India"; public const string WestUS = "West US"; @@ -1504,6 +1509,7 @@ namespace Microsoft.Azure.Cosmos.Fluent public CosmosClientBuilder WithEnableRemoteRegionPreferredForSessionRetry(bool enableRemoteRegionPreferredForSessionRetry); public CosmosClientBuilder WithFaultInjection(IFaultInjector faultInjector); public CosmosClientBuilder WithHttpClientFactory(Func<HttpClient> httpClientFactory); + public CosmosClientBuilder WithInferenceRequestTimeout(TimeSpan inferenceRequestTimeout); public CosmosClientBuilder WithLimitToEndpoint(bool limitToEndpoint); public CosmosClientBuilder WithPriorityLevel(PriorityLevel priorityLevel); public CosmosClientBuilder WithReadConsistencyStrategy(ReadConsistencyStrategy readConsistencyStrategy); ``` ### Checklist - [ ] 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> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This was referenced Apr 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes issue #5268 where
ChangeFeedProcessorcan skip the first change when started without explicit start options.Bug Description
When
ChangeFeedProcessoris started with default options (noWithStartTime, no continuation, no start-from-beginning), it uses the default start-from-now path.This path anchors at the first service read (
IfNoneMatch: *) rather than atStartAsync()call time. Because startup includes asynchronous lease acquisition and load balancing, there is a window where documents can be written afterStartAsync()returns but before the first read reaches the backend. Those documents can be skipped.Root Cause
PartitionLoadBalancerCore.Start()schedules background work and returns immediately.Nowbehavior is based on first read timing, notStartAsync()timing.Fix
In
ChangeFeedProcessorCore.StartAsync():StartFromBeginning == falseStartTime == nullStartContinuationis null/emptychangeFeedProcessorOptions.StartTime = DateTime.UtcNowbefore async initialization.This anchors start behavior at processor startup time and closes the timing gap.
Why This Is Safe
Tests Added
Unit tests
Microsoft.Azure.Cosmos.Tests/ChangeFeed/ChangeFeedProcessorCoreTests.csStartAsync_SetsStartTime_WhenNoStartOptionsProvidedStartAsync_DoesNotOverrideExplicitStartTimeStartAsync_DoesNotSetStartTime_WhenStartFromBeginningEmulator regression test
Microsoft.Azure.Cosmos.EmulatorTests/ChangeFeed/DynamicTests.csTestWithRunningProcessor_ImmediateWriteAfterStartValidation
A/B test on the new emulator regression
Additional checks
Files Changed
Microsoft.Azure.Cosmos/src/ChangeFeedProcessor/ChangeFeedProcessorCore.csMicrosoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/ChangeFeed/ChangeFeedProcessorCoreTests.csMicrosoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/ChangeFeed/DynamicTests.cs