Skip to content

feat(mediator): add optimistic concurrency to sample#797

Closed
AndreaCuneo with Copilot wants to merge 5 commits into
masterfrom
copilot/docsmediator-frameworkprogress
Closed

feat(mediator): add optimistic concurrency to sample#797
AndreaCuneo with Copilot wants to merge 5 commits into
masterfrom
copilot/docsmediator-frameworkprogress

Conversation

Copilot AI commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

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

    • Add SQL ROWVERSION tracking to greetings.
    • Detect stale writes and raise OptimisticConcurrencyException.
    • Mirror version checks in the in-memory store.
  • Retries

    • Register a request-handler decorator that retries optimistic conflicts up to two times.
    • Propagate exhausted conflicts for standard 409 Conflict mapping.
  • Coverage

    • Add retry, exhausted-conflict, and stale-version tests.
    • Mark SMP-04 complete in the mediator task board.
container.RegisterDecorator(
    typeof(IRequestHandler<,>),
    typeof(OptimisticConcurrencyRetrierDecorator<,>));

ETag/If-Match response-header support remains deferred because generated response-header emission is not yet available.

Copilot AI and others added 4 commits July 26, 2026 10:27
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 AI review requested due to automatic review settings July 26, 2026 11:17
Copilot AI removed the request for review from Copilot July 26, 2026 11:17
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>
Copilot AI review requested due to automatic review settings July 26, 2026 11:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 Version concurrency token to GreetingResponse and persist it via SQL ROWVERSION + in-memory versioning.
  • Add OptimisticConcurrencyRetrierDecorator<,> and register it for all IRequestHandler<,> 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

  • SaveAsync falls back to the MERGE upsert path when greeting.Version is null, which means an existing greeting can be overwritten without any optimistic-concurrency check. This is inconsistent with InMemoryGreetingStore (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;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants