Skip to content

Add scheduling support to the Mocha in-memory transport#9987

Draft
glen-84 wants to merge 9 commits into
mainfrom
gai/inmemory-transport-scheduling
Draft

Add scheduling support to the Mocha in-memory transport#9987
glen-84 wants to merge 9 commits into
mainfrom
gai/inmemory-transport-scheduling

Conversation

@glen-84

@glen-84 glen-84 commented Jun 25, 2026

Copy link
Copy Markdown
Member

Summary

  • Mocha's in-memory transport now honors scheduled delivery: a message dispatched with a future ScheduledTime (via ScheduleSendAsync/SchedulePublishAsync, a saga's Timeout(), or delayed redelivery) is delivered at its scheduled time instead of immediately.
  • Adds an in-memory IScheduledMessageStore and a background IHostedService worker, wired into AddInMemory through Mocha's generic scheduling middleware (UseSchedulerCore). Cancellation is supported: a pending timeout is cancelled when a saga reaches a final state. Scheduled messages are held in process (non-durable); for durability use UsePostgresScheduling().
  • Updates the saga integration test harness to start the hosted scheduler worker so Timeout() sagas are driven by real scheduling.
  • Updates the Mocha v16 docs (sagas, scheduling, in-memory transport) to reflect that in-memory scheduling is on by default, supports cancellation, and is non-durable.

Test plan

  • Store unit tests and end-to-end tests (via FakeTimeProvider): a scheduled message is delivered after its due time and not before, cancellation prevents delivery, and a scheduled message's body and headers are preserved until delivery.
  • Full Mocha in-memory transport suite green (206 tests).
  • Mocha saga suite green across net8.0–net11.0.

Copilot AI review requested due to automatic review settings June 25, 2026 16:06
@github-actions github-actions Bot added 📚 documentation This issue is about working on our documentation. 🌶️ mocha labels Jun 25, 2026

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 first-class scheduled delivery semantics to Mocha’s in-memory transport by wiring it into the shared scheduling middleware, backed by an in-process scheduled message store + hosted background worker, and updates docs/tests accordingly.

Changes:

  • Register an in-memory IScheduledMessageStore + IHostedService dispatcher in AddInMemory() and enable scheduler core by default.
  • Add unit/integration coverage for due-time delivery, cancellation, and payload/header preservation under pooled context reuse.
  • Update Mocha v16 docs (scheduling, sagas, in-memory transport) to reflect in-memory scheduling + cancellation support and non-durability.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
website/src/docs/mocha/v16/transports/in-memory.md Documents in-memory scheduled delivery behavior, non-durability, and cancellation.
website/src/docs/mocha/v16/scheduling.md Updates cancellation semantics and transport capability matrix for InMemory.
website/src/docs/mocha/v16/sagas.md Updates saga Timeout() prerequisites/behavior notes for in-memory scheduling + cancellation.
src/Mocha/test/Mocha.Transport.InMemory.Tests/Scheduling/InMemorySchedulingTests.cs New end-to-end tests for scheduled delivery timing, cancellation, and payload preservation.
src/Mocha/test/Mocha.Transport.InMemory.Tests/Scheduling/InMemoryScheduledMessageStoreTests.cs New unit tests for store token format, ordering, cancellation, and deep-copy behavior.
src/Mocha/test/Mocha.Transport.InMemory.Tests/Mocha.Transport.InMemory.Tests.csproj Adds Microsoft.Extensions.TimeProvider.Testing dependency for FakeTimeProvider.
src/Mocha/test/Mocha.Transport.InMemory.Tests/Helpers/MessageBusBuilder.cs Starts hosted services in the in-memory transport test harness so scheduling worker runs.
src/Mocha/test/Mocha.Sagas.Tests/IntegrationTests.cs Starts hosted services and adjusts timeout test flow to better synchronize with persistence/finalization.
src/Mocha/src/Mocha.Transport.InMemory/Scheduling/InMemoryScheduledMessageWorker.cs New hosted worker that wakes on scheduler signal and dispatches due scheduled envelopes.
src/Mocha/src/Mocha.Transport.InMemory/Scheduling/InMemoryScheduledMessageStore.cs New in-memory scheduled message store that deep-copies envelopes and supports cancellation.
src/Mocha/src/Mocha.Transport.InMemory/MessageBusBuilderExtensions.cs Wires scheduling store/worker into AddInMemory() and enables scheduler core.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +64 to +73
while (!cancellationToken.IsCancellationRequested)
{
if (store.TryTakeDue(timeProvider.GetUtcNow(), out var envelope))
{
await DispatchAsync(envelope, cancellationToken);
continue;
}

await signal.WaitUntilAsync(store.NextDueTime() ?? DateTimeOffset.MaxValue, cancellationToken);
}
Comment on lines +63 to +90
public bool TryTakeDue(DateTimeOffset now, [NotNullWhen(true)] out MessageEnvelope? envelope)
{
lock (_lock)
{
var foundId = Guid.Empty;
var foundTime = DateTimeOffset.MaxValue;
MessageEnvelope? foundEnvelope = null;

foreach (var (id, entry) in _entries)
{
if (entry.ScheduledTime <= now && entry.ScheduledTime < foundTime)
{
foundId = id;
foundTime = entry.ScheduledTime;
foundEnvelope = entry.Envelope;
}
}

if (foundEnvelope is null)
{
envelope = null;
return false;
}

_entries.Remove(foundId);
envelope = foundEnvelope;
return true;
}
Comment on lines +27 to +42
busBuilder.Services.TryAddSingleton<InMemoryScheduledMessageStore>();
busBuilder.Services.TryAddSingleton<IScheduledMessageStore>(
sp => sp.GetRequiredService<InMemoryScheduledMessageStore>());
busBuilder.Services.TryAddSingleton(sp => new InMemoryScheduledMessageWorker(
sp,
sp.GetRequiredService<IMessagingRuntime>(),
sp.GetRequiredService<IMessagingPools>(),
sp.GetRequiredService<ISchedulerSignal>(),
sp.GetRequiredService<InMemoryScheduledMessageStore>(),
sp.GetService<TimeProvider>() ?? TimeProvider.System,
sp.GetRequiredService<ILogger<InMemoryScheduledMessageWorker>>()));
busBuilder.Services.AddHostedService(
sp => sp.GetRequiredService<InMemoryScheduledMessageWorker>());

busBuilder.UseSchedulerCore();

@glen-84 glen-84 marked this pull request as draft June 25, 2026 16:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📚 documentation This issue is about working on our documentation. 🌶️ mocha

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants