Repository navigation
Support for IAsyncEnumerable<T> returning methods in grain observers. - #11610
ledjon-behluli wants to merge 6 commits into
Conversation
56b5aaa to
58e35b5
Compare
58e35b5 to
3a05ef2
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved concurrency races can silently drop producer results and block StopAsync indefinitely, while explicit disposal can return before producer teardown completes.
5 open issues
What changed in this PR
Adds support for observer methods returning IAsyncEnumerable<T>, reusing Orleans’ existing enumeration protocol while managing observer-specific concurrency and cleanup.
Changes:
- Adds an observer enumeration extension with synchronized iteration, cancellation, and disposal.
- Tracks observer cleanup during deregistration and shutdown, and returns
ObserverNotReachableExceptionfor requests to missing observers. - Expands integration tests for enumeration, batching, cancellation, cleanup, and observer lifecycle behavior.
| File | Description |
|---|---|
test/Orleans.DefaultCluster.Tests/Orleans.DefaultCluster.Tests.csproj |
Adds an async-enumerable test folder entry. |
test/Orleans.DefaultCluster.Tests/ObserverTests.cs |
Tests calls to deleted observers. |
test/Orleans.DefaultCluster.Tests/AsyncEnumerableGrainCallTests.cs |
Shares test infrastructure and adds observer enumeration tests. |
test/Orleans.Core.Tests/Runtime/InvokableObjectManagerTests.cs |
Tests missing-observer responses and shutdown registration rejection. |
test/Grains/TestInternalGrains/ObservableGrainObserver.cs |
Adds an observer implementation for enumeration tests. |
test/Grains/TestInternalGrains/ObservableGrain.cs |
Makes asynchronous yielding conditional in the test iterator. |
test/Grains/TestGrainInterfaces/ISimpleObserverableGrain.cs |
Adds a request-response observer interface. |
test/Grains/TestGrainInterfaces/IObservableGrainObserver.cs |
Defines observer enumeration test methods. |
src/Orleans.Core/Runtime/InvokableObjectManager.LocalObjectData.cs |
Extracts observer context handling and adds owned-extension disposal. |
src/Orleans.Core/Runtime/InvokableObjectManager.cs |
Tracks lifecycle cleanup and responds to missing-observer requests. |
src/Orleans.Core/Runtime/InvokableObjectManager.AsyncEnumerableExtension.cs |
Implements observer enumeration, synchronization, and cleanup. |
src/Orleans.Core.Abstractions/Exceptions/OrleansMessageRejectionException.cs |
Removes a trailing blank line; no behavioral change. |
src/Orleans.Core.Abstractions/Exceptions/ObserverNotAvailableException.cs |
Defines ObserverNotReachableException. |
Review effort: Balanced
|
|
||
| try | ||
| { | ||
| await state.WaitToEnterGateAsync(cancellationToken).ConfigureAwait(false); |
| var moveNextException = moveNextTask.Exception!; | ||
| result = (EnumerationResult.Error, moveNextException.InnerExceptions.Count == 1 ? moveNextException.InnerException! : moveNextException); | ||
| Done:; | ||
| if (moveNextTask is { IsCompleted: true }) |
|
|
||
| // Then we complete the response-facing task. In-flight MoveNextAsync / StartEnumeration invocations | ||
| // await this *before* returning their response, and they must NOT be gated on the teardown below. | ||
| work.Completion.TrySetResult(); |
| var values = new List<string>(); | ||
| await foreach (var value in observerRef.GetValues(cancellationToken).WithCancellation(cancellationToken)) | ||
| { | ||
| values.Add(value); | ||
| if (values.Count == 2) | ||
| { | ||
| break; | ||
| } | ||
| } |
Code coverage
Report-only conclusion: mixed. The current-main baseline is commit Coverage combines every CI test matrix job, including providers, CodeGen, .NET 8/10, Linux, Windows, and macOS, using canonical physical source and branch identities. The comparison remains report-only while normal line and branch variance is calibrated. Coverage details |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
IAsyncEnumerable<T> returning methods in grain observers.b95865d to
60d01fa
Compare
IAsyncEnumerable<T> returning methods in grain observers.
SummaryThe core enumerator protocol in The remaining problems are in lifecycle and failure semantics:
On the testing side, none of the time- or concurrency-dependent guarantees is pinned by a test: the heartbeat boundary, shutdown with a parked enumerator, and dispose-joins-teardown. Findings
Adjacent, pre-existing on Questions for the maintainer and author
|


This PR adds support for
IAsyncEnumerable<T>returning methods in grain observers. So infos that might be worth pointing out:AsyncEnumerableGrainObserverExtension, owned byLocalObjectData.IAsyncEnumerableGrainExtension,IAsyncEnumerableRequest, and code generation, so we don't duplicate the existing request/response pipeline.LocalObjectData.MoveNextAsyncandDisposeAsyncdon't overlap and stale requests can't operate on replaced enumerators.LocalObjectDatadisposal and tracking of deregistration-triggered disposals soInvokableObjectManager.StopAsyncdoesn't miss cleanup already in progress once.ObserverNotReachableExceptionwhenDispatchcan't find the local observer anymore, returning it for request-response messages while preserving the existingOneWaymessage behavior.Microsoft Reviewers: Open in CodeFlow