dotnet / dotnet/orleans

InconsistentStateException thrown within IAsyncEnumerable grain method does not behave as expected

Open
#9,652 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
C#
Stars
10.9k
Forks
2.1k
Avg merge
15h 1m
Merged PRs (30d)
345

Description

### Describe the bug
If an `InconsistentStateException` is thrown as a result of a call to grain method that grain activation is deactivated automatically by the runtime.
If an `InconsistentStateException` is thrown as a result of a call to a grain method that returns `IAsyncEnumerable `the grain activation is **not** automatically deactivated by the runtime.

Because `InconsistentStateException` is not handled by Orleans and `InconsistentStateException.IsSourceActivation` remains set to `true` if the caller is also a grain it will cause it to deactivate instead, unless of course the caller grain is another IAsyncEnumerable method affected by this behaviour.

### To Reproduce

Implement a grain that throws `InconsistentStateException `within a method that returns `IAsyncEnumerable `like `GetListAsync()` in the example below:

This test will fail as the activationId remains the same.

```cs

var activationIdBefore = await grain.GetActivationId();

var enumerator = grain.GetListAsync().GetAsyncEnumerator(token);

_ = Assert.ThrowsAsync(async () => await enumerator.MoveNextAsync().AsTask());

Assert.That(await grain.GetActivationId(), Is.Not.EqualTo(activationIdBefore));
```

### Workaround

One workaround is to call `DeactivateOnIdle()` in a handler for `InconsistentStateException` from the enumeration return by implementing `IIncomingGrainCallFilter` on any grain with `IAsyncEnumerable `returns
```cs

public async Task Invoke(IIncomingGrainCallContext context)
{
await context.Invoke();
if (context.Response is Response> { TypedResult.Item1: EnumerationResult.Error, TypedResult.Item2: InconsistentStateException ise } enumResult)
{
enumResult.TypedResult = (enumResult.TypedResult.Item1, new EnumerationAbortedException(ise.Message, ise)); // Wrap exception to avoid caller deactivation
DeactivateOnIdle();
}
}
```

### Further technical details

Orleans 9.2.1

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.