Reimaginate.ProcessingLockService.Cosmos 4.0.0

dotnet add package Reimaginate.ProcessingLockService.Cosmos --version 4.0.0
                    
NuGet\Install-Package Reimaginate.ProcessingLockService.Cosmos -Version 4.0.0
                    
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="Reimaginate.ProcessingLockService.Cosmos" Version="4.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Reimaginate.ProcessingLockService.Cosmos" Version="4.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Reimaginate.ProcessingLockService.Cosmos" />
                    
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 Reimaginate.ProcessingLockService.Cosmos --version 4.0.0
                    
#r "nuget: Reimaginate.ProcessingLockService.Cosmos, 4.0.0"
                    
#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 Reimaginate.ProcessingLockService.Cosmos@4.0.0
                    
#: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=Reimaginate.ProcessingLockService.Cosmos&version=4.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Reimaginate.ProcessingLockService.Cosmos&version=4.0.0
                    
Install as a Cake Tool

Reimaginate.ProcessingLockService.Cosmos

Optional Azure Cosmos DB for NoSQL provider for the processing lock service (.NET 10). This provider now targets the 4.0.0 package family and does not bring in Redis dependencies. See the 4.0 migration guide when upgrading existing applications. It implements ITenantAwareProcessingLockRepository and preserves the service's tenant, ownership-token, same-owner reacquisition, renewal and partial-batch semantics.

Configure an existing container

Use a dedicated container with one partition key path, /tenantId, partition key version 2, and container TTL enabled with no default expiry (DefaultTimeToLive = -1). Use a single write region. The runtime does not provision infrastructure or own/dispose the supplied CosmosClient. Reuse a long-lived client. Resource keys are hashed to safe document IDs; the original key is retained in the document. Tenant IDs support up to 2048 UTF-8 bytes.

using Microsoft.Azure.Cosmos;
using Microsoft.Extensions.DependencyInjection;
using Reimaginate.ProcessingLockService;
using Reimaginate.ProcessingLockService.Cosmos;

// Reuse the application's existing client (including its authentication configuration).
Container container = cosmosClient.GetContainer("application", "processing-locks");
using var startupTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(30));
ConsistencyLevel accountConsistency =
    await container.ValidateCosmosLockContainerAsync(startupTimeout.Token);

services.AddProcessingLockService(options => options.WithCosmosRepository(container, cosmos =>
{
    cosmos.OperationTimeout = TimeSpan.FromSeconds(5);
    cosmos.MaxConcurrencyRetries = 8;
}));

ValidateCosmosLockContainerAsync checks partition configuration, TTL and write-region count without changing them. Call it during startup; constructors perform no network I/O. It returns account consistency for the host to check against its failover requirements. The client/database/container overload is also available: options.WithCosmosRepository(cosmosClient, "application", "processing-locks"). The store uses stream APIs with its own JSON schema, independently of client serializer settings.

Provision the container separately, for example during environment setup:

await cosmosClient.GetDatabase("application").CreateContainerAsync(
    new ContainerProperties("processing-locks", "/tenantId")
    {
        PartitionKeyDefinitionVersion = PartitionKeyDefinitionVersion.V2,
        DefaultTimeToLive = -1
    });

The application identity needs item read/query/create/replace/delete and metadata read permissions. Startup validation also reads account metadata. Provisioning permissions are only needed by the environment setup identity.

Correctness and limits

  • New leases use create-if-absent. Expired-document takeovers, renewals and releases use the ETag read from Cosmos. Release and renewal also require the current ownership token. Key-only release is rejected with BadRequest, including the obsolete repository overload.
  • Null and empty-string owners are anonymous. An active anonymous lock cannot be reacquired by owner value, but its token permits explicit renewal and release, matching Redis.
  • If any renewal/release batch item has a null or empty token, the entire batch is rejected before storage changes: BatchFailure overall and InUse for every item. Nonempty but incorrect tokens retain per-item failure behaviour, allowing other valid items to proceed.
  • requireAll: true uses one transaction in the tenant partition, up to 100 distinct keys. Every key participates, including same-owner leases returned without renewal. Requests above the limit are rejected before writes, never split into independent transactions.
  • Serialized documents plus a conservative per-operation framing allowance must fit a 1,900,000-byte budget below Cosmos's 2 MB batch limit. Oversized requests return BadRequest.
  • requireAll: false acquires keys independently, skips held keys and allows more than 100 keys. Storage errors can leave earlier successful acquisitions in place; returned results identify them when available. A timeout/cancellation can leave an unknown write outcome.
  • Explicit UTC expiry controls lease validity, including subsecond durations. Cosmos TTL only cleans up later (duration rounded up to seconds, plus 60 seconds). Reads exclude logically expired leases even while their documents remain stored.
  • UTC comes from TimeProvider.System by default. All participants must have synchronized clocks. A custom TimeProvider is available for testing. This is not a server-clock lease or a fencing mechanism for writes to external resources. Choose lease durations with enough margin for network latency, clock uncertainty and pauses, and renew before expiry.
  • After ambiguous acquisition/renewal responses (HTTP 408/500/503/504 or transport loss), the provider verifies every written document's operation ID, token and expiry. It returns success only if all can be confirmed and remain active; it does not blindly reacquire. An exhausted/cancelled verification returns failure. Unconfirmed leases expire normally. An ambiguous delete returns failure even if it actually committed.
  • An operation timeout (default 5 seconds) covers the entire repository call, SDK throttling retries and verification. ETag races have a bounded retry count and short backoff. Cancellation/timeouts/throttling map to TimeOut; authorization/availability failures map to UnexpectedFailure, rather than InUse. Cosmos 400/413 map to BadRequest.
  • Multi-region writes and conflict resolution are unsupported. Single-writer ETag checks protect normal operation with weaker read consistency, but stale reads can cause retries or conservative failures. Regional failover with possible loss of acknowledged writes is outside the lease guarantee. Use a strong-consistency account if preserving acknowledged locks across regional failover is required, and validate the actual account/client setup. This provider does not orchestrate failover. Recheck configuration after account changes.
  • Each tenant shares a logical partition. Benchmark busy tenants, polling frequency, request-unit consumption and achievable lease durations before production use.

Tests

The default suite runs the shared provider contract against the real Cosmos repository with an atomic test store, plus deterministic races, timeout/lost-response tests and SDK stream/batch/query adapter tests. These tests do not establish live-service behaviour.

With Docker Desktop, PowerShell 7 and the .NET 10 SDK available, run the repository's local emulator command from the repository root:

./tools/test-cosmos-emulator.ps1
./tools/test-cosmos-emulator.ps1 -AllTests

The first command executes the live Cosmos integration tests and fails if any are skipped. -AllTests runs the full regression suite with Cosmos enabled. Results are written under artifacts/cosmos-emulator/. See the local emulator guide.

The runner uses compose.cosmos.yml with a pinned official vNext emulator image, HTTPS, localhost-only ports, and a dedicated data volume. It exports the container's public root certificate and trusts it only in the test client's certificate chain policy. It does not modify Windows certificate stores or disable certificate/hostname validation. The test database is created only when emulator mode is explicitly enabled for an HTTPS loopback endpoint. The container and database remain available after the run.

For an existing Azure test database or separately managed emulator, configure these variables:

  • PROCESSINGLOCK_COSMOS_CONNECTION_STRING: the emulator connection string.
  • PROCESSINGLOCK_COSMOS_DATABASE_ID: the existing test database ID.

The fixture uses gateway mode and normal certificate verification unless the explicit local emulator root is supplied. It creates a unique processing-lock-tests-<guid> container with /tenantId and TTL enabled, then deletes only that container at teardown. The existing-database mode does not create/delete the database. Configured connection failures fail tests; tests skip only when neither environment configuration nor local emulator settings are present. You can also use a dedicated Azure test database; operations consume request units and container creation may allocate throughput according to that database's configuration.

The runner temporarily sets PROCESSINGLOCK_COSMOS_EMULATOR=true to create the local test database if absent and PROCESSINGLOCK_COSMOS_EMULATOR_CERTIFICATE to the exported public root certificate. Both features reject non-loopback or non-HTTPS endpoints. All four environment variables are restored when the script exits.

The runner also saves Git-ignored cosmos-emulator.local.json settings in the test project for Visual Studio Test Explorer and ordinary dotnet test runs. Build the test project after setup; the fixture reads the copied local settings when no explicit environment configuration is present. Keep the emulator running. See the emulator guide for precedence and opt-out details.

The vNext emulator does not enforce authentication. These runs validate provider behaviour, not Azure authentication/authorization or multi-region failover. Use an Azure test account for those environment-specific checks.

dotnet test test/Reimaginate.ProcessingLockService.UnitTests/Reimaginate.ProcessingLockService.UnitTests.csproj --filter FullyQualifiedName~Cosmos

The live suite includes the shared contract and concurrent requests through independent repositories, bypassing the service's process-local semaphore. Very short contract leases (100 ms) require a low-latency local environment; lease expiry before confirmation is a correct failure rather than a successful acquisition.

Package and release

Build/pack locally with:

dotnet build src/Reimaginate.ProcessingLockService.Cosmos/Reimaginate.ProcessingLockService.Cosmos.csproj -c Release

This produces a separate Reimaginate.ProcessingLockService.Cosmos package. The public release manifest includes it alongside Abstractions, Core and Redis at version 4.0.0. The existing GitHub Actions NuGet Release workflow packs/uploads all four on manual runs and publishes them on release-* tag pushes through the nuget-publish environment, after confirming the tagged commit is reachable from main. Adding the manifest entry does not publish anything; a release still requires the explicit tag-driven release process.

References: ETag concurrency, transactional batches and limits, TTL, consistency.

Product Compatible and additional computed target framework versions.
.NET 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
4.0.0 95 10/27/2026