Skip to content

.NET: Add Redis checkpoint store for workflows - #9253

Draft
Murat Ay (Murat7Ay) wants to merge 2 commits into
microsoft:mainfrom
Murat7Ay:dotnet-redis-checkpoint-store
Draft

Murat Ay (Murat7Ay) wants to merge 2 commits into
microsoft:mainfrom
Murat7Ay:dotnet-redis-checkpoint-store

Conversation

@Murat7Ay

@Murat7Ay Murat Ay (Murat7Ay) commented Oct 10, 2026 •

Copy link
Copy Markdown

Motivation & Context

Workflows that run across processes, pods or machines need a durable, shared checkpoint store. On .NET the built-in stores are in-memory and file system (process-exclusive), plus Cosmos DB. Redis is a common choice for this kind of short-lived, shared state (Azure Managed Redis, ElastiCache, self-hosted, Kubernetes), and #2401 asks for a Redis-backed checkpoint store. The earlier .NET attempt, #2799, was closed as outdated with its Copilot review comments still open; this PR is a fresh implementation on current main and addresses all of them (details below).

Description & Review Guide

  • What are the major changes?

    • New package Microsoft.Agents.AI.Redis (VersionSuffix=alpha, IsPackable=false until maintainers want to publish it, as was done for Valkey) with RedisCheckpointStore : JsonCheckpointStore and RedisCheckpointStoreOptions (KeyPrefix, Database, TimeToLive). It is built on StackExchange.Redis, which Add Redis-backed checkpoint storage for distributed workflow persistence #2401 names. Types live in Microsoft.Agents.AI.Workflows.Checkpointing, like CosmosCheckpointStore.
    • Storage layout per session: a sorted set of checkpoint ids scored by a per-session commit counter (INCR), a hash with the checkpoint JSON (stored verbatim, so $type discriminators stay first) and a hash with parent ids. One Lua script writes all of it atomically.
    • RetrieveIndexAsync returns checkpoints in commit order, as ICheckpointStore requires and CheckpointManager.GetLatestCheckpointAsync relies on. It stays correct for many checkpoints in the same second and for several processes writing to the same session.
    • All keys of a session share one hash tag built from the session id ({, } and % are escaped, and KeyPrefix may not contain braces), so the store works on Redis Cluster. The optional TimeToLive is reset on every new checkpoint and covers all keys of the session, so a session expires as a whole.
    • The store takes an IConnectionMultiplexer the application owns. It holds no other resources, so it is not IDisposable and is safe to share between threads.
    • A missing checkpoint throws KeyNotFoundException, like FileSystemJsonCheckpointStore.
    • Tests in Microsoft.Agents.AI.Redis.UnitTests: 40 unit tests against a mocked IDatabase (always run), and 12 tests against a real Redis server that follow the Cosmos emulator pattern: REDIS_CONNECTION_STRING (default localhost:6379), skipped when no server is reachable, failing instead when REDIS_AVAILABLE=true. The server tests include a real workflow that is resumed from Redis with a new connection, store and workflow instance.
    • New sample samples/03-workflows/Checkpoint/CheckpointWithRedis: run, stop part way, then resume from the latest checkpoint found in Redis. Added to the solution, the 03-workflows README and eng/verify-samples (skipped there because it needs a Redis server).
  • What is the impact of these changes? Additive only: a new package, test project and sample. No existing public API changes. New central package version: StackExchange.Redis 3.4.0.

  • What do you want reviewers to focus on?

    • Package location. I added a separate Microsoft.Agents.AI.Redis package instead of extending Microsoft.Agents.AI.Valkey, because: Add Redis-backed checkpoint storage for distributed workflow persistence #2401 asks for StackExchange.Redis; Python already ships agent-framework-redis, so the name matches across languages; and in the Valkey PR (.NET: Adds Valkey to chat message history - issue 5445 #5542) a reviewer asked whether that package should be called Redis, and the Valkey maintainers preferred to keep the Valkey package Valkey-specific (Valkey.Glide only guarantees Redis 6.2–7.2). The Valkey package also has no dependency on Workflows today. I'm happy to move the store into the Valkey package if you prefer.
    • Feature usage telemetry. I did not add a FeatureIndex bit, since docs/specs/feature-usage-bit-registry.md treats new bits as a maintainer decision. I can add a redis row (next free .NET index is 75) if you want one.
    • StackExchange.Redis version. 3.4.0 is the latest stable release and fits the current central pins. I can lower it to the 2.x line if you'd rather not push 3.x onto applications.
    • Scope: no DI helpers or connection-string constructors, to keep the first version small. Applications usually already register one IConnectionMultiplexer.
How the Copilot review comments on #2799 are addressed
#2799 comment Where Now
_disposed checked without synchronization (3 comments) RedisCheckpointStore.cs The store no longer owns the connection, has no _disposed flag and is not IDisposable. All fields are readonly and set in the constructor, so there is no race to guard.
Sample uses var instead of explicit types (11 comments) sample Program.cs The new sample uses explicit types everywhere.
Use TargetFramework (singular) for a single target sample .csproj Kept <TargetFrameworks>: samples/Directory.Build.props sets TargetFrameworks (net10.0;net472), and a singular TargetFramework would not override it. All samples in the repo use the plural form.
Test class should be sealed tests All test classes are sealed.
Use this._disposed in tests tests The tests no longer implement a dispose pattern; instance members use this. throughout.
Unused disposing parameter tests There is no Dispose(bool) anymore.

Other differences from #2799:

Local test run

Windows, .NET SDK 10.0.401, Redis 7 (redis:7-alpine in Docker), standalone and a single-node Redis Cluster:

  • dotnet build tests/Microsoft.Agents.AI.Redis.UnitTests: 0 warnings, 0 errors.
  • dotnet test --project tests/Microsoft.Agents.AI.Redis.UnitTests -f <tfm> with REDIS_CONNECTION_STRING and REDIS_AVAILABLE=true: net10.0, net9.0 and net8.0 each total: 52, failed: 0, succeeded: 52, skipped: 0, against both standalone Redis and Redis Cluster.
  • Without a Redis server (net10.0): total: 52, succeeded: 40, skipped: 12 ("Redis is not available").
  • dotnet format <csproj> --verify-no-changes on the new package, test project, sample and eng/verify-samples: no changes.
  • The sample output is identical on every run and ends with Workflow completed with result: 42 found in 7 tries!.

Related Issue

Fixes #2401

Supersedes #2799 (closed as outdated). There is no other open PR for this issue. #2401 also covers Python; this PR is .NET only.

AI Assistance

  • No material AI assistance was used.
  • This is an AI-assisted contribution. I reviewed, understood, and verified all submitted content and accept responsibility for it.

AI assistance details: implementation, tests, sample and PR description were drafted with an AI coding assistant (Claude), starting from a Redis checkpoint store I already maintain in my own project. I reviewed the design and code and ran the builds and tests listed above against a real Redis server.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR links to an agreed issue with no competing open PR, or the Related Issue section documents a trivial-change or repository-automation exception.
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.

Add Microsoft.Agents.AI.Redis (alpha, not packable yet) with
RedisCheckpointStore, a JsonCheckpointStore on StackExchange.Redis.

Per session it keeps a sorted set of checkpoint ids scored by a commit
counter, a hash with the checkpoint JSON and a hash with parent ids,
written atomically by a Lua script. The index is returned in commit
order (as ICheckpointStore requires), all keys of a session share a
Redis Cluster hash tag, and an optional time to live expires a session
as a whole.

Unit tests run against a mocked IDatabase; server tests run against a
real Redis and are skipped when none is reachable. Adds the
CheckpointWithRedis workflow sample.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 10, 2026 19:13
@agent-framework-automation agent-framework-automation Bot added documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs .NET Usage: [Issues, PRs], Target: .Net workflows Usage: [Issues, PRs], Target: Workflows labels Oct 10, 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.

🟡 Changes recommended

Redis hash-tag edge cases break cluster operation, and the sample’s checkpoint stopping point is timing-dependent.

2 open findings
What changed in this PR

Adds a Redis-backed durable checkpoint store for distributed .NET workflows.

Changes:

  • Implements atomic, ordered Redis checkpoint persistence with optional TTL.
  • Adds mocked and live-server tests.
  • Adds a checkpoint-resumption sample and project registrations.
File Description
dotnet/​src/​Microsoft.Agents.AI.Redis/​RedisCheckpointStore.cs Implements Redis checkpoint storage.
dotnet/​src/​Microsoft.Agents.AI.Redis/​RedisCheckpointStoreOptions.cs Defines Redis configuration options.
dotnet/​src/​Microsoft.Agents.AI.Redis/​Microsoft.Agents.AI.Redis.csproj Defines the new integration project.
dotnet/​tests/​Microsoft.Agents.AI.Redis.UnitTests/​RedisFixture.cs Provides live Redis test setup.
dotnet/​tests/​Microsoft.Agents.AI.Redis.UnitTests/​RedisCheckpointStoreTests.cs Tests store behavior with mocks.
dotnet/​tests/​Microsoft.Agents.AI.Redis.UnitTests/​RedisCheckpointStoreServerTests.cs Tests against a Redis server.
dotnet/​tests/​Microsoft.Agents.AI.Redis.UnitTests/​GuessNumberWorkflow.cs Supplies a resumable test workflow.
dotnet/​tests/​Microsoft.Agents.AI.Redis.UnitTests/​Microsoft.Agents.AI.Redis.UnitTests.csproj Defines the test project.
dotnet/​samples/​03-workflows/​Checkpoint/​CheckpointWithRedis/​Program.cs Demonstrates checkpoint resumption.
dotnet/​samples/​03-workflows/​Checkpoint/​CheckpointWithRedis/​WorkflowFactory.cs Defines the sample workflow.
dotnet/​samples/​03-workflows/​Checkpoint/​CheckpointWithRedis/​README.md Documents sample usage.
dotnet/​samples/​03-workflows/​Checkpoint/​CheckpointWithRedis/​CheckpointWithRedis.csproj Defines the sample project.
dotnet/​samples/​03-workflows/​README.md Lists the Redis sample.
dotnet/​eng/​verify-samples/​WorkflowSamples.cs Registers sample verification.
dotnet/​Directory.Packages.props Pins StackExchange.Redis.
dotnet/​agent-framework-src-and-tests.slnf Registers source and test projects.
dotnet/​agent-framework-dotnet.slnx Registers all new projects.

🧠 Review effort: Balanced


💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread dotnet/src/Microsoft.Agents.AI.Redis/RedisCheckpointStore.cs Outdated
Comment thread dotnet/samples/03-workflows/Checkpoint/CheckpointWithRedis/Program.cs Outdated
@Murat7Ay

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

…erministic

Escape '%', '{' and '}' in the session id inside the hash tag and reject
braces in KeyPrefix, so all keys of a session always share one valid
Redis Cluster hash tag. A session id such as "}abc" produced an empty
tag before, which spread the keys over different slots and made the
create script fail with CROSSSLOT.

Add unit tests that compute the cluster slot (CRC16) of the keys for
awkward session ids and prefixes, and server tests with such session
ids (run against standalone Redis and a single-node Redis Cluster).

Run the first part of the CheckpointWithRedis sample in lockstep mode,
so it stops exactly after the fourth checkpoint and resumes from it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
github-app-auth — add7f3a5 Deployed Oct 10, 2026 by Murat7Ay via add_label #24922
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs .NET Usage: [Issues, PRs], Target: .Net workflows Usage: [Issues, PRs], Target: Workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Redis-backed checkpoint storage for distributed workflow persistence

2 participants