-
Notifications
You must be signed in to change notification settings - Fork 2
Address review feedback on idempotency docs and stores #69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Address review feedback on idempotency docs and stores #69
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR improves the Orleans idempotency grain to properly handle command restarts and enhances the memory cache idempotency store's concurrency management while adding comprehensive documentation.
- Enhanced Orleans grain logic to block restarting already completed commands while allowing failed command retries
- Replaced global locking with per-command semaphores in the memory cache store for better concurrency
- Added detailed documentation covering command correlation/tracing identifiers and idempotency architecture
Reviewed Changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Added extensive documentation on command correlation/tracing identifiers and idempotency architecture |
| MemoryCacheCommandIdempotencyStore.cs | Replaced global semaphore with per-command locking system using reference counting |
| CommandIdempotencyExtensions.cs | Refactored execution flow and improved null result handling |
| CommandTests.cs | Added tests for timestamp validation and GUID version verification |
| CommandIdempotencyTests.cs | Added comprehensive concurrency and edge case tests |
| OrleansCommandIdempotencyStore.cs | Enhanced result retrieval logic with proper type checking |
| CommandIdempotencyGrain.cs | Improved command restart logic to block completed commands while allowing failed retries |
| CommandIdempotencyExtensions.cs (AspNetCore) | Applied same execution flow improvements as core extensions |
| if (_commandLocks.TryRemove(commandId, out var existingLock) && !ReferenceEquals(existingLock, commandLock)) | ||
| { | ||
| _commandLocks.TryAdd(commandId, existingLock); | ||
| } |
Copilot
AI
Oct 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Race condition vulnerability: between TryRemove and TryAdd, another thread could add a different lock for the same commandId, causing the wrong lock to be restored. Consider using CompareExchange or a more atomic approach to handle concurrent modifications safely.
| if (_commandLocks.TryRemove(commandId, out var existingLock) && !ReferenceEquals(existingLock, commandLock)) | |
| { | |
| _commandLocks.TryAdd(commandId, existingLock); | |
| } | |
| _commandLocks.TryRemove(commandId, commandLock); |
| state.State.CompletedAt = null; | ||
| state.State.FailedAt = null; | ||
| } | ||
| else if (state.State.Status is not CommandExecutionStatus.NotFound and not CommandExecutionStatus.NotStarted) |
Copilot
AI
Oct 11, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Complex negated pattern matching reduces readability. Consider using positive matching with an explicit list of allowed statuses or restructure the conditional logic for better clarity.
| else if (state.State.Status is not CommandExecutionStatus.NotFound and not CommandExecutionStatus.NotStarted) | |
| else if (!(state.State.Status is CommandExecutionStatus.NotFound || state.State.Status is CommandExecutionStatus.NotStarted)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Communication/ManagedCode.Communication.Orleans/Stores/OrleansCommandIdempotencyStore.cs
Lines 87 to 99 in 3fe4476
| // New atomic operations | |
| public async Task<bool> TrySetCommandStatusAsync(string commandId, CommandExecutionStatus expectedStatus, CommandExecutionStatus newStatus, CancellationToken cancellationToken = default) | |
| { | |
| var grain = _grainFactory.GetGrain<ICommandIdempotencyGrain>(commandId); | |
| var currentStatus = await grain.GetStatusAsync(); | |
| if (currentStatus == expectedStatus) | |
| { | |
| await SetCommandStatusAsync(commandId, newStatus, cancellationToken); | |
| return true; | |
| } | |
| return false; |
The Orleans implementation of TrySetCommandStatusAsync checks the grain’s current status and returns true whenever it matches the expected value, but it does not verify that SetCommandStatusAsync actually transitioned the grain to InProgress. Two concurrent callers can both read NotFound before either call persists the new state; each then invokes SetCommandStatusAsync, where the first succeeds and the second is rejected by TryStartProcessingAsync. Because this method still returns true for the second caller, both ExecuteIdempotentAsync invocations proceed as if they have exclusive ownership and the command body runs twice. TrySetCommandStatusAsync must only return true when the underlying grain really acquired the processing state (e.g., by wiring the boolean result from TryStartProcessingAsync).
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting
💡 Codex ReviewCommunication/ManagedCode.Communication.Orleans/Stores/OrleansCommandIdempotencyStore.cs Lines 87 to 99 in 3fe4476
The Orleans implementation of ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting |
|
@codex fix all comments |
|
Summary
Testing
|
* Add grain-side status compare-and-set helper * Update ManagedCode.Communication.Orleans/Grains/CommandIdempotencyGrain.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Summary
Testing
~/.dotnet/dotnet test ManagedCode.Communication.Tests/ManagedCode.Communication.Tests.csproj(fails: ObjectDisposedException in ServiceCollectionExtensionsTests when resolving CommunicationLogger from a disposed service provider)https://chatgpt.com/codex/tasks/task_e_68e9fbc7ad5c83268e969223e18fcead