duroxide-cdb 0.1.6

A CosmosDB-based provider implementation for Duroxide, a durable task orchestration framework
Documentation

duroxide-cdb

Crates.io License: MIT

A CosmosDB NoSQL provider for duroxide — a durable execution runtime for Rust.

Stores orchestration state, event history, and work queues in a single CosmosDB container using document-type discrimination and partition-per-instance layout.

Latest Release (0.1.6)

  • Updated to duroxide 0.1.25 with KV timestamp support in the provider contract
  • KeyValueDocument now persists last_updated_at_ms for backward-compatible KV timestamps
  • Added bulk KV reads via get_kv_all_values() and timestamped KV snapshots via KvEntry
  • KV entries are now instance-scoped during pruning and survive execution cleanup
  • See CHANGELOG.md for full version history

Features

  • Single-container design — all document types coexist, partitioned by instanceId
  • Transactional batch — atomic commits within a partition (up to 100 operations)
  • Transactional outbox — reliable cross-partition delivery for sub-orchestrations
  • Optimistic concurrency — ETag-based locking, no database-level locks
  • Dispatch slot partitioning — 256-slot keyspace eliminates dispatcher contention
  • Session affinity — routes work items to the same worker by session ID
  • KV store — durable per-instance key-value state for orchestration coordination
  • Timestamped KV snapshots — preserves per-key update times and supports bulk reads across all instance keys
  • Raw REST client — direct HTTP to CosmosDB, no Azure SDK dependency

Quick Start

1. Start the CosmosDB Emulator

docker run -p 8081:8081 -p 10250-10255:10250-10255 \
  mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest

2. Add the dependency

[dependencies]
duroxide = "0.1.25"
duroxide-cdb = "0.1.6"

3. Create a provider and run an orchestration

use duroxide::runtime::Runtime;
use duroxide::{ActivityContext, OrchestrationContext, OrchestrationRegistry};
use duroxide::runtime::registry::ActivityRegistry;
use duroxide_cdb::CosmosDBProvider;
use std::sync::Arc;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let provider = CosmosDBProvider::new(
        "http://localhost:8081",
        "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
        "duroxide",
    ).await?;

    let activities = ActivityRegistry::builder()
        .register("Greet", |_ctx: ActivityContext, name: String| async move {
            Ok(format!("Hello, {name}!"))
        })
        .build();

    let orchestrations = OrchestrationRegistry::builder()
        .register("HelloWorld", |ctx: OrchestrationContext, name: String| async move {
            let greeting = ctx.schedule_activity("Greet", name).await?;
            Ok(greeting)
        })
        .build();

    let rt = Runtime::start(Arc::new(provider), orchestrations, activities, Default::default());
    let client = rt.client();

    let result = client.schedule_and_wait("my-instance", "HelloWorld", "Rust", 30_000).await?;
    println!("Result: {result}");

    rt.shutdown().await;
    Ok(())
}

Configuration

Environment Variables

Variable Description Default
COSMOS_ENDPOINT CosmosDB account endpoint http://localhost:8081
COSMOS_KEY CosmosDB account key Emulator default key
COSMOS_DATABASE Database name duroxide

Copy .env.example to .env for local development.

Programmatic Configuration

use duroxide_cdb::{CosmosDBProvider, CosmosDBProviderConfig};
use std::time::Duration;

let config = CosmosDBProviderConfig {
    endpoint: "https://myaccount.documents.azure.com:443/".to_string(),
    key: "your-key-here".to_string(),
    database: "mydb".to_string(),
    container: "mycontainer".to_string(),
    orch_concurrency: 2,        // 2 orchestration dispatchers
    worker_concurrency: 4,      // 4 worker dispatchers
    reconciler_interval: Duration::from_secs(2),
    reconciler_age_threshold: Duration::from_secs(2),
};

let provider = CosmosDBProvider::new_with_config(config).await?;

Architecture

See docs/ARCHITECTURE.md for comprehensive design documentation covering:

  • Data model (7 document types, indexing policy, composite indexes)
  • Core algorithms (fetch/ack orchestration items, work items)
  • Transactional outbox pattern for cross-partition writes
  • Dispatch slot partitioning (256-slot keyspace)
  • Cross-partition query strategy (gateway limitations and workarounds)
  • Session affinity implementation
  • Error handling and retry strategy

Data Model

All documents live in a single container with /instanceId as the partition key:

Document Type Purpose
instance Orchestration metadata, lock state, custom status
history Append-only event log (one doc per event)
orch_queue Messages for the orchestration dispatcher
worker_queue Activity execution requests
outbox_intent Pending cross-partition writes
session Session affinity ownership tracking
kv Per-instance key-value state entries

Testing

Prerequisites

  • CosmosDB Emulator (Docker) or an Azure CosmosDB account
  • cargo-nextest for running tests

Run provider validation tests

cargo nextest run --features provider-test

Run e2e sample tests

cargo nextest run --test e2e_samples

Run against Azure CosmosDB

Set environment variables in .env:

COSMOS_ENDPOINT=https://your-account.documents.azure.com:443/
COSMOS_KEY=your-primary-key
COSMOS_DATABASE=duroxide

Module Structure

src/
├── lib.rs          Re-exports CosmosDBProvider, CosmosDBProviderConfig
├── provider.rs     Provider + ProviderAdmin trait implementations
├── client.rs       Raw REST client (HMAC-SHA256 auth, CRUD, query, batch)
├── models.rs       Serde structs for all document types
├── query.rs        Cross-partition and single-partition query builders
├── batch.rs        Transactional batch operations
├── containers.rs   Database/container bootstrapping with retry
├── outbox.rs       Outbox intent delivery + background reconciler
├── leases.rs       Dispatch slot assignment (LeaseProvider trait)
└── errors.rs       CosmosDB HTTP status → ProviderError mapping

Contributing

See CONTRIBUTING.md for contribution guidelines.

License

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

Copyright (c) Microsoft Corporation. All Rights Reserved.