Yautbox 1.4.0
dotnet add package Yautbox --version 1.4.0
NuGet\Install-Package Yautbox -Version 1.4.0
<PackageReference Include="Yautbox" Version="1.4.0" />
<PackageVersion Include="Yautbox" Version="1.4.0" />
<PackageReference Include="Yautbox" />
paket add Yautbox --version 1.4.0
#r "nuget: Yautbox, 1.4.0"
#:package Yautbox@1.4.0
#addin nuget:?package=Yautbox&version=1.4.0
#tool nuget:?package=Yautbox&version=1.4.0
Yautbox
Yautbox is a lightweight .NET outbox library. It lets you enqueue messages during application work and process them later with background handlers. The core package is storage-agnostic; choose the in-memory or PostgreSQL provider, or implement your own.
Features
- Outbox pattern with background processing
- Typed handlers with retry support
- Scheduled messages and visibility timeouts
- Configurable concurrency, polling, and cleanup
- Pluggable storage via
IOutboxProvider
Delivery model
- Delivery is at least once: a message can be delivered more than once when a handler succeeds but persisting its result fails.
- Processing order is not guaranteed, especially with multiple workers or parallel batches.
- Handlers must be idempotent.
Installation
NuGet:
dotnet add package Yautbox
From source:
dotnet add <your-app>.csproj reference src/Yautbox/Yautbox.csproj
Quick start
- Register the outbox and a provider.
using Microsoft.Extensions.DependencyInjection;
using Yautbox.Extensions.Ioc;
using Yautbox.InMemory.Extensions; // from Yautbox.InMemory package
services.AddOutbox(builder => builder.UseInMemory());
- Register a handler for a payload type.
using Yautbox.Extensions.Ioc;
using Yautbox.Handlers;
services.AddOutboxHandler<OrderPlaced, OrderPlacedHandler>();
public sealed class OrderPlacedHandler : IOutboxHandler<OrderPlaced>
{
public Task HandleAsync(IEnumerable<OutboxMessage<OrderPlaced>> messages, CancellationToken ct)
{
foreach (var message in messages)
{
// process message.Payload
// message.Retry(TimeSpan.FromMinutes(1)); // optional retry
}
return Task.CompletedTask;
}
}
- Enqueue messages from your app code.
using Yautbox.Services;
await outbox.HandleAsync(new[] { new OrderPlaced("A-123") });
await outbox.HandleAsync(
new[] { new OrderPlaced("B-456") },
scheduledAt: DateTimeOffset.UtcNow.AddMinutes(5));
Cancel by id if needed:
using Yautbox.Extensions.Outbox;
await outbox.CancelAsync<OrderPlaced>(messageId);
For a heterogeneous batch, use IPolymorphicOutboxService. Each payload is stored under its concrete runtime type
and routed to that type's handler, while returned identifiers preserve input order.
Configuration options
Each handler can be configured via AddOutboxHandler().ConfigureOptions<TOptions>(). Implement IOutboxRunnerOptions for full control or ISimpleRunnerOptions when only BufferSize must be supplied.
using Microsoft.Extensions.Options;
using Yautbox.Extensions.Ioc;
using Yautbox.Runner.Options;
services
.AddOutboxHandler<OrderPlaced, OrderPlacedHandler>()
.ConfigureOptions<DefaultRunnerOptions>(options =>
options.Configure(o =>
{
o.BufferSize = 500;
o.WorkersCount = 2;
o.ExecutionPolicy = ExecutionPolicy.Parallel;
o.BackupInterval = TimeSpan.FromHours(24);
}));
IOutboxRunnerOptions settings and defaults:
Identifier(default: payload type assembly-qualified name without version, culture, or public key token)PollDelay(default: 5s + jitter)BufferSize(default: 1000)PerBufferCount(default: BufferSize)HandleTimeout(default: 55m)IsEnabled(default: true)WorkersCount(default: 1)DeletePolicy(default: Safe)FailureDelay(default: 2s + jitter)Visibility(default: 1h)BackupInterval(default: null, disabled)CleanupInterval(default: 1d)ExecutionPolicy(default: Parallel)CancellationPolicy(default: Safe)PolicyTimeout(default: 55m)ScopeLifetime(default: PerBatch)
How it works
IOutboxServiceenqueues messages into anIOutboxProvider.AddOutboxHandler<TPayload, THandler>()registers two hosted services:- a handler runner that polls, locks, and dispatches messages
- a cleanup runner that deletes old handled records when
BackupIntervalis set
ExecutionPolicy.Sequentialuses a provider-level policy scope to ensure single active processing for the same identifier when the provider supports distributed locking.
Extensibility
To create a custom storage implementation, implement IOutboxProvider and register it via AddOutbox(builder => builder.SetProvider<...>()).
Metrics
Yautbox reports lifecycle metrics through IMetricsHandler. The default handler is a no-op. Register a custom handler via the infrastructure builder:
using Yautbox.Extensions.Ioc;
using Yautbox.Metrics;
services.AddOutbox(builder =>
{
builder.UseInMemory();
builder.SetMetrics<MyMetricsHandler>();
});
public sealed class MyMetricsHandler : IMetricsHandler
{
public ValueTask AddedAsync(string identifier, int count, CancellationToken ct) => ValueTask.CompletedTask;
public ValueTask CanceledAsync(string identifier, int count, CancellationToken ct) => ValueTask.CompletedTask;
public ValueTask HandledAsync(string identifier, int count, TimeSpan elapsed, CancellationToken ct) => ValueTask.CompletedTask;
public ValueTask RetriedAsync(string identifier, int count, CancellationToken ct) => ValueTask.CompletedTask;
public ValueTask DeletedAsync(string identifier, int count, CancellationToken ct) => ValueTask.CompletedTask;
public ValueTask CleanedInAsync(string identifier, TimeSpan elapsed, CancellationToken ct) => ValueTask.CompletedTask;
public ValueTask ReadInAsync(string identifier, TimeSpan elapsed, CancellationToken ct) => ValueTask.CompletedTask;
public ValueTask ErrorsAsync(string identifier, int count, CancellationToken ct) => ValueTask.CompletedTask;
}
Tracing
Yautbox creates tracing scopes for enqueue, fetch, handle, persist, and cleanup through IOutboxTracer. The default
tracer is a no-op. Register an Activity/OpenTelemetry adapter with builder.SetTracing<MyOutboxTracer>().
Target frameworks
net8.0
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Options (>= 10.0.8)
- Polly (>= 8.6.6)
- Scrutor (>= 7.0.0)
- StronglyTypedId (>= 0.2.1)
- System.Linq.Async (>= 7.0.1)
NuGet packages (4)
Showing the top 4 NuGet packages that depend on Yautbox:
| Package | Downloads |
|---|---|
|
Yautbox.Postgres
PostgreSQL provider for Yautbox with schema migrations and advisory locks for sequential execution. |
|
|
Yautbox.InMemory
In-memory Yautbox provider using a bounded in-process queue. Ideal for tests and local development where durability is not required. |
|
|
Yautbox.Mssql
SQL Server provider for Yautbox with schema migrations and distributed locks for sequential execution. |
|
|
Yautbox.Mysql
MySQL provider for Yautbox with schema migrations and distributed locks for sequential execution. |
GitHub repositories
This package is not used by any popular GitHub repositories.