NetEvolve.Pulse.CosmosDb 0.84.106

Prefix Reserved
dotnet add package NetEvolve.Pulse.CosmosDb --version 0.84.106
                    
NuGet\Install-Package NetEvolve.Pulse.CosmosDb -Version 0.84.106
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="NetEvolve.Pulse.CosmosDb" Version="0.84.106" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NetEvolve.Pulse.CosmosDb" Version="0.84.106" />
                    
Directory.Packages.props
<PackageReference Include="NetEvolve.Pulse.CosmosDb" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add NetEvolve.Pulse.CosmosDb --version 0.84.106
                    
#r "nuget: NetEvolve.Pulse.CosmosDb, 0.84.106"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package NetEvolve.Pulse.CosmosDb@0.84.106
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=NetEvolve.Pulse.CosmosDb&version=0.84.106
                    
Install as a Cake Addin
#tool nuget:?package=NetEvolve.Pulse.CosmosDb&version=0.84.106
                    
Install as a Cake Tool

NetEvolve.Pulse.CosmosDb

NuGet Version NuGet Downloads License

Azure Cosmos DB persistence provider for the Pulse outbox pattern using the official Microsoft.Azure.Cosmos SDK. Designed for document-oriented architectures on Azure Cosmos DB that need the outbox pattern without a relational database.

Features

  • Native Cosmos DB implementation of IOutboxRepository using Microsoft.Azure.Cosmos
  • Optimistic concurrency via ETag-based conditional patch operations, so concurrent workers never claim the same message twice
  • Dead-letter inspection, replay, dismissal and statistics via IOutboxManagement
  • Optional TTL-based automatic cleanup of completed and dead-letter documents
  • Configurable database and container name (default container: outbox_messages)
  • Health check support via a container read
  • Requires CosmosClient to be registered in the dependency injection container by the caller

Installation

NuGet Package Manager

Install-Package NetEvolve.Pulse.CosmosDb

.NET CLI

dotnet add package NetEvolve.Pulse.CosmosDb

PackageReference

<PackageReference Include="NetEvolve.Pulse.CosmosDb" Version="x.x.x" />

Container Setup

The provider does not create the database or the container. Create both before the application starts processing the outbox.

The container must use the partition key path /id. The repository and the management API always use the document id (the outbox message ID) as partition key value for point reads, patches and deletes. On a container with any other partition key path these operations return 404 Not Found, which would stall the outbox silently. CosmosDbOutboxOptions.PartitionKeyPath therefore only accepts /id; any other value fails options validation at host startup with an OptionsValidationException.

using Microsoft.Azure.Cosmos;

var database = await cosmosClient.CreateDatabaseIfNotExistsAsync("MyDatabase");

_ = await database.Database.CreateContainerIfNotExistsAsync(
    new ContainerProperties(id: "outbox_messages", partitionKeyPath: "/id")
    {
        // Required when EnableTimeToLive is true: -1 turns TTL on without a default
        // expiry. The provider writes "ttl": -1 on pending, processing, failed and
        // replayed documents and "ttl": TtlSeconds on completed and dead-letter ones.
        // A positive default expires documents without a "ttl" property, for example
        // pending documents written by an older version, and loses those messages.
        DefaultTimeToLive = -1,
    });

Keep the default indexing policy (all paths indexed). The provider filters on status, retryCount, nextRetryAt and updatedAt and sorts by _ts and updatedAt.

With the /id partition key every document is its own logical partition. Point operations stay single-partition, but the recurring status queries (polling, retry polling, counts and cleanup) fan out to all physical partitions. Enable TTL cleanup to keep the container small and the fan-out inexpensive.

Quick Start

using Microsoft.Azure.Cosmos;
using NetEvolve.Pulse;

// Register CosmosClient in DI (required by the caller)
services.AddSingleton(new CosmosClient(connectionString));

services.AddPulse(config => config
    .AddCosmosDbOutbox(opts =>
    {
        opts.DatabaseName = "MyDatabase";
        opts.ContainerName = "outbox_messages";
    }));

Usage

Using AddCosmosDbOutbox (Full Setup)

Registers the core outbox services, the Cosmos DB repository and the management API in a single call. Calling AddOutbox() before is optional.

services.AddSingleton(new CosmosClient(connectionString));

services.AddPulse(config => config
    .AddCosmosDbOutbox(opts =>
    {
        opts.DatabaseName = "MyDatabase";
        opts.EnableTimeToLive = true;
        opts.TtlSeconds = 3600; // remove completed and dead-letter documents after one hour
    }));

Using UseCosmosDbOutbox (Provider Swap)

Call AddOutbox() first, then UseCosmosDbOutbox to replace any previously registered IOutboxRepository and IOutboxManagement with the Cosmos DB implementations.

services.AddSingleton(new CosmosClient(connectionString));

services.AddPulse(config => config
    .AddOutbox()
    .UseCosmosDbOutbox(opts =>
    {
        opts.DatabaseName = "MyDatabase";
        opts.ContainerName = "outbox_messages";
    }));

Registered Services

Service Implementation Lifetime
IEventOutbox OutboxEventStore Scoped (AddCosmosDbOutbox only)
IOutboxRepository CosmosDbOutboxRepository Scoped
IOutboxManagement CosmosDbOutboxManagement Scoped
TimeProvider TimeProvider.System Singleton (if not already registered)

Configuration

Property Type Default Description
DatabaseName string (required) The Cosmos DB database name. The database must exist.
ContainerName string outbox_messages The Cosmos DB container name. The container must exist.
PartitionKeyPath string /id Only /id is supported; other values fail validation at startup. The container must use /id (see Container Setup).
EnableTimeToLive bool false Sets the ttl property on documents that become Completed or DeadLetter, so the Cosmos DB TTL engine deletes them. All other documents (pending, processing, failed and replayed) get ttl = -1, so they never expire. Pending documents written by older versions have no ttl until they are claimed, so the container still requires DefaultTimeToLive = -1.
TtlSeconds int 86400 (24 hours) TTL in seconds for completed and dead-letter documents. Only applies when EnableTimeToLive is true.
ProcessingLeaseTimeout TimeSpan 5 minutes How long a claimed message may stay in Processing before the next pending poll reclaims it, for example after a crash or shutdown. Must be greater than zero; other values fail validation at startup.

Concurrency

Workers claim pending and retryable messages with a conditional patch (IfMatchEtag) that uses the _etag returned by the candidate query. When another worker changed the document in the meantime, Cosmos DB answers with 412 Precondition Failed and the message is skipped, so each message is claimed by one worker only. Replay and dismissal in IOutboxManagement use the same ETag precondition.

Cosmos DB has no transactions across containers or with other data stores, so the outbox write is not atomic with your domain writes. This provider does not support IOutboxTransactionScope.

Dead Letter Management

The IOutboxManagement service is registered automatically by AddCosmosDbOutbox(...) and UseCosmosDbOutbox(...).

using NetEvolve.Pulse.Extensibility.Outbox;

public class OutboxMonitorService
{
    private readonly IOutboxManagement _management;

    public OutboxMonitorService(IOutboxManagement management) => _management = management;

    public async Task PrintStatisticsAsync(CancellationToken ct)
    {
        var stats = await _management.GetStatisticsAsync(ct);
        Console.WriteLine($"Pending: {stats.Pending}, Dead Letter: {stats.DeadLetter}, Total: {stats.Total}");
    }

    public async Task ReplayAllDeadLettersAsync(CancellationToken ct)
    {
        var replayed = await _management.ReplayAllDeadLetterAsync(ct);
        Console.WriteLine($"Replayed {replayed} dead-letter messages.");
    }
}

Available Operations

Method Description
GetStatisticsAsync() Returns message counts grouped by status (OutboxStatistics)
GetDeadLetterMessagesAsync(pageSize, page) Returns a paginated list of dead-letter messages
GetDeadLetterMessageAsync(messageId) Returns a single dead-letter message by ID
GetDeadLetterCountAsync() Returns the total count of dead-letter messages
ReplayMessageAsync(messageId) Resets a single dead-letter message to Pending for reprocessing
ReplayAllDeadLetterAsync() Resets all dead-letter messages to Pending and returns the updated count
GetMessagesAsync(pageSize, page, status) Returns a paginated, read-only list of messages in any status, optionally filtered by status
GetMessageAsync(messageId) Returns a single message by ID, regardless of its status
DismissMessageAsync(messageId) Permanently deletes a single dead-letter message and returns whether one was deleted

Known Limitation: Paging Order

GetDeadLetterMessagesAsync and GetMessagesAsync sort by updatedAt (descending) only. A secondary sort by id would require a composite index that containers created for earlier versions do not have. Messages that share the same updatedAt value therefore have no guaranteed relative order and can shift between pages, so a message can appear on two pages or on none while you page through the results.

NativeAOT and Trimming

The package is built with IsAotCompatible and has no trim or AOT warnings of its own. The Microsoft.Azure.Cosmos SDK and its default Newtonsoft.Json-based serializer are outside the control of this package; publish your application with NativeAOT and check the warnings reported for the SDK before you rely on it in production.

See NativeAOT and Trimming in the NetEvolve.Pulse README for the payload serialization setup.

Requirements

  • .NET 8.0 or higher (net8.0, net9.0, net10.0 targets)
  • An Azure Cosmos DB for NoSQL account (or the Cosmos DB emulator)
  • CosmosClient registered in the dependency injection container
  • An existing database and a container with partition key path /id

Documentation

For complete documentation, please visit the official documentation.

Contributing

Contributions are welcome! Please read the Contributing Guidelines before submitting a pull request.

Support

License

This project is licensed under the MIT License - see the LICENSE file for details.


Made with ❤️ by the NetEvolve Team Visit us at https://www.daily-devops.net for more information about our services and solutions.

Product 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 is compatible.  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 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.84.106 130 11/8/2026
0.70.75 651 7/28/2026
0.70.16 118 7/27/2026
0.70.0 131 7/24/2026
0.67.140 142 7/23/2026
0.67.36 134 5/10/2026