feat(mediator): add optimistic concurrency to sample#797
Closed
AndreaCuneo with Copilot wants to merge 5 commits into
Closed
feat(mediator): add optimistic concurrency to sample#797AndreaCuneo with Copilot wants to merge 5 commits into
AndreaCuneo with Copilot wants to merge 5 commits into
Conversation
Co-authored-by: AndreaCuneo <5227688+AndreaCuneo@users.noreply.github.com>
Co-authored-by: AndreaCuneo <5227688+AndreaCuneo@users.noreply.github.com>
Co-authored-by: AndreaCuneo <5227688+AndreaCuneo@users.noreply.github.com>
Co-authored-by: AndreaCuneo <5227688+AndreaCuneo@users.noreply.github.com>
Copilot created this pull request from a session on behalf of
AndreaCuneo
July 26, 2026 11:17
View session
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> Signed-off-by: Andrea Cuneo <kaildio@gmail.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds optimistic concurrency to the MediatorFramework sample by introducing a version token (ROWVERSION in SQL / incrementing version in-memory), retrying transient concurrency conflicts via a request-handler decorator, and documenting the deferred ETag/If-Match follow-up in the task board.
Changes:
- Add
Versionconcurrency token toGreetingResponseand persist it via SQLROWVERSION+ in-memory versioning. - Add
OptimisticConcurrencyRetrierDecorator<,>and register it for allIRequestHandler<,>to retry optimistic conflicts. - Add initial tests for the retrier and in-memory stale-version rejection; update SMP-04 task docs as complete.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| samples/Ark.MediatorFramework.Sample/test/Ark.MediatorFramework.Sample.Tests/OptimisticConcurrencyTests.cs | Adds tests for retrier behavior and in-memory stale-version rejection. |
| samples/Ark.MediatorFramework.Sample/src/Ark.MediatorFramework.Sample.Database/dbo/Tables/Greeting.sql | Adds ROWVERSION column for SQL optimistic concurrency. |
| samples/Ark.MediatorFramework.Sample/src/Ark.MediatorFramework.Sample.Application/SampleDataContext.cs | Adds version-aware SQL persistence and reads/writes the Version token. |
| samples/Ark.MediatorFramework.Sample/src/Ark.MediatorFramework.Sample.Application/OptimisticConcurrencyRetrierDecorator.cs | Introduces retry decorator for optimistic concurrency exceptions. |
| samples/Ark.MediatorFramework.Sample/src/Ark.MediatorFramework.Sample.Application/GreetingStore.cs | Adds in-memory versioning and stale-version detection. |
| samples/Ark.MediatorFramework.Sample/src/Ark.MediatorFramework.Sample.Application/GreetingContracts.cs | Extends response contract with Version token for future transport-level concurrency. |
| samples/Ark.MediatorFramework.Sample/src/Ark.MediatorFramework.Sample.Application/ApplicationComposition.cs | Registers the retry decorator for request handlers. |
| docs/mediator-framework/progress/tasks/sample-parity/SMP-04-optimistic-concurrency.md | Marks ETag demo as explicitly deferred due to missing generator support. |
| docs/mediator-framework/progress/tasks/README.md | Marks SMP-04 as completed. |
Comments suppressed due to low confidence (1)
samples/Ark.MediatorFramework.Sample/src/Ark.MediatorFramework.Sample.Application/SampleDataContext.cs:88
SaveAsyncfalls back to the MERGE upsert path whengreeting.Versionis null, which means an existing greeting can be overwritten without any optimistic-concurrency check. This is inconsistent withInMemoryGreetingStore(which rejects updates without a version) and undermines the goal of version-aware persistence.
const string sql = """
MERGE [dbo].[Greeting] AS target
USING (SELECT @Id AS [Id]) AS source ON target.[Id] = source.[Id]
WHEN MATCHED THEN UPDATE SET [Message] = @Message, [Date] = @Date,
[DateTime] = @DateTime, [OffsetDateTime] = @OffsetDateTime, [Period] = @Period,
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+59
to
64
| if (_items.TryGetValue(greeting.Id, out var current) | ||
| && (greeting.Version is null || current.Version is null || !greeting.Version.SequenceEqual(current.Version))) | ||
| throw new OptimisticConcurrencyException("Greeting '{0}' was modified concurrently.", greeting.Id); | ||
|
|
||
| greeting.Version = BitConverter.GetBytes(Interlocked.Increment(ref _version)); | ||
| _items[greeting.Id] = greeting; |
Comment on lines
+24
to
+36
| for (var attempt = 0; attempt < 3; attempt++) | ||
| { | ||
| try | ||
| { | ||
| return await _inner.ExecuteAsync(request, ctk).ConfigureAwait(false); | ||
| } | ||
| catch (OptimisticConcurrencyException) when (attempt < 2) | ||
| { | ||
| } | ||
| } | ||
|
|
||
| throw new OptimisticConcurrencyException("Optimistic concurrency retries exhausted."); | ||
| } |
Comment on lines
+55
to
+61
| if (greeting.Version is not null) | ||
| { | ||
| const string updateSql = """ | ||
| UPDATE [dbo].[Greeting] | ||
| SET [Message] = @Message, [Date] = @Date, [DateTime] = @DateTime, | ||
| [OffsetDateTime] = @OffsetDateTime, [Period] = @Period, [AuditId] = @AuditId | ||
| WHERE [Id] = @Id AND [Version] = @Version; |
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.
The mediator sample lacked optimistic-concurrency handling for concurrent greeting updates. This adds version-aware persistence and retry behavior, while documenting ETag/If-Match as a follow-up requiring generator support.
Concurrency
ROWVERSIONtracking to greetings.OptimisticConcurrencyException.Retries
409 Conflictmapping.Coverage
ETag/If-Match response-header support remains deferred because generated response-header emission is not yet available.