Skip to content

Add Redis Cluster support - #75

Merged
thomhurst merged 30 commits into
mainfrom
agent/issue-60-cluster
Aug 9, 2026
Merged

thomhurst merged 30 commits into
mainfrom
agent/issue-60-cluster

Conversation

@thomhurst

@thomhurst thomhurst commented Aug 9, 2026 •

Copy link
Copy Markdown
Owner

Closes #60

Summary

  • add opt-in Redis Cluster mode with multiple seed fallback and database-0 validation
  • discover all 16,384 slots through CLUSTER SLOTS, including advertised hostnames
  • route keyed commands with CRC16/hash-tag support, then learn and retry MOVED and ASK redirects
  • make scripts, cross-node batches, same-slot transactions, blocking commands, and regular pub/sub cluster-aware
  • expose RespireKey.ClusterSlot, connection-string cluster=true, docs, benchmarks, and deterministic wire tests
  • reject cross-slot transactions, cluster WATCH, and cluster sharded pub/sub explicitly instead of routing them incorrectly

Verification

  • dotnet build Respire.sln --no-restore
  • dotnet test Respire.sln --no-build --no-restore — 592 passed across net8.0/net10.0
  • real Redis 7.4 three-master cluster smoke: cross-node batch, same-slot transaction, blocking pop, and EVALSHA→EVAL fallback all passed
  • git diff --check

Performance

  • single-node Respire_Ping: 390.9 µs / 184 B before; 381.9 µs / 184 B after (network noise, no regression)
  • cluster-slot calculation: 111.4 ns / 0 B initial; 15.58 ns / 0 B after table-based CRC16 optimization

Summary by CodeRabbit

New Features

  • Added Redis Cluster support with seed discovery, slot-based routing, failover, and redirect handling.
  • Added cluster-aware batches, transactions, scripts, scans, blocking commands, server commands, and Pub/Sub.
  • Added hash-slot and hash-tag support, including ClusterSlot access for keys.
  • Added cluster-mode configuration through options and connection strings.

Bug Fixes

  • Improved connection-state reporting, endpoint telemetry, and recovery behavior.
  • Added validation for unsupported sharded subscriptions, nonzero databases, and cross-slot transactions.

Documentation

  • Added Redis Cluster setup, routing, configuration, limitations, and usage guidance.

@coderabbitai

coderabbitai Bot commented Aug 9, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds Redis Cluster support. It adds slot calculation, seed and topology management, redirect handling, cluster-aware commands, batch and transaction routing, configuration parsing, tests, documentation, and benchmarks.

Changes

Redis Cluster support

Layer / File(s) Summary
Slot calculation and routing contracts
src/Respire/Internal/ClusterHash.cs, src/Respire/Protocol/RespWriter.cs, src/Respire/Commands/*, src/Respire/RespireKey.cs, src/Respire/RespireValue.cs
Commands and values expose cluster-slot information. ClusterHash implements Redis CRC16 slots and hash tags.
Cluster configuration and connection management
src/Respire/RespireOptions.cs, src/Respire/Internal/ClientCore.cs, src/Respire/Internal/ClusterRouter.cs, src/Respire/PubSub/SubscriptionHub.cs
Cluster mode accepts seed endpoints, discovers topology with CLUSTER SLOTS, handles redirects, propagates connection state, and disposes managed connections.
Client command and script routing
src/Respire/RespireClient.cs, src/Respire/Networking/RespireConnection.cs, src/Respire/Facets/KeyCommands.cs, src/Respire/Facets/ScriptCommands.cs, src/Respire/Facets/ServerCommands.cs
Regular, blocking, stored-procedure, script, server, and scan operations use cluster-aware connections. MOVED and ASK redirects are retried with ASKING for ASK responses.
Batch, transaction, and cache behavior
src/Respire/RespireBatch.cs, src/Respire/RespireTransaction.cs, src/Respire/Networking/PendingResponseSource.cs, src/Respire.Extensions.Caching/RespireDistributedCache.cs
Batches route operations independently. Transactions require one hash slot. Cluster-aware tracked operations preserve connection identity and deterministic lease placement.
Cluster validation, documentation, and benchmark coverage
tests/Respire.Tests/Networking/*, tests/Respire.Extensions.Caching.Tests/*, README.md, docs/*, benchmarks/Respire.Benchmarks/*
Tests cover hashing, topology, redirects, routing, failover, scanning, validation, cache integration, and parsing. Documentation and benchmarks describe cluster behavior.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RespireClient
  participant ClusterRouter
  participant RedisNode
  RespireClient->>ClusterRouter: Select connection for command slot
  ClusterRouter->>RedisNode: Send command
  RedisNode-->>RespireClient: Result or MOVED/ASK response
  RespireClient->>ClusterRouter: Resolve redirect
  ClusterRouter-->>RespireClient: Target connection
  RespireClient->>RedisNode: ASKING and retry command
Loading

Possibly related PRs

Poem

I hop through slots with ears held high,
CRC16 maps keys nearby.
Seeds reveal the nodes I seek,
MOVED and ASK guide every leap.
Cluster paths now bloom and sing! 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the primary change: adding Redis Cluster support.
Linked Issues check ✅ Passed The changes directly address issue #60 by adding opt-in Redis Cluster support, routing, topology discovery, redirects, and documented limitations.
Out of Scope Changes check ✅ Passed The implementation, documentation, benchmarks, and tests all support the Redis Cluster objectives and issue #60.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/issue-60-cluster

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

🧪 Integration Test Results (net10.0)

ℹ️ Execution Details

  • Framework: net10.0
  • Test Runner: TUnit
  • Runner: Linux - X64
  • Status: success
  • Timestamp: 2026-08-09T10:02:59.497Z

Automated integration testing with Redis containers using TUnit

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

🧪 Integration Test Results (net8.0)

ℹ️ Execution Details

  • Framework: net8.0
  • Test Runner: TUnit
  • Runner: Linux - X64
  • Status: success
  • Timestamp: 2026-08-09T10:03:22.126Z

Automated integration testing with Redis containers using TUnit

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Redis Container Benchmark Results (net8.0)

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host] : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3

Runtime=.NET 8.0 IterationCount=3 WarmupCount=1

Method Mean Error StdDev Min Max Median P95 Ratio RatioSD Allocated Alloc Ratio
Respire_Ping 177.8 μs 10.97 μs 0.60 μs 177.2 μs 178.4 μs 177.8 μs 178.3 μs 1.00 0.00 184 B 1.00
StackExchange_Ping 228.5 μs 4.71 μs 0.26 μs 228.3 μs 228.7 μs 228.7 μs 228.7 μs 1.29 0.00 424 B 2.30
Respire_Set_Small 180.2 μs 16.53 μs 0.91 μs 179.2 μs 181.0 μs 180.4 μs 180.9 μs 1.01 0.01 176 B 0.96
StackExchange_Set_Small 231.8 μs 32.93 μs 1.80 μs 230.3 μs 233.8 μs 231.4 μs 233.6 μs 1.30 0.01 432 B 2.35
Respire_Set_Medium 181.0 μs 22.69 μs 1.24 μs 179.6 μs 181.9 μs 181.5 μs 181.9 μs 1.02 0.01 176 B 0.96
StackExchange_Set_Medium 232.6 μs 5.74 μs 0.31 μs 232.3 μs 232.9 μs 232.6 μs 232.9 μs 1.31 0.00 432 B 2.35
Respire_Set_Large 189.5 μs 18.10 μs 0.99 μs 188.4 μs 190.3 μs 189.7 μs 190.3 μs 1.07 0.01 176 B 0.96
StackExchange_Set_Large 247.9 μs 207.34 μs 11.36 μs 240.4 μs 261.0 μs 242.3 μs 259.1 μs 1.39 0.06 432 B 2.35
Respire_Get 178.8 μs 9.00 μs 0.49 μs 178.3 μs 179.3 μs 178.8 μs 179.2 μs 1.01 0.00 224 B 1.22
StackExchange_Get 230.8 μs 30.56 μs 1.68 μs 229.0 μs 232.2 μs 231.3 μs 232.1 μs 1.30 0.01 504 B 2.74
StackExchange_MGet 239.0 μs 104.85 μs 5.75 μs 235.7 μs 245.7 μs 235.8 μs 244.7 μs 1.34 0.03 896 B 4.87
StackExchange_Pipeline_5_Sets 242.5 μs 29.54 μs 1.62 μs 241.4 μs 244.4 μs 241.7 μs 244.1 μs 1.36 0.01 2968 B 16.13
Respire_Exists 178.7 μs 11.29 μs 0.62 μs 178.0 μs 179.1 μs 179.0 μs 179.1 μs 1.01 0.00 176 B 0.96
StackExchange_Exists 233.1 μs 96.43 μs 5.29 μs 228.6 μs 238.9 μs 231.8 μs 238.2 μs 1.31 0.03 400 B 2.17
Respire_Del 345.6 μs 36.47 μs 2.00 μs 343.9 μs 347.8 μs 345.2 μs 347.5 μs 1.94 0.01 200 B 1.09
StackExchange_Del 447.3 μs 26.32 μs 1.44 μs 445.6 μs 448.3 μs 447.9 μs 448.3 μs 2.52 0.01 664 B 3.61
Respire_Incr 178.4 μs 25.53 μs 1.40 μs 177.5 μs 180.0 μs 177.6 μs 179.8 μs 1.00 0.01 183 B 0.99
StackExchange_Incr 234.6 μs 94.16 μs 5.16 μs 231.4 μs 240.5 μs 231.8 μs 239.6 μs 1.32 0.03 400 B 2.17
View raw benchmark output

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host]     : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3

Runtime=.NET 8.0  IterationCount=3  WarmupCount=1  

Method                        | Mean     | Error     | StdDev   | Min      | Max      | Median   | P95      | Ratio | RatioSD | Allocated | Alloc Ratio |
------------------------------ |---------:|----------:|---------:|---------:|---------:|---------:|---------:|------:|--------:|----------:|------------:|
Respire_Ping                  | 177.8 μs |  10.97 μs |  0.60 μs | 177.2 μs | 178.4 μs | 177.8 μs | 178.3 μs |  1.00 |    0.00 |     184 B |        1.00 |
StackExchange_Ping            | 228.5 μs |   4.71 μs |  0.26 μs | 228.3 μs | 228.7 μs | 228.7 μs | 228.7 μs |  1.29 |    0.00 |     424 B |        2.30 |
Respire_Set_Small             | 180.2 μs |  16.53 μs |  0.91 μs | 179.2 μs | 181.0 μs | 180.4 μs | 180.9 μs |  1.01 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Small       | 231.8 μs |  32.93 μs |  1.80 μs | 230.3 μs | 233.8 μs | 231.4 μs | 233.6 μs |  1.30 |    0.01 |     432 B |        2.35 |
Respire_Set_Medium            | 181.0 μs |  22.69 μs |  1.24 μs | 179.6 μs | 181.9 μs | 181.5 μs | 181.9 μs |  1.02 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Medium      | 232.6 μs |   5.74 μs |  0.31 μs | 232.3 μs | 232.9 μs | 232.6 μs | 232.9 μs |  1.31 |    0.00 |     432 B |        2.35 |
Respire_Set_Large             | 189.5 μs |  18.10 μs |  0.99 μs | 188.4 μs | 190.3 μs | 189.7 μs | 190.3 μs |  1.07 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Large       | 247.9 μs | 207.34 μs | 11.36 μs | 240.4 μs | 261.0 μs | 242.3 μs | 259.1 μs |  1.39 |    0.06 |     432 B |        2.35 |
Respire_Get                   | 178.8 μs |   9.00 μs |  0.49 μs | 178.3 μs | 179.3 μs | 178.8 μs | 179.2 μs |  1.01 |    0.00 |     224 B |        1.22 |
StackExchange_Get             | 230.8 μs |  30.56 μs |  1.68 μs | 229.0 μs | 232.2 μs | 231.3 μs | 232.1 μs |  1.30 |    0.01 |     504 B |        2.74 |
StackExchange_MGet            | 239.0 μs | 104.85 μs |  5.75 μs | 235.7 μs | 245.7 μs | 235.8 μs | 244.7 μs |  1.34 |    0.03 |     896 B |        4.87 |
StackExchange_Pipeline_5_Sets | 242.5 μs |  29.54 μs |  1.62 μs | 241.4 μs | 244.4 μs | 241.7 μs | 244.1 μs |  1.36 |    0.01 |    2968 B |       16.13 |
Respire_Exists                | 178.7 μs |  11.29 μs |  0.62 μs | 178.0 μs | 179.1 μs | 179.0 μs | 179.1 μs |  1.01 |    0.00 |     176 B |        0.96 |
StackExchange_Exists          | 233.1 μs |  96.43 μs |  5.29 μs | 228.6 μs | 238.9 μs | 231.8 μs | 238.2 μs |  1.31 |    0.03 |     400 B |        2.17 |
Respire_Del                   | 345.6 μs |  36.47 μs |  2.00 μs | 343.9 μs | 347.8 μs | 345.2 μs | 347.5 μs |  1.94 |    0.01 |     200 B |        1.09 |
StackExchange_Del             | 447.3 μs |  26.32 μs |  1.44 μs | 445.6 μs | 448.3 μs | 447.9 μs | 448.3 μs |  2.52 |    0.01 |     664 B |        3.61 |
Respire_Incr                  | 178.4 μs |  25.53 μs |  1.40 μs | 177.5 μs | 180.0 μs | 177.6 μs | 179.8 μs |  1.00 |    0.01 |     183 B |        0.99 |
StackExchange_Incr            | 234.6 μs |  94.16 μs |  5.16 μs | 231.4 μs | 240.5 μs | 231.8 μs | 239.6 μs |  1.32 |    0.03 |     400 B |        2.17 |

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Redis Container Benchmark Results (net10.0)

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host] : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3

Runtime=.NET 8.0 IterationCount=3 WarmupCount=1

Method Mean Error StdDev Min Max Median P95 Ratio Allocated Alloc Ratio
Respire_Ping 186.9 μs 15.70 μs 0.86 μs 185.9 μs 187.5 μs 187.3 μs 187.5 μs 1.00 184 B 1.00
StackExchange_Ping 243.4 μs 27.27 μs 1.49 μs 242.2 μs 245.1 μs 242.9 μs 244.9 μs 1.30 424 B 2.30
Respire_Set_Small 187.8 μs 16.63 μs 0.91 μs 186.8 μs 188.5 μs 188.2 μs 188.5 μs 1.00 176 B 0.96
StackExchange_Set_Small 246.0 μs 25.03 μs 1.37 μs 244.9 μs 247.5 μs 245.5 μs 247.3 μs 1.32 432 B 2.35
Respire_Set_Medium 190.6 μs 24.25 μs 1.33 μs 189.7 μs 192.2 μs 190.1 μs 192.0 μs 1.02 176 B 0.96
StackExchange_Set_Medium 246.3 μs 6.04 μs 0.33 μs 246.0 μs 246.6 μs 246.4 μs 246.6 μs 1.32 432 B 2.35
Respire_Set_Large 200.1 μs 23.93 μs 1.31 μs 199.3 μs 201.6 μs 199.4 μs 201.4 μs 1.07 176 B 0.96
StackExchange_Set_Large 254.8 μs 35.73 μs 1.96 μs 253.6 μs 257.1 μs 253.8 μs 256.8 μs 1.36 432 B 2.35
Respire_Get 188.4 μs 29.44 μs 1.61 μs 187.0 μs 190.2 μs 188.2 μs 190.0 μs 1.01 224 B 1.22
StackExchange_Get 245.4 μs 24.38 μs 1.34 μs 244.1 μs 246.8 μs 245.3 μs 246.6 μs 1.31 504 B 2.74
StackExchange_MGet 247.4 μs 33.23 μs 1.82 μs 245.5 μs 249.2 μs 247.5 μs 249.0 μs 1.32 896 B 4.87
StackExchange_Pipeline_5_Sets 259.0 μs 32.01 μs 1.75 μs 257.8 μs 261.0 μs 258.1 μs 260.7 μs 1.39 2968 B 16.13
Respire_Exists 188.5 μs 16.04 μs 0.88 μs 187.5 μs 189.2 μs 188.9 μs 189.1 μs 1.01 176 B 0.96
StackExchange_Exists 244.4 μs 10.91 μs 0.60 μs 244.0 μs 245.1 μs 244.1 μs 245.0 μs 1.31 400 B 2.17
Respire_Del 360.7 μs 33.47 μs 1.83 μs 359.1 μs 362.7 μs 360.3 μs 362.5 μs 1.93 200 B 1.09
StackExchange_Del 471.5 μs 20.48 μs 1.12 μs 470.2 μs 472.2 μs 472.0 μs 472.2 μs 2.52 664 B 3.61
Respire_Incr 188.3 μs 18.85 μs 1.03 μs 187.2 μs 189.2 μs 188.6 μs 189.2 μs 1.01 184 B 1.00
StackExchange_Incr 243.4 μs 42.05 μs 2.30 μs 240.7 μs 244.9 μs 244.6 μs 244.8 μs 1.30 400 B 2.17
View raw benchmark output

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host]     : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3

Runtime=.NET 8.0  IterationCount=3  WarmupCount=1  

Method                        | Mean     | Error    | StdDev  | Min      | Max      | Median   | P95      | Ratio | Allocated | Alloc Ratio |
------------------------------ |---------:|---------:|--------:|---------:|---------:|---------:|---------:|------:|----------:|------------:|
Respire_Ping                  | 186.9 μs | 15.70 μs | 0.86 μs | 185.9 μs | 187.5 μs | 187.3 μs | 187.5 μs |  1.00 |     184 B |        1.00 |
StackExchange_Ping            | 243.4 μs | 27.27 μs | 1.49 μs | 242.2 μs | 245.1 μs | 242.9 μs | 244.9 μs |  1.30 |     424 B |        2.30 |
Respire_Set_Small             | 187.8 μs | 16.63 μs | 0.91 μs | 186.8 μs | 188.5 μs | 188.2 μs | 188.5 μs |  1.00 |     176 B |        0.96 |
StackExchange_Set_Small       | 246.0 μs | 25.03 μs | 1.37 μs | 244.9 μs | 247.5 μs | 245.5 μs | 247.3 μs |  1.32 |     432 B |        2.35 |
Respire_Set_Medium            | 190.6 μs | 24.25 μs | 1.33 μs | 189.7 μs | 192.2 μs | 190.1 μs | 192.0 μs |  1.02 |     176 B |        0.96 |
StackExchange_Set_Medium      | 246.3 μs |  6.04 μs | 0.33 μs | 246.0 μs | 246.6 μs | 246.4 μs | 246.6 μs |  1.32 |     432 B |        2.35 |
Respire_Set_Large             | 200.1 μs | 23.93 μs | 1.31 μs | 199.3 μs | 201.6 μs | 199.4 μs | 201.4 μs |  1.07 |     176 B |        0.96 |
StackExchange_Set_Large       | 254.8 μs | 35.73 μs | 1.96 μs | 253.6 μs | 257.1 μs | 253.8 μs | 256.8 μs |  1.36 |     432 B |        2.35 |
Respire_Get                   | 188.4 μs | 29.44 μs | 1.61 μs | 187.0 μs | 190.2 μs | 188.2 μs | 190.0 μs |  1.01 |     224 B |        1.22 |
StackExchange_Get             | 245.4 μs | 24.38 μs | 1.34 μs | 244.1 μs | 246.8 μs | 245.3 μs | 246.6 μs |  1.31 |     504 B |        2.74 |
StackExchange_MGet            | 247.4 μs | 33.23 μs | 1.82 μs | 245.5 μs | 249.2 μs | 247.5 μs | 249.0 μs |  1.32 |     896 B |        4.87 |
StackExchange_Pipeline_5_Sets | 259.0 μs | 32.01 μs | 1.75 μs | 257.8 μs | 261.0 μs | 258.1 μs | 260.7 μs |  1.39 |    2968 B |       16.13 |
Respire_Exists                | 188.5 μs | 16.04 μs | 0.88 μs | 187.5 μs | 189.2 μs | 188.9 μs | 189.1 μs |  1.01 |     176 B |        0.96 |
StackExchange_Exists          | 244.4 μs | 10.91 μs | 0.60 μs | 244.0 μs | 245.1 μs | 244.1 μs | 245.0 μs |  1.31 |     400 B |        2.17 |
Respire_Del                   | 360.7 μs | 33.47 μs | 1.83 μs | 359.1 μs | 362.7 μs | 360.3 μs | 362.5 μs |  1.93 |     200 B |        1.09 |
StackExchange_Del             | 471.5 μs | 20.48 μs | 1.12 μs | 470.2 μs | 472.2 μs | 472.0 μs | 472.2 μs |  2.52 |     664 B |        3.61 |
Respire_Incr                  | 188.3 μs | 18.85 μs | 1.03 μs | 187.2 μs | 189.2 μs | 188.6 μs | 189.2 μs |  1.01 |     184 B |        1.00 |
StackExchange_Incr            | 243.4 μs | 42.05 μs | 2.30 μs | 240.7 μs | 244.9 μs | 244.6 μs | 244.8 μs |  1.30 |     400 B |        2.17 |

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9cb04c9e0b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Respire/Commands/Verbs.cs Outdated
public readonly int RoutingKeyIndex;

public Verb(params string[] words)
public Verb(params string[] words) : this(0, words)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Traverse every master when scanning a cluster

Treating argument zero as the routing key by default makes ScanAsync hash its cursor and potentially send each page to a different master. Redis SCAN cursors are local to one node, so a cursor returned by one master is not valid traversal state for another; on a multi-master cluster this can omit keys, duplicate pages, or terminate before all nodes are visited. Cluster scans need to iterate each master independently rather than slot-route the cursor.

Useful? React with 👍 / 👎.

Comment thread src/Respire/RespireClient.cs Outdated
Comment on lines +467 to +469
var askingReply = await ClusterRouter.SendAskingAsync(connection, cancellationToken)
.ConfigureAwait(false);
askingReply.Dispose();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Enqueue ASKING and its redirected command atomically

When an ASK redirect occurs under concurrent traffic, awaiting ASKING before separately enqueueing the original command allows another caller's command to be inserted between them on this multiplexed connection. Redis applies ASKING only to the immediately following command, so the unrelated command can consume the state and the redirected operation can repeatedly fail during slot migration. Both frames need to be appended atomically on the selected connection.

Useful? React with 👍 / 👎.

Comment on lines +94 to +96
await EnsureConnectedAsync(cancellationToken).ConfigureAwait(false);
var node = slot is { } value ? Volatile.Read(ref _slots[value]) : null;
node ??= Volatile.Read(ref _seed)!;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use cached slot routes when a seed is unavailable

After topology has been loaded, every keyed command still reconnects the configured seed before consulting _slots. If the seed node is down while the cached slot owner remains healthy, EnsureConnectedAsync fails and prevents otherwise routable cluster traffic from reaching that owner, turning a single seed outage into a client-wide outage. For known slots, resolve and connect the cached node first and require seed discovery only when no route is available.

Useful? React with 👍 / 👎.

Comment thread src/Respire/Commands/Verbs.cs Outdated
public static readonly Verb XAck = new("XACK");
public static readonly Verb XGroupCreate = new("XGROUP", "CREATE");
public static readonly Verb XReadGroup = new("XREADGROUP");
public static readonly Verb XReadGroup = new(7, "XREADGROUP");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Route XREADGROUP using the stream key

In the argument array built by ReadGroupAsync, index 7 is the literal STREAMS token and the actual stream key is at index 8. Hashing index 7 therefore sends every polling iteration to the node owning the slot for "STREAMS"; Redis must redirect it to the real key's owner each time, and the learned route is never used because subsequent calls hash the same literal again. Set the routing index to 8.

Useful? React with 👍 / 👎.

Comment thread src/Respire/RespireTransaction.cs Outdated
Comment on lines 222 to 225
connection ??= await _client.AcquireConnectionAsync(
_hasClusterSlot ? _clusterSlot : null, token)
.ConfigureAwait(false);
return await connection.SendTransactionAsync(_buffer.WrittenMemory, _ops.Count, token).ConfigureAwait(false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Follow cluster redirects for transactions

A transaction is sent directly on the cached slot connection without the redirect handling used by ordinary cluster commands. When ownership changes after CLUSTER SLOTS was loaded, MULTI and its queued command reach the former owner and the commit surfaces MOVED, ASK, or EXECABORT instead of retrying against the new owner, despite cluster redirects being handled elsewhere. Same-slot transactions therefore fail during normal resharding or failover until another command refreshes the route.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Respire/Internal/ClientCore.cs (1)

28-33: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

A failing ClusterRouter constructor leaks the multiplexer and the dedicated pool.

ClusterRouter throws ArgumentException when options.Database != 0. At that point Multiplexer and DedicatedPool are already constructed, and the ClientCore constructor unwinds without returning an instance. No caller can dispose them, so both the sockets and the pooled buffers leak. Validate the cluster preconditions before you open any connection.

🛡️ Proposed fix
     public ClientCore(RespireOptions options)
     {
+        if (options.Cluster && options.Database != 0)
+        {
+            throw new ArgumentException("Redis Cluster supports database 0 only.", nameof(options));
+        }
+
         Options = options;
         Logger = options.CreateLogger("Respire.RespireClient");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Respire/Internal/ClientCore.cs` around lines 28 - 33, Validate the
cluster-specific preconditions, including the supported options.Database value,
before constructing RespireConnectionMultiplexer or DedicatedConnectionPool in
the ClientCore constructor. Ensure ClusterRouter is only created after these
checks pass, so invalid cluster configuration throws before any connection
resources are opened.
🧹 Nitpick comments (5)
src/Respire/Internal/ClusterRouter.cs (1)

357-357: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Disposing _seedGate can race an in-flight EnsureConnectedAsync.

DisposeAsync disposes _seedGate while another caller can still hold it. Disposing a SemaphoreSlim does not release pending waiters, and the finally { _seedGate.Release(); } in EnsureConnectedAsync then throws ObjectDisposedException that replaces the real outcome. SemaphoreSlim requires disposal only when AvailableWaitHandle was used, and this code never reads it. Drop the call.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Respire/Internal/ClusterRouter.cs` at line 357, Remove the
_seedGate.Dispose() call from DisposeAsync. Keep _seedGate undisposed so an
in-flight EnsureConnectedAsync can complete its finally release without
ObjectDisposedException.
src/Respire/RespireOptions.cs (1)

220-222: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

A connection string cannot express more than one seed.

Parse always returns a single endpoint from uri.Host and uri.Port (Line 230). ClusterRouter treats Endpoints as the seed list and fails over across it, so seed failover is unreachable for users who configure Respire with a connection string. Consider accepting a comma-separated host list in the authority, or state in the Parse documentation that multiple seeds require Endpoints.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Respire/RespireOptions.cs` around lines 220 - 222, Update the
connection-string parsing flow in RespireOptions.Parse to support multiple
comma-separated hosts in the authority and populate the full seed list used by
ClusterRouter, while preserving the existing single-host behavior;
alternatively, document in Parse that connection strings support only one seed
and multiple seed failover requires the Endpoints configuration.
src/Respire/Protocol/RespWriter.cs (1)

101-105: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid boxing default IRespCommand.TryGetClusterSlot during cluster routing.

SendClusterAsync and SendBlockingClusterAsync call this member on where TCommand : struct, IRespCommand. Structs without an explicit implementation, such as Cmd, use default interface dispatch and can allocate one box per cluster-routed send. Add an explicit no-slot implementation to command structs used by these paths. Keep the default for external custom commands.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Respire/Protocol/RespWriter.cs` around lines 101 - 105, Add explicit
TryGetClusterSlot implementations returning false to the built-in command
structs used by SendClusterAsync and SendBlockingClusterAsync, including Cmd, so
generic cluster routing avoids default interface dispatch and boxing. Leave
IRespCommand.TryGetClusterSlot’s default implementation unchanged for external
custom commands.
src/Respire/RespireClient.cs (2)

623-628: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Telemetry records the seed endpoint, not the routed node.

RespireTelemetry.StartOperation receives core.Multiplexer.Host and core.Multiplexer.Port. In cluster mode the command runs on a slot-owning node from pool, which can be a different node. Lines 536-537 and 859-860 changed the other paths to record the real connection endpoint. This path keeps the seed endpoint, so blocking-command spans in cluster mode point at the wrong server.

The connection is not known before pool.RentAsync. Consider moving StartOperation after the first rent, or resolving the endpoint from pool.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Respire/RespireClient.cs` around lines 623 - 628, Update the telemetry
setup in the blocking-command path to use the routed connection’s actual
endpoint rather than core.Multiplexer.Host and core.Multiplexer.Port. Move
RespireTelemetry.StartOperation until after pool.RentAsync or resolve the
endpoint from the rented connection, while preserving the existing operation,
database, and storedProcedureName values.

778-791: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Reuse the sliced script tail

BuildScriptTail always creates at least one element, so tail[0] is safe on both paths. Create tail[1..] once before try and reuse it for the EVALSHA and EVAL commands.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Respire/RespireClient.cs` around lines 778 - 791, In the script execution
flow around BuildScriptTail, compute tail[1..] once before the try block and
reuse that sliced tail for both SendClusterAsync calls. Keep tail[0] as the
key-count argument in the EVALSHA and EVAL command construction, relying on
BuildScriptTail’s guaranteed non-empty result.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@README.md`:
- Around line 87-110: Update the Redis Cluster section near the existing WATCH
limitation to state that cluster sharded pub/sub is unsupported, including that
SSUBSCRIBE is unavailable in cluster mode.

In `@src/Respire/Commands/CommandStructs.cs`:
- Around line 19-20: Update Verbs.XReadGroup.RoutingKeyIndex to 8 so cluster
routing hashes the resolved stream key placed at argument index 8 by
ReadGroupAsync, rather than the "STREAMS" marker at index 7. Leave the existing
Eval and EvalSha Cmd2N routing behavior unchanged.

In `@src/Respire/Commands/Verbs.cs`:
- Line 126: Update the XReadGroup routing key index from 7 to 8 in the Verb
declaration, preserving the existing XREADGROUP verb name and ensuring it
matches ReadGroupAsync’s argument layout where index 7 is “STREAMS”.

In `@src/Respire/Internal/ClusterRouter.cs`:
- Around line 202-205: Update SendAskingAsync and the SendClusterAsync
redirect-retry flow so ASKING and the redirected command are submitted as one
indivisible connection-level batch, or execute both on a dedicated connection.
Do not enqueue ASKING and then wait for its reply before enqueueing the
redirected command; preserve the existing retry behavior while preventing other
multiplexed callers from interposing commands.

In `@src/Respire/RespireBatch.cs`:
- Around line 113-128: Update the XML documentation for the public SendAsync
method to describe cluster mode accurately: operations are dispatched
independently through RespireClient.SendAsync rather than sent in one flush,
connection-acquisition failures fault only the affected operation while
SendAsync completes normally and records the first error through telemetry, and
ordering across different slots is not guaranteed.

In `@src/Respire/RespireClient.cs`:
- Around line 646-664: In the error-handling branch of the cluster send flow,
return the healthy connection to the current pool and set returned to true
before throwing any non-redirect server error. Preserve the existing redirect
handling and ensure the catch path does not discard the connection after
ResponseReader.ServerError has consumed the response.

In `@src/Respire/RespireTransaction.cs`:
- Around line 257-268: The cluster transaction commit path lacks handling for
MOVED redirects when its slot-to-node mapping is stale. Update CommitAsync to
retry the complete transaction against the redirected owner, using the existing
ClusterRouter.RedirectLimit behavior and refreshing the slot mapping before
retrying; preserve the same-slot validation in the shown transaction setup.

In `@tests/Respire.Tests/Networking/ClusterTests.cs`:
- Around line 164-183: Update Connect_TriesLaterSeedWhenFirstIsUnavailable so
the fallback FakeRespServer returns a valid RESP CLUSTER SLOTS topology
identifying an advertised node, rather than only PONG. After connection, execute
a keyed command routed through that topology and retain assertions confirming
the fallback seed handled CLUSTER SLOTS and the keyed request.
- Around line 48-68: Extend AskRedirect_SendsAskingOnTargetWithoutCachingSlot
with a second GetStringAsync("key") call, then assert the seed receives the
second GET request and the target receives ASKING followed by GET key again,
confirming the ASK redirect target is not cached.

---

Outside diff comments:
In `@src/Respire/Internal/ClientCore.cs`:
- Around line 28-33: Validate the cluster-specific preconditions, including the
supported options.Database value, before constructing
RespireConnectionMultiplexer or DedicatedConnectionPool in the ClientCore
constructor. Ensure ClusterRouter is only created after these checks pass, so
invalid cluster configuration throws before any connection resources are opened.

---

Nitpick comments:
In `@src/Respire/Internal/ClusterRouter.cs`:
- Line 357: Remove the _seedGate.Dispose() call from DisposeAsync. Keep
_seedGate undisposed so an in-flight EnsureConnectedAsync can complete its
finally release without ObjectDisposedException.

In `@src/Respire/Protocol/RespWriter.cs`:
- Around line 101-105: Add explicit TryGetClusterSlot implementations returning
false to the built-in command structs used by SendClusterAsync and
SendBlockingClusterAsync, including Cmd, so generic cluster routing avoids
default interface dispatch and boxing. Leave IRespCommand.TryGetClusterSlot’s
default implementation unchanged for external custom commands.

In `@src/Respire/RespireClient.cs`:
- Around line 623-628: Update the telemetry setup in the blocking-command path
to use the routed connection’s actual endpoint rather than core.Multiplexer.Host
and core.Multiplexer.Port. Move RespireTelemetry.StartOperation until after
pool.RentAsync or resolve the endpoint from the rented connection, while
preserving the existing operation, database, and storedProcedureName values.
- Around line 778-791: In the script execution flow around BuildScriptTail,
compute tail[1..] once before the try block and reuse that sliced tail for both
SendClusterAsync calls. Keep tail[0] as the key-count argument in the EVALSHA
and EVAL command construction, relying on BuildScriptTail’s guaranteed non-empty
result.

In `@src/Respire/RespireOptions.cs`:
- Around line 220-222: Update the connection-string parsing flow in
RespireOptions.Parse to support multiple comma-separated hosts in the authority
and populate the full seed list used by ClusterRouter, while preserving the
existing single-host behavior; alternatively, document in Parse that connection
strings support only one seed and multiple seed failover requires the Endpoints
configuration.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 826861bc-3108-47cb-8c1f-45c2239c5010

📥 Commits

Reviewing files that changed from the base of the PR and between eb859d4 and 9cb04c9.

📒 Files selected for processing (18)
  • README.md
  • benchmarks/Respire.Benchmarks/RespireKeyBenchmarks.cs
  • docs/API_DESIGN.md
  • docs/WHY_RESPIRE.md
  • src/Respire/Commands/CommandStructs.cs
  • src/Respire/Commands/Verbs.cs
  • src/Respire/Internal/ClientCore.cs
  • src/Respire/Internal/ClusterHash.cs
  • src/Respire/Internal/ClusterRouter.cs
  • src/Respire/Protocol/RespWriter.cs
  • src/Respire/PubSub/SubscriptionHub.cs
  • src/Respire/RespireBatch.cs
  • src/Respire/RespireClient.cs
  • src/Respire/RespireKey.cs
  • src/Respire/RespireOptions.cs
  • src/Respire/RespireTransaction.cs
  • src/Respire/RespireValue.cs
  • tests/Respire.Tests/Networking/ClusterTests.cs

Comment thread README.md Resolved
Comment thread src/Respire/Commands/CommandStructs.cs Resolved
Comment thread src/Respire/Commands/Verbs.cs Resolved
Comment thread src/Respire/Internal/ClusterRouter.cs Resolved
Comment thread src/Respire/RespireBatch.cs Resolved
Comment thread src/Respire/RespireClient.cs Resolved
Comment thread src/Respire/RespireTransaction.cs Resolved

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Respire/Internal/ClusterRouter.cs`:
- Around line 89-100: Before fallback selection in ClusterRouter’s cached-owner
path (src/Respire/Internal/ClusterRouter.cs#L89-L100), invalidate the failed
slot owner conditionally or force a slot refresh so the fallback can use a seed
and receive updated MOVED information; apply the same change before selecting
the dedicated pool in src/Respire/Internal/ClusterRouter.cs#L133-L144.

In `@tests/Respire.Tests/Networking/FakeRespServer.cs`:
- Around line 139-147: Update the reply-flushing condition in the connection
handling logic to compare pendingReplies.Count with MinimumCommandsBeforeReply
instead of the global CommandsSeen value, while preserving the existing reply
iteration and queue-clearing behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 330815a5-0d38-417a-99aa-5f052db6af29

📥 Commits

Reviewing files that changed from the base of the PR and between 9cb04c9 and 3251c28.

📒 Files selected for processing (13)
  • README.md
  • src/Respire/Commands/CommandStructs.cs
  • src/Respire/Commands/Verbs.cs
  • src/Respire/Facets/KeyCommands.cs
  • src/Respire/Internal/ClientCore.cs
  • src/Respire/Internal/ClusterRouter.cs
  • src/Respire/Networking/RespireConnection.cs
  • src/Respire/RespireBatch.cs
  • src/Respire/RespireClient.cs
  • src/Respire/RespireOptions.cs
  • src/Respire/RespireTransaction.cs
  • tests/Respire.Tests/Networking/ClusterTests.cs
  • tests/Respire.Tests/Networking/FakeRespServer.cs
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/Respire/Internal/ClientCore.cs
  • src/Respire/RespireOptions.cs
  • src/Respire/RespireBatch.cs
  • src/Respire/Commands/CommandStructs.cs
  • src/Respire/RespireClient.cs

Comment thread src/Respire/Internal/ClusterRouter.cs Resolved
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

🧪 Integration Test Results (net10.0)

ℹ️ Execution Details

  • Framework: net10.0
  • Test Runner: TUnit
  • Runner: Linux - X64
  • Status: success
  • Timestamp: 2026-08-09T11:18:42.306Z

Automated integration testing with Redis containers using TUnit

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

🧪 Integration Test Results (net8.0)

ℹ️ Execution Details

  • Framework: net8.0
  • Test Runner: TUnit
  • Runner: Linux - X64
  • Status: success
  • Timestamp: 2026-08-09T11:18:57.897Z

Automated integration testing with Redis containers using TUnit

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Redis Container Benchmark Results (net10.0)

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host] : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3

Runtime=.NET 8.0 IterationCount=3 WarmupCount=1

Method Mean Error StdDev Min Max Median P95 Ratio RatioSD Allocated Alloc Ratio
Respire_Ping 170.5 μs 97.42 μs 5.34 μs 164.5 μs 174.9 μs 172.1 μs 174.6 μs 1.00 0.04 184 B 1.00
StackExchange_Ping 231.0 μs 121.01 μs 6.63 μs 225.3 μs 238.3 μs 229.3 μs 237.4 μs 1.36 0.05 424 B 2.30
Respire_Set_Small 179.0 μs 19.25 μs 1.06 μs 178.1 μs 180.1 μs 178.8 μs 180.0 μs 1.05 0.03 176 B 0.96
StackExchange_Set_Small 230.3 μs 58.20 μs 3.19 μs 228.2 μs 233.9 μs 228.6 μs 233.4 μs 1.35 0.04 432 B 2.35
Respire_Set_Medium 178.5 μs 12.75 μs 0.70 μs 177.7 μs 179.1 μs 178.8 μs 179.0 μs 1.05 0.03 171 B 0.93
StackExchange_Set_Medium 231.3 μs 33.47 μs 1.83 μs 230.2 μs 233.4 μs 230.3 μs 233.1 μs 1.36 0.04 432 B 2.35
Respire_Set_Large 187.1 μs 11.17 μs 0.61 μs 186.4 μs 187.6 μs 187.2 μs 187.6 μs 1.10 0.03 176 B 0.96
StackExchange_Set_Large 236.9 μs 36.19 μs 1.98 μs 234.9 μs 238.8 μs 237.0 μs 238.7 μs 1.39 0.04 432 B 2.35
Respire_Get 175.5 μs 28.64 μs 1.57 μs 174.2 μs 177.3 μs 175.0 μs 177.0 μs 1.03 0.03 223 B 1.21
StackExchange_Get 229.2 μs 18.94 μs 1.04 μs 228.2 μs 230.3 μs 229.0 μs 230.1 μs 1.34 0.04 504 B 2.74
StackExchange_MGet 241.7 μs 245.29 μs 13.45 μs 233.8 μs 257.3 μs 234.2 μs 255.0 μs 1.42 0.08 896 B 4.87
StackExchange_Pipeline_5_Sets 239.8 μs 51.32 μs 2.81 μs 236.7 μs 242.0 μs 240.9 μs 241.9 μs 1.41 0.04 2968 B 16.13
Respire_Exists 175.6 μs 7.38 μs 0.40 μs 175.2 μs 176.0 μs 175.7 μs 176.0 μs 1.03 0.03 175 B 0.95
StackExchange_Exists 228.8 μs 69.33 μs 3.80 μs 226.4 μs 233.2 μs 226.8 μs 232.5 μs 1.34 0.04 400 B 2.17
Respire_Del 336.4 μs 26.51 μs 1.45 μs 335.0 μs 337.9 μs 336.5 μs 337.7 μs 1.97 0.05 200 B 1.09
StackExchange_Del 441.9 μs 35.37 μs 1.94 μs 439.7 μs 443.6 μs 442.3 μs 443.4 μs 2.59 0.07 664 B 3.61
Respire_Incr 177.5 μs 23.19 μs 1.27 μs 176.2 μs 178.7 μs 177.6 μs 178.6 μs 1.04 0.03 184 B 1.00
StackExchange_Incr 229.5 μs 12.22 μs 0.67 μs 229.0 μs 230.2 μs 229.3 μs 230.1 μs 1.35 0.04 400 B 2.17
View raw benchmark output

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host]     : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3

Runtime=.NET 8.0  IterationCount=3  WarmupCount=1  

Method                        | Mean     | Error     | StdDev   | Min      | Max      | Median   | P95      | Ratio | RatioSD | Allocated | Alloc Ratio |
------------------------------ |---------:|----------:|---------:|---------:|---------:|---------:|---------:|------:|--------:|----------:|------------:|
Respire_Ping                  | 170.5 μs |  97.42 μs |  5.34 μs | 164.5 μs | 174.9 μs | 172.1 μs | 174.6 μs |  1.00 |    0.04 |     184 B |        1.00 |
StackExchange_Ping            | 231.0 μs | 121.01 μs |  6.63 μs | 225.3 μs | 238.3 μs | 229.3 μs | 237.4 μs |  1.36 |    0.05 |     424 B |        2.30 |
Respire_Set_Small             | 179.0 μs |  19.25 μs |  1.06 μs | 178.1 μs | 180.1 μs | 178.8 μs | 180.0 μs |  1.05 |    0.03 |     176 B |        0.96 |
StackExchange_Set_Small       | 230.3 μs |  58.20 μs |  3.19 μs | 228.2 μs | 233.9 μs | 228.6 μs | 233.4 μs |  1.35 |    0.04 |     432 B |        2.35 |
Respire_Set_Medium            | 178.5 μs |  12.75 μs |  0.70 μs | 177.7 μs | 179.1 μs | 178.8 μs | 179.0 μs |  1.05 |    0.03 |     171 B |        0.93 |
StackExchange_Set_Medium      | 231.3 μs |  33.47 μs |  1.83 μs | 230.2 μs | 233.4 μs | 230.3 μs | 233.1 μs |  1.36 |    0.04 |     432 B |        2.35 |
Respire_Set_Large             | 187.1 μs |  11.17 μs |  0.61 μs | 186.4 μs | 187.6 μs | 187.2 μs | 187.6 μs |  1.10 |    0.03 |     176 B |        0.96 |
StackExchange_Set_Large       | 236.9 μs |  36.19 μs |  1.98 μs | 234.9 μs | 238.8 μs | 237.0 μs | 238.7 μs |  1.39 |    0.04 |     432 B |        2.35 |
Respire_Get                   | 175.5 μs |  28.64 μs |  1.57 μs | 174.2 μs | 177.3 μs | 175.0 μs | 177.0 μs |  1.03 |    0.03 |     223 B |        1.21 |
StackExchange_Get             | 229.2 μs |  18.94 μs |  1.04 μs | 228.2 μs | 230.3 μs | 229.0 μs | 230.1 μs |  1.34 |    0.04 |     504 B |        2.74 |
StackExchange_MGet            | 241.7 μs | 245.29 μs | 13.45 μs | 233.8 μs | 257.3 μs | 234.2 μs | 255.0 μs |  1.42 |    0.08 |     896 B |        4.87 |
StackExchange_Pipeline_5_Sets | 239.8 μs |  51.32 μs |  2.81 μs | 236.7 μs | 242.0 μs | 240.9 μs | 241.9 μs |  1.41 |    0.04 |    2968 B |       16.13 |
Respire_Exists                | 175.6 μs |   7.38 μs |  0.40 μs | 175.2 μs | 176.0 μs | 175.7 μs | 176.0 μs |  1.03 |    0.03 |     175 B |        0.95 |
StackExchange_Exists          | 228.8 μs |  69.33 μs |  3.80 μs | 226.4 μs | 233.2 μs | 226.8 μs | 232.5 μs |  1.34 |    0.04 |     400 B |        2.17 |
Respire_Del                   | 336.4 μs |  26.51 μs |  1.45 μs | 335.0 μs | 337.9 μs | 336.5 μs | 337.7 μs |  1.97 |    0.05 |     200 B |        1.09 |
StackExchange_Del             | 441.9 μs |  35.37 μs |  1.94 μs | 439.7 μs | 443.6 μs | 442.3 μs | 443.4 μs |  2.59 |    0.07 |     664 B |        3.61 |
Respire_Incr                  | 177.5 μs |  23.19 μs |  1.27 μs | 176.2 μs | 178.7 μs | 177.6 μs | 178.6 μs |  1.04 |    0.03 |     184 B |        1.00 |
StackExchange_Incr            | 229.5 μs |  12.22 μs |  0.67 μs | 229.0 μs | 230.2 μs | 229.3 μs | 230.1 μs |  1.35 |    0.04 |     400 B |        2.17 |

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Redis Container Benchmark Results (net8.0)

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
Intel Xeon Platinum 8370C CPU 2.80GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host] : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v4
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v4

Runtime=.NET 8.0 IterationCount=3 WarmupCount=1

Method Mean Error StdDev Min Max Median P95 Ratio RatioSD Allocated Alloc Ratio
Respire_Ping 110.1 μs 37.85 μs 2.07 μs 108.52 μs 112.5 μs 109.4 μs 112.2 μs 1.00 0.02 184 B 1.00
StackExchange_Ping 153.6 μs 8.84 μs 0.48 μs 153.12 μs 154.1 μs 153.7 μs 154.0 μs 1.40 0.02 424 B 2.30
Respire_Set_Small 107.5 μs 201.33 μs 11.04 μs 94.77 μs 114.0 μs 113.7 μs 114.0 μs 0.98 0.09 176 B 0.96
StackExchange_Set_Small 158.0 μs 76.75 μs 4.21 μs 155.36 μs 162.9 μs 155.9 μs 162.2 μs 1.44 0.04 432 B 2.35
Respire_Set_Medium 116.0 μs 32.24 μs 1.77 μs 114.44 μs 117.9 μs 115.7 μs 117.7 μs 1.05 0.02 176 B 0.96
StackExchange_Set_Medium 157.7 μs 17.02 μs 0.93 μs 156.79 μs 158.6 μs 157.6 μs 158.5 μs 1.43 0.02 432 B 2.35
Respire_Set_Large 121.3 μs 85.13 μs 4.67 μs 115.97 μs 124.5 μs 123.5 μs 124.4 μs 1.10 0.04 176 B 0.96
StackExchange_Set_Large 164.5 μs 5.69 μs 0.31 μs 164.17 μs 164.8 μs 164.5 μs 164.8 μs 1.49 0.02 432 B 2.35
Respire_Get 111.8 μs 37.14 μs 2.04 μs 110.09 μs 114.0 μs 111.2 μs 113.8 μs 1.02 0.02 224 B 1.22
StackExchange_Get 155.4 μs 20.95 μs 1.15 μs 154.66 μs 156.8 μs 154.9 μs 156.6 μs 1.41 0.02 504 B 2.74
StackExchange_MGet 159.8 μs 11.99 μs 0.66 μs 159.03 μs 160.3 μs 160.0 μs 160.2 μs 1.45 0.02 896 B 4.87
StackExchange_Pipeline_5_Sets 165.2 μs 5.12 μs 0.28 μs 164.85 μs 165.4 μs 165.2 μs 165.4 μs 1.50 0.02 2968 B 16.13
Respire_Exists 111.7 μs 11.66 μs 0.64 μs 110.98 μs 112.2 μs 111.8 μs 112.2 μs 1.01 0.02 176 B 0.96
StackExchange_Exists 154.0 μs 11.99 μs 0.66 μs 153.49 μs 154.7 μs 153.7 μs 154.6 μs 1.40 0.02 400 B 2.17
Respire_Del 217.0 μs 18.34 μs 1.01 μs 215.92 μs 217.9 μs 217.0 μs 217.8 μs 1.97 0.03 200 B 1.09
StackExchange_Del 303.1 μs 5.32 μs 0.29 μs 302.79 μs 303.4 μs 303.1 μs 303.3 μs 2.75 0.04 664 B 3.61
Respire_Incr 111.5 μs 12.96 μs 0.71 μs 110.70 μs 112.1 μs 111.6 μs 112.1 μs 1.01 0.02 184 B 1.00
StackExchange_Incr 157.0 μs 16.40 μs 0.90 μs 156.06 μs 157.9 μs 157.0 μs 157.8 μs 1.43 0.02 400 B 2.17
View raw benchmark output

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
Intel Xeon Platinum 8370C CPU 2.80GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host]     : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v4
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v4

Runtime=.NET 8.0  IterationCount=3  WarmupCount=1  

Method                        | Mean     | Error     | StdDev   | Min       | Max      | Median   | P95      | Ratio | RatioSD | Allocated | Alloc Ratio |
------------------------------ |---------:|----------:|---------:|----------:|---------:|---------:|---------:|------:|--------:|----------:|------------:|
Respire_Ping                  | 110.1 μs |  37.85 μs |  2.07 μs | 108.52 μs | 112.5 μs | 109.4 μs | 112.2 μs |  1.00 |    0.02 |     184 B |        1.00 |
StackExchange_Ping            | 153.6 μs |   8.84 μs |  0.48 μs | 153.12 μs | 154.1 μs | 153.7 μs | 154.0 μs |  1.40 |    0.02 |     424 B |        2.30 |
Respire_Set_Small             | 107.5 μs | 201.33 μs | 11.04 μs |  94.77 μs | 114.0 μs | 113.7 μs | 114.0 μs |  0.98 |    0.09 |     176 B |        0.96 |
StackExchange_Set_Small       | 158.0 μs |  76.75 μs |  4.21 μs | 155.36 μs | 162.9 μs | 155.9 μs | 162.2 μs |  1.44 |    0.04 |     432 B |        2.35 |
Respire_Set_Medium            | 116.0 μs |  32.24 μs |  1.77 μs | 114.44 μs | 117.9 μs | 115.7 μs | 117.7 μs |  1.05 |    0.02 |     176 B |        0.96 |
StackExchange_Set_Medium      | 157.7 μs |  17.02 μs |  0.93 μs | 156.79 μs | 158.6 μs | 157.6 μs | 158.5 μs |  1.43 |    0.02 |     432 B |        2.35 |
Respire_Set_Large             | 121.3 μs |  85.13 μs |  4.67 μs | 115.97 μs | 124.5 μs | 123.5 μs | 124.4 μs |  1.10 |    0.04 |     176 B |        0.96 |
StackExchange_Set_Large       | 164.5 μs |   5.69 μs |  0.31 μs | 164.17 μs | 164.8 μs | 164.5 μs | 164.8 μs |  1.49 |    0.02 |     432 B |        2.35 |
Respire_Get                   | 111.8 μs |  37.14 μs |  2.04 μs | 110.09 μs | 114.0 μs | 111.2 μs | 113.8 μs |  1.02 |    0.02 |     224 B |        1.22 |
StackExchange_Get             | 155.4 μs |  20.95 μs |  1.15 μs | 154.66 μs | 156.8 μs | 154.9 μs | 156.6 μs |  1.41 |    0.02 |     504 B |        2.74 |
StackExchange_MGet            | 159.8 μs |  11.99 μs |  0.66 μs | 159.03 μs | 160.3 μs | 160.0 μs | 160.2 μs |  1.45 |    0.02 |     896 B |        4.87 |
StackExchange_Pipeline_5_Sets | 165.2 μs |   5.12 μs |  0.28 μs | 164.85 μs | 165.4 μs | 165.2 μs | 165.4 μs |  1.50 |    0.02 |    2968 B |       16.13 |
Respire_Exists                | 111.7 μs |  11.66 μs |  0.64 μs | 110.98 μs | 112.2 μs | 111.8 μs | 112.2 μs |  1.01 |    0.02 |     176 B |        0.96 |
StackExchange_Exists          | 154.0 μs |  11.99 μs |  0.66 μs | 153.49 μs | 154.7 μs | 153.7 μs | 154.6 μs |  1.40 |    0.02 |     400 B |        2.17 |
Respire_Del                   | 217.0 μs |  18.34 μs |  1.01 μs | 215.92 μs | 217.9 μs | 217.0 μs | 217.8 μs |  1.97 |    0.03 |     200 B |        1.09 |
StackExchange_Del             | 303.1 μs |   5.32 μs |  0.29 μs | 302.79 μs | 303.4 μs | 303.1 μs | 303.3 μs |  2.75 |    0.04 |     664 B |        3.61 |
Respire_Incr                  | 111.5 μs |  12.96 μs |  0.71 μs | 110.70 μs | 112.1 μs | 111.6 μs | 112.1 μs |  1.01 |    0.02 |     184 B |        1.00 |
StackExchange_Incr            | 157.0 μs |  16.40 μs |  0.90 μs | 156.06 μs | 157.9 μs | 157.0 μs | 157.8 μs |  1.43 |    0.02 |     400 B |        2.17 |

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

🧪 Integration Test Results (net10.0)

ℹ️ Execution Details

  • Framework: net10.0
  • Test Runner: TUnit
  • Runner: Linux - X64
  • Status: success
  • Timestamp: 2026-08-09T11:22:30.953Z

Automated integration testing with Redis containers using TUnit

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

🧪 Integration Test Results (net8.0)

ℹ️ Execution Details

  • Framework: net8.0
  • Test Runner: TUnit
  • Runner: Linux - X64
  • Status: success
  • Timestamp: 2026-08-09T11:22:39.931Z

Automated integration testing with Redis containers using TUnit

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/Respire.Tests/Networking/ClusterTests.cs`:
- Around line 281-293: Update the retry loop around client.GetStringAsync so it
performs only one request after the cached owner becomes unavailable, and assert
that the returned value is "fallback". Remove the repeated exception-swallowing
and delay behavior so the test verifies fallback during the failed operation
itself.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 01c52725-bbd0-493d-9b00-d20581816ec0

📥 Commits

Reviewing files that changed from the base of the PR and between 3251c28 and 5d9feb7.

📒 Files selected for processing (8)
  • README.md
  • docs/API_DESIGN.md
  • docs/WHY_RESPIRE.md
  • src/Respire/Internal/ClusterRouter.cs
  • src/Respire/Networking/RespireConnection.cs
  • src/Respire/RespireOptions.cs
  • tests/Respire.Tests/Networking/ClusterTests.cs
  • tests/Respire.Tests/Networking/FakeRespServer.cs
🚧 Files skipped from review as they are similar to previous changes (5)
  • docs/WHY_RESPIRE.md
  • README.md
  • src/Respire/Networking/RespireConnection.cs
  • src/Respire/RespireOptions.cs
  • src/Respire/Internal/ClusterRouter.cs

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Redis Container Benchmark Results (net8.0)

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 9V74 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host] : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3

Runtime=.NET 8.0 IterationCount=3 WarmupCount=1

Method Mean Error StdDev Min Max Median P95 Ratio RatioSD Allocated Alloc Ratio
Respire_Ping 106.71 μs 96.295 μs 5.278 μs 100.68 μs 110.5 μs 108.95 μs 110.3 μs 1.00 0.06 168 B 1.00
StackExchange_Ping 193.46 μs 27.219 μs 1.492 μs 191.74 μs 194.4 μs 194.19 μs 194.4 μs 1.82 0.08 423 B 2.52
Respire_Set_Small 109.06 μs 227.936 μs 12.494 μs 98.14 μs 122.7 μs 106.35 μs 121.1 μs 1.02 0.11 167 B 0.99
StackExchange_Set_Small 197.21 μs 4.201 μs 0.230 μs 197.05 μs 197.5 μs 197.10 μs 197.4 μs 1.85 0.08 432 B 2.57
Respire_Set_Medium 119.01 μs 125.477 μs 6.878 μs 112.42 μs 126.1 μs 118.48 μs 125.4 μs 1.12 0.07 157 B 0.93
StackExchange_Set_Medium 200.21 μs 154.171 μs 8.451 μs 193.04 μs 209.5 μs 198.05 μs 208.4 μs 1.88 0.11 432 B 2.57
Respire_Set_Large 138.67 μs 75.729 μs 4.151 μs 134.25 μs 142.5 μs 139.28 μs 142.2 μs 1.30 0.07 171 B 1.02
StackExchange_Set_Large 211.59 μs 46.261 μs 2.536 μs 209.71 μs 214.5 μs 210.59 μs 214.1 μs 1.99 0.09 432 B 2.57
Respire_Get 121.17 μs 262.157 μs 14.370 μs 104.99 μs 132.4 μs 126.08 μs 131.8 μs 1.14 0.13 218 B 1.30
StackExchange_Get 195.56 μs 35.844 μs 1.965 μs 193.51 μs 197.4 μs 195.75 μs 197.3 μs 1.84 0.08 503 B 2.99
StackExchange_MGet 209.58 μs 273.922 μs 15.015 μs 200.05 μs 226.9 μs 201.81 μs 224.4 μs 1.97 0.15 896 B 5.33
StackExchange_Pipeline_5_Sets 226.04 μs 629.720 μs 34.517 μs 205.39 μs 265.9 μs 206.84 μs 260.0 μs 2.12 0.30 2968 B 17.67
Respire_Exists 99.83 μs 287.584 μs 15.763 μs 86.07 μs 117.0 μs 96.41 μs 115.0 μs 0.94 0.13 161 B 0.96
StackExchange_Exists 193.73 μs 31.633 μs 1.734 μs 191.79 μs 195.1 μs 194.30 μs 195.0 μs 1.82 0.08 400 B 2.38
Respire_Del 228.26 μs 301.796 μs 16.542 μs 209.27 μs 239.6 μs 235.90 μs 239.2 μs 2.14 0.16 193 B 1.15
StackExchange_Del 372.69 μs 30.138 μs 1.652 μs 371.42 μs 374.6 μs 372.09 μs 374.3 μs 3.50 0.15 664 B 3.95
Respire_Incr 122.16 μs 72.636 μs 3.981 μs 117.58 μs 124.8 μs 124.06 μs 124.8 μs 1.15 0.06 178 B 1.06
StackExchange_Incr 194.82 μs 33.586 μs 1.841 μs 193.14 μs 196.8 μs 194.54 μs 196.6 μs 1.83 0.08 400 B 2.38
View raw benchmark output

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 9V74 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host]     : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3

Runtime=.NET 8.0  IterationCount=3  WarmupCount=1  

Method                        | Mean      | Error      | StdDev    | Min       | Max      | Median    | P95      | Ratio | RatioSD | Allocated | Alloc Ratio |
------------------------------ |----------:|-----------:|----------:|----------:|---------:|----------:|---------:|------:|--------:|----------:|------------:|
Respire_Ping                  | 106.71 μs |  96.295 μs |  5.278 μs | 100.68 μs | 110.5 μs | 108.95 μs | 110.3 μs |  1.00 |    0.06 |     168 B |        1.00 |
StackExchange_Ping            | 193.46 μs |  27.219 μs |  1.492 μs | 191.74 μs | 194.4 μs | 194.19 μs | 194.4 μs |  1.82 |    0.08 |     423 B |        2.52 |
Respire_Set_Small             | 109.06 μs | 227.936 μs | 12.494 μs |  98.14 μs | 122.7 μs | 106.35 μs | 121.1 μs |  1.02 |    0.11 |     167 B |        0.99 |
StackExchange_Set_Small       | 197.21 μs |   4.201 μs |  0.230 μs | 197.05 μs | 197.5 μs | 197.10 μs | 197.4 μs |  1.85 |    0.08 |     432 B |        2.57 |
Respire_Set_Medium            | 119.01 μs | 125.477 μs |  6.878 μs | 112.42 μs | 126.1 μs | 118.48 μs | 125.4 μs |  1.12 |    0.07 |     157 B |        0.93 |
StackExchange_Set_Medium      | 200.21 μs | 154.171 μs |  8.451 μs | 193.04 μs | 209.5 μs | 198.05 μs | 208.4 μs |  1.88 |    0.11 |     432 B |        2.57 |
Respire_Set_Large             | 138.67 μs |  75.729 μs |  4.151 μs | 134.25 μs | 142.5 μs | 139.28 μs | 142.2 μs |  1.30 |    0.07 |     171 B |        1.02 |
StackExchange_Set_Large       | 211.59 μs |  46.261 μs |  2.536 μs | 209.71 μs | 214.5 μs | 210.59 μs | 214.1 μs |  1.99 |    0.09 |     432 B |        2.57 |
Respire_Get                   | 121.17 μs | 262.157 μs | 14.370 μs | 104.99 μs | 132.4 μs | 126.08 μs | 131.8 μs |  1.14 |    0.13 |     218 B |        1.30 |
StackExchange_Get             | 195.56 μs |  35.844 μs |  1.965 μs | 193.51 μs | 197.4 μs | 195.75 μs | 197.3 μs |  1.84 |    0.08 |     503 B |        2.99 |
StackExchange_MGet            | 209.58 μs | 273.922 μs | 15.015 μs | 200.05 μs | 226.9 μs | 201.81 μs | 224.4 μs |  1.97 |    0.15 |     896 B |        5.33 |
StackExchange_Pipeline_5_Sets | 226.04 μs | 629.720 μs | 34.517 μs | 205.39 μs | 265.9 μs | 206.84 μs | 260.0 μs |  2.12 |    0.30 |    2968 B |       17.67 |
Respire_Exists                |  99.83 μs | 287.584 μs | 15.763 μs |  86.07 μs | 117.0 μs |  96.41 μs | 115.0 μs |  0.94 |    0.13 |     161 B |        0.96 |
StackExchange_Exists          | 193.73 μs |  31.633 μs |  1.734 μs | 191.79 μs | 195.1 μs | 194.30 μs | 195.0 μs |  1.82 |    0.08 |     400 B |        2.38 |
Respire_Del                   | 228.26 μs | 301.796 μs | 16.542 μs | 209.27 μs | 239.6 μs | 235.90 μs | 239.2 μs |  2.14 |    0.16 |     193 B |        1.15 |
StackExchange_Del             | 372.69 μs |  30.138 μs |  1.652 μs | 371.42 μs | 374.6 μs | 372.09 μs | 374.3 μs |  3.50 |    0.15 |     664 B |        3.95 |
Respire_Incr                  | 122.16 μs |  72.636 μs |  3.981 μs | 117.58 μs | 124.8 μs | 124.06 μs | 124.8 μs |  1.15 |    0.06 |     178 B |        1.06 |
StackExchange_Incr            | 194.82 μs |  33.586 μs |  1.841 μs | 193.14 μs | 196.8 μs | 194.54 μs | 196.6 μs |  1.83 |    0.08 |     400 B |        2.38 |

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Redis Container Benchmark Results (net10.0)

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 9V74 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host] : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3

Runtime=.NET 8.0 IterationCount=3 WarmupCount=1

Method Mean Error StdDev Min Max Median P95 Ratio RatioSD Allocated Alloc Ratio
Respire_Ping 101.3 μs 208.92 μs 11.45 μs 90.62 μs 113.4 μs 99.88 μs 112.0 μs 1.01 0.14 171 B 1.00
StackExchange_Ping 183.5 μs 9.89 μs 0.54 μs 182.97 μs 184.1 μs 183.58 μs 184.0 μs 1.83 0.18 424 B 2.48
Respire_Set_Small 109.9 μs 190.22 μs 10.43 μs 99.23 μs 120.1 μs 110.51 μs 119.1 μs 1.09 0.14 172 B 1.01
StackExchange_Set_Small 189.3 μs 9.70 μs 0.53 μs 188.69 μs 189.7 μs 189.34 μs 189.7 μs 1.88 0.18 432 B 2.53
Respire_Set_Medium 110.0 μs 198.24 μs 10.87 μs 97.88 μs 118.9 μs 113.13 μs 118.3 μs 1.09 0.14 138 B 0.81
StackExchange_Set_Medium 188.6 μs 36.05 μs 1.98 μs 186.31 μs 189.8 μs 189.62 μs 189.8 μs 1.88 0.18 432 B 2.53
Respire_Set_Large 122.4 μs 144.79 μs 7.94 μs 113.28 μs 127.4 μs 126.57 μs 127.4 μs 1.22 0.14 158 B 0.92
StackExchange_Set_Large 209.1 μs 186.36 μs 10.22 μs 203.11 μs 220.9 μs 203.30 μs 219.1 μs 2.08 0.22 432 B 2.53
Respire_Get 110.9 μs 274.15 μs 15.03 μs 94.16 μs 123.2 μs 115.28 μs 122.4 μs 1.10 0.17 212 B 1.24
StackExchange_Get 188.6 μs 16.46 μs 0.90 μs 187.66 μs 189.5 μs 188.74 μs 189.4 μs 1.88 0.18 504 B 2.95
StackExchange_MGet 200.5 μs 171.81 μs 9.42 μs 192.89 μs 211.0 μs 197.51 μs 209.7 μs 2.00 0.21 896 B 5.24
StackExchange_Pipeline_5_Sets 234.1 μs 950.22 μs 52.08 μs 202.86 μs 294.2 μs 205.17 μs 285.3 μs 2.33 0.50 2968 B 17.36
Respire_Exists 104.5 μs 190.60 μs 10.45 μs 93.15 μs 113.8 μs 106.46 μs 113.0 μs 1.04 0.14 158 B 0.92
StackExchange_Exists 184.6 μs 4.07 μs 0.22 μs 184.43 μs 184.9 μs 184.59 μs 184.8 μs 1.84 0.18 398 B 2.33
Respire_Del 220.2 μs 29.66 μs 1.63 μs 219.14 μs 222.0 μs 219.29 μs 221.8 μs 2.19 0.21 194 B 1.13
StackExchange_Del 360.9 μs 44.78 μs 2.45 μs 359.03 μs 363.7 μs 359.92 μs 363.3 μs 3.59 0.35 664 B 3.88
Respire_Incr 101.3 μs 97.45 μs 5.34 μs 97.13 μs 107.3 μs 99.35 μs 106.5 μs 1.01 0.11 165 B 0.96
StackExchange_Incr 186.0 μs 15.58 μs 0.85 μs 185.31 μs 187.0 μs 185.81 μs 186.9 μs 1.85 0.18 400 B 2.34
View raw benchmark output

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 9V74 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host]     : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3

Runtime=.NET 8.0  IterationCount=3  WarmupCount=1  

Method                        | Mean     | Error     | StdDev   | Min       | Max      | Median    | P95      | Ratio | RatioSD | Allocated | Alloc Ratio |
------------------------------ |---------:|----------:|---------:|----------:|---------:|----------:|---------:|------:|--------:|----------:|------------:|
Respire_Ping                  | 101.3 μs | 208.92 μs | 11.45 μs |  90.62 μs | 113.4 μs |  99.88 μs | 112.0 μs |  1.01 |    0.14 |     171 B |        1.00 |
StackExchange_Ping            | 183.5 μs |   9.89 μs |  0.54 μs | 182.97 μs | 184.1 μs | 183.58 μs | 184.0 μs |  1.83 |    0.18 |     424 B |        2.48 |
Respire_Set_Small             | 109.9 μs | 190.22 μs | 10.43 μs |  99.23 μs | 120.1 μs | 110.51 μs | 119.1 μs |  1.09 |    0.14 |     172 B |        1.01 |
StackExchange_Set_Small       | 189.3 μs |   9.70 μs |  0.53 μs | 188.69 μs | 189.7 μs | 189.34 μs | 189.7 μs |  1.88 |    0.18 |     432 B |        2.53 |
Respire_Set_Medium            | 110.0 μs | 198.24 μs | 10.87 μs |  97.88 μs | 118.9 μs | 113.13 μs | 118.3 μs |  1.09 |    0.14 |     138 B |        0.81 |
StackExchange_Set_Medium      | 188.6 μs |  36.05 μs |  1.98 μs | 186.31 μs | 189.8 μs | 189.62 μs | 189.8 μs |  1.88 |    0.18 |     432 B |        2.53 |
Respire_Set_Large             | 122.4 μs | 144.79 μs |  7.94 μs | 113.28 μs | 127.4 μs | 126.57 μs | 127.4 μs |  1.22 |    0.14 |     158 B |        0.92 |
StackExchange_Set_Large       | 209.1 μs | 186.36 μs | 10.22 μs | 203.11 μs | 220.9 μs | 203.30 μs | 219.1 μs |  2.08 |    0.22 |     432 B |        2.53 |
Respire_Get                   | 110.9 μs | 274.15 μs | 15.03 μs |  94.16 μs | 123.2 μs | 115.28 μs | 122.4 μs |  1.10 |    0.17 |     212 B |        1.24 |
StackExchange_Get             | 188.6 μs |  16.46 μs |  0.90 μs | 187.66 μs | 189.5 μs | 188.74 μs | 189.4 μs |  1.88 |    0.18 |     504 B |        2.95 |
StackExchange_MGet            | 200.5 μs | 171.81 μs |  9.42 μs | 192.89 μs | 211.0 μs | 197.51 μs | 209.7 μs |  2.00 |    0.21 |     896 B |        5.24 |
StackExchange_Pipeline_5_Sets | 234.1 μs | 950.22 μs | 52.08 μs | 202.86 μs | 294.2 μs | 205.17 μs | 285.3 μs |  2.33 |    0.50 |    2968 B |       17.36 |
Respire_Exists                | 104.5 μs | 190.60 μs | 10.45 μs |  93.15 μs | 113.8 μs | 106.46 μs | 113.0 μs |  1.04 |    0.14 |     158 B |        0.92 |
StackExchange_Exists          | 184.6 μs |   4.07 μs |  0.22 μs | 184.43 μs | 184.9 μs | 184.59 μs | 184.8 μs |  1.84 |    0.18 |     398 B |        2.33 |
Respire_Del                   | 220.2 μs |  29.66 μs |  1.63 μs | 219.14 μs | 222.0 μs | 219.29 μs | 221.8 μs |  2.19 |    0.21 |     194 B |        1.13 |
StackExchange_Del             | 360.9 μs |  44.78 μs |  2.45 μs | 359.03 μs | 363.7 μs | 359.92 μs | 363.3 μs |  3.59 |    0.35 |     664 B |        3.88 |
Respire_Incr                  | 101.3 μs |  97.45 μs |  5.34 μs |  97.13 μs | 107.3 μs |  99.35 μs | 106.5 μs |  1.01 |    0.11 |     165 B |        0.96 |
StackExchange_Incr            | 186.0 μs |  15.58 μs |  0.85 μs | 185.31 μs | 187.0 μs | 185.81 μs | 186.9 μs |  1.85 |    0.18 |     400 B |        2.34 |

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5d9feb7c89

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +784 to +790
if (core.Cluster is { } cluster)
{
var arguments = tail[1..];
RespValue clusterReply;
try
{
clusterReply = await SendClusterAsync(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Route tracked cache scripts through the cluster router

When Respire.Extensions.Caching uses a real client whose ACL permits CLIENT ID (the normal default), GetTrackedWireAsync selects StartTrackedScriptExecution for cache reads and writes, bypassing this new cluster-aware ExecuteScriptAsync branch and sending the script directly through the seed multiplexer. For any cache key owned by another master, GET/SET/refresh therefore surface MOVED instead of following it; the tracked execution path must acquire its connection by the script key's slot and handle redirects too.

Useful? React with 👍 / 👎.

Comment thread src/Respire/RespireTransaction.cs Outdated
Comment on lines +229 to +233
var reply = await (sendAsking
? ClusterRouter.SendAskingTransactionAsync(
connection, _buffer.WrittenMemory, _ops.Count, token)
: connection.SendTransactionAsync(_buffer.WrittenMemory, _ops.Count, token))
.ConfigureAwait(false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve queue-time redirects when retrying transactions

When ownership changes before commit, Redis returns MOVED/ASK for the keyed command while it is being queued inside MULTI, then returns EXECABORT from EXEC. SendTransactionAsync discards the MULTI and queued-command replies, so this new loop sees only EXECABORT, which is not recognized as a redirect and cannot be retried. Fresh evidence in this revision is that the redirect loop consumes only the final reply, while Transaction_FollowsMovedRedirectAsOneUnit unrealistically scripts MOVED as that final reply; queue-time errors must be preserved or routing must be validated before entering MULTI.

Useful? React with 👍 / 👎.

Route tracked cache scripts and correction fences through their key's node, including redirects. Retain transaction queue errors so MOVED and ASK can retry the full MULTI/EXEC unit.
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

🧪 Integration Test Results (net10.0)

ℹ️ Execution Details

  • Framework: net10.0
  • Test Runner: TUnit
  • Runner: Linux - X64
  • Status: success
  • Timestamp: 2026-08-09T12:06:01.673Z

Automated integration testing with Redis containers using TUnit

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

🧪 Integration Test Results (net8.0)

ℹ️ Execution Details

  • Framework: net8.0
  • Test Runner: TUnit
  • Runner: Linux - X64
  • Status: success
  • Timestamp: 2026-08-09T12:06:21.656Z

Automated integration testing with Redis containers using TUnit

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Redis Container Benchmark Results (net10.0)

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 9V74 2.87GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host] : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3

Runtime=.NET 8.0 IterationCount=3 WarmupCount=1

Method Mean Error StdDev Min Max Median P95 Ratio RatioSD Allocated Alloc Ratio
Respire_Ping 102.19 μs 45.433 μs 2.490 μs 99.61 μs 104.6 μs 102.37 μs 104.4 μs 1.00 0.03 152 B 1.00
StackExchange_Ping 186.18 μs 27.598 μs 1.513 μs 185.10 μs 187.9 μs 185.54 μs 187.7 μs 1.82 0.04 424 B 2.79
Respire_Set_Small 114.94 μs 90.461 μs 4.958 μs 110.34 μs 120.2 μs 114.30 μs 119.6 μs 1.13 0.05 156 B 1.03
StackExchange_Set_Small 189.07 μs 15.309 μs 0.839 μs 188.58 μs 190.0 μs 188.58 μs 189.9 μs 1.85 0.04 432 B 2.84
Respire_Set_Medium 110.86 μs 157.396 μs 8.627 μs 104.12 μs 120.6 μs 107.87 μs 119.3 μs 1.09 0.08 164 B 1.08
StackExchange_Set_Medium 189.89 μs 17.221 μs 0.944 μs 189.18 μs 191.0 μs 189.53 μs 190.8 μs 1.86 0.04 432 B 2.84
Respire_Set_Large 138.12 μs 61.697 μs 3.382 μs 134.38 μs 141.0 μs 139.02 μs 140.8 μs 1.35 0.04 160 B 1.05
StackExchange_Set_Large 205.46 μs 14.972 μs 0.821 μs 204.78 μs 206.4 μs 205.24 μs 206.3 μs 2.01 0.04 432 B 2.84
Respire_Get 98.92 μs 181.571 μs 9.953 μs 87.48 μs 105.6 μs 103.71 μs 105.4 μs 0.97 0.09 199 B 1.31
StackExchange_Get 186.93 μs 26.892 μs 1.474 μs 185.50 μs 188.4 μs 186.86 μs 188.3 μs 1.83 0.04 504 B 3.32
StackExchange_MGet 194.76 μs 13.528 μs 0.741 μs 194.14 μs 195.6 μs 194.56 μs 195.5 μs 1.91 0.04 896 B 5.89
StackExchange_Pipeline_5_Sets 218.62 μs 197.533 μs 10.827 μs 206.63 μs 227.7 μs 221.57 μs 227.1 μs 2.14 0.10 2967 B 19.52
Respire_Exists 97.02 μs 146.068 μs 8.006 μs 90.69 μs 106.0 μs 94.35 μs 104.9 μs 0.95 0.07 163 B 1.07
StackExchange_Exists 187.88 μs 14.256 μs 0.781 μs 187.34 μs 188.8 μs 187.52 μs 188.6 μs 1.84 0.04 400 B 2.63
Respire_Del 226.80 μs 78.101 μs 4.281 μs 224.26 μs 231.7 μs 224.40 μs 231.0 μs 2.22 0.06 196 B 1.29
StackExchange_Del 361.78 μs 14.438 μs 0.791 μs 361.05 μs 362.6 μs 361.66 μs 362.5 μs 3.54 0.08 664 B 4.37
Respire_Incr 104.60 μs 115.875 μs 6.352 μs 98.77 μs 111.4 μs 103.66 μs 110.6 μs 1.02 0.06 144 B 0.95
StackExchange_Incr 187.99 μs 2.939 μs 0.161 μs 187.81 μs 188.1 μs 188.07 μs 188.1 μs 1.84 0.04 400 B 2.63
Respire_Transaction_2 122.93 μs 99.195 μs 5.437 μs 117.48 μs 128.4 μs 122.96 μs 127.8 μs 1.20 0.05 1069 B 7.03
View raw benchmark output

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 9V74 2.87GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host]     : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3

Runtime=.NET 8.0  IterationCount=3  WarmupCount=1  

Method                        | Mean      | Error      | StdDev    | Min       | Max      | Median    | P95      | Ratio | RatioSD | Allocated | Alloc Ratio |
------------------------------ |----------:|-----------:|----------:|----------:|---------:|----------:|---------:|------:|--------:|----------:|------------:|
Respire_Ping                  | 102.19 μs |  45.433 μs |  2.490 μs |  99.61 μs | 104.6 μs | 102.37 μs | 104.4 μs |  1.00 |    0.03 |     152 B |        1.00 |
StackExchange_Ping            | 186.18 μs |  27.598 μs |  1.513 μs | 185.10 μs | 187.9 μs | 185.54 μs | 187.7 μs |  1.82 |    0.04 |     424 B |        2.79 |
Respire_Set_Small             | 114.94 μs |  90.461 μs |  4.958 μs | 110.34 μs | 120.2 μs | 114.30 μs | 119.6 μs |  1.13 |    0.05 |     156 B |        1.03 |
StackExchange_Set_Small       | 189.07 μs |  15.309 μs |  0.839 μs | 188.58 μs | 190.0 μs | 188.58 μs | 189.9 μs |  1.85 |    0.04 |     432 B |        2.84 |
Respire_Set_Medium            | 110.86 μs | 157.396 μs |  8.627 μs | 104.12 μs | 120.6 μs | 107.87 μs | 119.3 μs |  1.09 |    0.08 |     164 B |        1.08 |
StackExchange_Set_Medium      | 189.89 μs |  17.221 μs |  0.944 μs | 189.18 μs | 191.0 μs | 189.53 μs | 190.8 μs |  1.86 |    0.04 |     432 B |        2.84 |
Respire_Set_Large             | 138.12 μs |  61.697 μs |  3.382 μs | 134.38 μs | 141.0 μs | 139.02 μs | 140.8 μs |  1.35 |    0.04 |     160 B |        1.05 |
StackExchange_Set_Large       | 205.46 μs |  14.972 μs |  0.821 μs | 204.78 μs | 206.4 μs | 205.24 μs | 206.3 μs |  2.01 |    0.04 |     432 B |        2.84 |
Respire_Get                   |  98.92 μs | 181.571 μs |  9.953 μs |  87.48 μs | 105.6 μs | 103.71 μs | 105.4 μs |  0.97 |    0.09 |     199 B |        1.31 |
StackExchange_Get             | 186.93 μs |  26.892 μs |  1.474 μs | 185.50 μs | 188.4 μs | 186.86 μs | 188.3 μs |  1.83 |    0.04 |     504 B |        3.32 |
StackExchange_MGet            | 194.76 μs |  13.528 μs |  0.741 μs | 194.14 μs | 195.6 μs | 194.56 μs | 195.5 μs |  1.91 |    0.04 |     896 B |        5.89 |
StackExchange_Pipeline_5_Sets | 218.62 μs | 197.533 μs | 10.827 μs | 206.63 μs | 227.7 μs | 221.57 μs | 227.1 μs |  2.14 |    0.10 |    2967 B |       19.52 |
Respire_Exists                |  97.02 μs | 146.068 μs |  8.006 μs |  90.69 μs | 106.0 μs |  94.35 μs | 104.9 μs |  0.95 |    0.07 |     163 B |        1.07 |
StackExchange_Exists          | 187.88 μs |  14.256 μs |  0.781 μs | 187.34 μs | 188.8 μs | 187.52 μs | 188.6 μs |  1.84 |    0.04 |     400 B |        2.63 |
Respire_Del                   | 226.80 μs |  78.101 μs |  4.281 μs | 224.26 μs | 231.7 μs | 224.40 μs | 231.0 μs |  2.22 |    0.06 |     196 B |        1.29 |
StackExchange_Del             | 361.78 μs |  14.438 μs |  0.791 μs | 361.05 μs | 362.6 μs | 361.66 μs | 362.5 μs |  3.54 |    0.08 |     664 B |        4.37 |
Respire_Incr                  | 104.60 μs | 115.875 μs |  6.352 μs |  98.77 μs | 111.4 μs | 103.66 μs | 110.6 μs |  1.02 |    0.06 |     144 B |        0.95 |
StackExchange_Incr            | 187.99 μs |   2.939 μs |  0.161 μs | 187.81 μs | 188.1 μs | 188.07 μs | 188.1 μs |  1.84 |    0.04 |     400 B |        2.63 |
Respire_Transaction_2         | 122.93 μs |  99.195 μs |  5.437 μs | 117.48 μs | 128.4 μs | 122.96 μs | 127.8 μs |  1.20 |    0.05 |    1069 B |        7.03 |

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Redis Container Benchmark Results (net8.0)

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
INTEL XEON PLATINUM 8573C 2.30GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host] : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v4
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v4

Runtime=.NET 8.0 IterationCount=3 WarmupCount=1

Method Mean Error StdDev Min Max Median P95 Ratio RatioSD Allocated Alloc Ratio
Respire_Ping 97.47 μs 13.041 μs 0.715 μs 96.81 μs 98.23 μs 97.36 μs 98.14 μs 1.00 0.01 184 B 1.00
StackExchange_Ping 100.77 μs 8.753 μs 0.480 μs 100.36 μs 101.30 μs 100.66 μs 101.23 μs 1.03 0.01 407 B 2.21
Respire_Set_Small 99.98 μs 23.518 μs 1.289 μs 98.49 μs 100.76 μs 100.69 μs 100.75 μs 1.03 0.01 176 B 0.96
StackExchange_Set_Small 101.31 μs 30.271 μs 1.659 μs 99.40 μs 102.43 μs 102.10 μs 102.40 μs 1.04 0.02 416 B 2.26
Respire_Set_Medium 98.10 μs 56.428 μs 3.093 μs 94.57 μs 100.31 μs 99.44 μs 100.22 μs 1.01 0.03 176 B 0.96
StackExchange_Set_Medium 101.85 μs 11.660 μs 0.639 μs 101.14 μs 102.40 μs 102.00 μs 102.36 μs 1.04 0.01 416 B 2.26
Respire_Set_Large 107.98 μs 19.633 μs 1.076 μs 106.99 μs 109.12 μs 107.82 μs 108.99 μs 1.11 0.01 176 B 0.96
StackExchange_Set_Large 112.23 μs 21.620 μs 1.185 μs 110.95 μs 113.29 μs 112.44 μs 113.21 μs 1.15 0.01 416 B 2.26
Respire_Get 100.14 μs 41.323 μs 2.265 μs 98.14 μs 102.60 μs 99.68 μs 102.31 μs 1.03 0.02 223 B 1.21
StackExchange_Get 101.81 μs 15.241 μs 0.835 μs 101.03 μs 102.69 μs 101.71 μs 102.59 μs 1.04 0.01 424 B 2.30
StackExchange_MGet 105.64 μs 8.080 μs 0.443 μs 105.15 μs 106.02 μs 105.74 μs 105.99 μs 1.08 0.01 656 B 3.57
StackExchange_Pipeline_5_Sets 120.25 μs 143.338 μs 7.857 μs 115.46 μs 129.31 μs 115.96 μs 127.98 μs 1.23 0.07 2888 B 15.70
Respire_Exists 98.77 μs 19.524 μs 1.070 μs 97.66 μs 99.80 μs 98.86 μs 99.70 μs 1.01 0.01 176 B 0.96
StackExchange_Exists 99.04 μs 37.208 μs 2.039 μs 97.50 μs 101.36 μs 98.28 μs 101.05 μs 1.02 0.02 400 B 2.17
Respire_Del 186.43 μs 67.482 μs 3.699 μs 182.41 μs 189.69 μs 187.18 μs 189.44 μs 1.91 0.04 200 B 1.09
StackExchange_Del 192.44 μs 19.306 μs 1.058 μs 191.29 μs 193.36 μs 192.68 μs 193.30 μs 1.97 0.02 648 B 3.52
Respire_Incr 97.98 μs 5.959 μs 0.327 μs 97.62 μs 98.25 μs 98.07 μs 98.23 μs 1.01 0.01 181 B 0.98
StackExchange_Incr 99.62 μs 6.346 μs 0.348 μs 99.29 μs 99.99 μs 99.59 μs 99.95 μs 1.02 0.01 399 B 2.17
Respire_Transaction_2 103.09 μs 6.130 μs 0.336 μs 102.70 μs 103.31 μs 103.26 μs 103.30 μs 1.06 0.01 1064 B 5.78
View raw benchmark output

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
INTEL XEON PLATINUM 8573C 2.30GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host]     : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v4
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v4

Runtime=.NET 8.0  IterationCount=3  WarmupCount=1  

Method                        | Mean      | Error      | StdDev   | Min       | Max       | Median    | P95       | Ratio | RatioSD | Allocated | Alloc Ratio |
------------------------------ |----------:|-----------:|---------:|----------:|----------:|----------:|----------:|------:|--------:|----------:|------------:|
Respire_Ping                  |  97.47 μs |  13.041 μs | 0.715 μs |  96.81 μs |  98.23 μs |  97.36 μs |  98.14 μs |  1.00 |    0.01 |     184 B |        1.00 |
StackExchange_Ping            | 100.77 μs |   8.753 μs | 0.480 μs | 100.36 μs | 101.30 μs | 100.66 μs | 101.23 μs |  1.03 |    0.01 |     407 B |        2.21 |
Respire_Set_Small             |  99.98 μs |  23.518 μs | 1.289 μs |  98.49 μs | 100.76 μs | 100.69 μs | 100.75 μs |  1.03 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Small       | 101.31 μs |  30.271 μs | 1.659 μs |  99.40 μs | 102.43 μs | 102.10 μs | 102.40 μs |  1.04 |    0.02 |     416 B |        2.26 |
Respire_Set_Medium            |  98.10 μs |  56.428 μs | 3.093 μs |  94.57 μs | 100.31 μs |  99.44 μs | 100.22 μs |  1.01 |    0.03 |     176 B |        0.96 |
StackExchange_Set_Medium      | 101.85 μs |  11.660 μs | 0.639 μs | 101.14 μs | 102.40 μs | 102.00 μs | 102.36 μs |  1.04 |    0.01 |     416 B |        2.26 |
Respire_Set_Large             | 107.98 μs |  19.633 μs | 1.076 μs | 106.99 μs | 109.12 μs | 107.82 μs | 108.99 μs |  1.11 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Large       | 112.23 μs |  21.620 μs | 1.185 μs | 110.95 μs | 113.29 μs | 112.44 μs | 113.21 μs |  1.15 |    0.01 |     416 B |        2.26 |
Respire_Get                   | 100.14 μs |  41.323 μs | 2.265 μs |  98.14 μs | 102.60 μs |  99.68 μs | 102.31 μs |  1.03 |    0.02 |     223 B |        1.21 |
StackExchange_Get             | 101.81 μs |  15.241 μs | 0.835 μs | 101.03 μs | 102.69 μs | 101.71 μs | 102.59 μs |  1.04 |    0.01 |     424 B |        2.30 |
StackExchange_MGet            | 105.64 μs |   8.080 μs | 0.443 μs | 105.15 μs | 106.02 μs | 105.74 μs | 105.99 μs |  1.08 |    0.01 |     656 B |        3.57 |
StackExchange_Pipeline_5_Sets | 120.25 μs | 143.338 μs | 7.857 μs | 115.46 μs | 129.31 μs | 115.96 μs | 127.98 μs |  1.23 |    0.07 |    2888 B |       15.70 |
Respire_Exists                |  98.77 μs |  19.524 μs | 1.070 μs |  97.66 μs |  99.80 μs |  98.86 μs |  99.70 μs |  1.01 |    0.01 |     176 B |        0.96 |
StackExchange_Exists          |  99.04 μs |  37.208 μs | 2.039 μs |  97.50 μs | 101.36 μs |  98.28 μs | 101.05 μs |  1.02 |    0.02 |     400 B |        2.17 |
Respire_Del                   | 186.43 μs |  67.482 μs | 3.699 μs | 182.41 μs | 189.69 μs | 187.18 μs | 189.44 μs |  1.91 |    0.04 |     200 B |        1.09 |
StackExchange_Del             | 192.44 μs |  19.306 μs | 1.058 μs | 191.29 μs | 193.36 μs | 192.68 μs | 193.30 μs |  1.97 |    0.02 |     648 B |        3.52 |
Respire_Incr                  |  97.98 μs |   5.959 μs | 0.327 μs |  97.62 μs |  98.25 μs |  98.07 μs |  98.23 μs |  1.01 |    0.01 |     181 B |        0.98 |
StackExchange_Incr            |  99.62 μs |   6.346 μs | 0.348 μs |  99.29 μs |  99.99 μs |  99.59 μs |  99.95 μs |  1.02 |    0.01 |     399 B |        2.17 |
Respire_Transaction_2         | 103.09 μs |   6.130 μs | 0.336 μs | 102.70 μs | 103.31 μs | 103.26 μs | 103.30 μs |  1.06 |    0.01 |    1064 B |        5.78 |

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 883d7a6b51

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Respire/RespireClient.cs Outdated
Comment on lines 1493 to 1494
await multiplexer.SendToAllConnectionsAsync(
new Cmd2N(Verbs.Eval, script.Source, tail[0], tail[1..]), CancellationToken.None).ConfigureAwait(false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve ASKING when broadcasting cache corrections

When a tracked cache SET or sliding-refresh script follows an ASK redirect and then loses or times out waiting for that retry's reply, ConnectionIdentity points at the importing node, but this correction broadcasts a plain EVAL. The importing node requires a fresh one-shot ASKING before every keyed correction, so it returns MOVED instead; RunCorrectionAsync propagates that server error and the uncertain write can retain the stale or overlong TTL the correction was meant to prevent. Send the correction with ASKING on each connection, or fence the original connection and reroute the correction normally.

Useful? React with 👍 / 👎.

Comment thread src/Respire/RespireClient.cs Outdated
Comment on lines +788 to +790
var response = await (sendAsking
? ClusterRouter.SendAskingUncheckedAsync(connection, in command, cancellationToken)
: connection.SendAsync(in command, cancellationToken))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Suppress the response watchdog for cluster blocking calls

When RespireOptions.ResponseTimeout is configured and a cluster blocking operation such as BLPOP legitimately waits longer than that interval, this path uses the ordinary SendAsync/prefixed send instead of SendWithoutResponseTimeoutAsync, so the receive watchdog aborts the dedicated connection and the blocking call fails. The non-cluster path explicitly suppresses this watchdog because blocking is intentional; the cluster path needs the same suppression for both normal and ASKING sends.

Useful? React with 👍 / 👎.

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

🧪 Integration Test Results (net10.0)

ℹ️ Execution Details

  • Framework: net10.0
  • Test Runner: TUnit
  • Runner: Linux - X64
  • Status: success
  • Timestamp: 2026-08-09T21:29:41.142Z

Automated integration testing with Redis containers using TUnit

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

🧪 Integration Test Results (net8.0)

ℹ️ Execution Details

  • Framework: net8.0
  • Test Runner: TUnit
  • Runner: Linux - X64
  • Status: success
  • Timestamp: 2026-08-09T21:30:03.091Z

Automated integration testing with Redis containers using TUnit

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Redis Container Benchmark Results (net10.0)

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host] : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3

Runtime=.NET 8.0 IterationCount=3 WarmupCount=1

Method Mean Error StdDev Min Max Median P95 Ratio RatioSD Allocated Alloc Ratio
Respire_Ping 187.0 μs 24.11 μs 1.32 μs 185.9 μs 188.4 μs 186.6 μs 188.3 μs 1.00 0.01 184 B 1.00
StackExchange_Ping 188.9 μs 43.73 μs 2.40 μs 186.1 μs 190.5 μs 190.0 μs 190.4 μs 1.01 0.01 408 B 2.22
Respire_Set_Small 189.4 μs 7.98 μs 0.44 μs 189.1 μs 189.9 μs 189.2 μs 189.8 μs 1.01 0.01 176 B 0.96
StackExchange_Set_Small 190.7 μs 13.88 μs 0.76 μs 189.9 μs 191.4 μs 190.8 μs 191.3 μs 1.02 0.01 416 B 2.26
Respire_Set_Medium 191.9 μs 14.64 μs 0.80 μs 191.0 μs 192.4 μs 192.3 μs 192.4 μs 1.03 0.01 176 B 0.96
StackExchange_Set_Medium 193.0 μs 28.41 μs 1.56 μs 191.9 μs 194.7 μs 192.2 μs 194.5 μs 1.03 0.01 416 B 2.26
Respire_Set_Large 198.5 μs 12.89 μs 0.71 μs 197.7 μs 199.0 μs 198.9 μs 199.0 μs 1.06 0.01 176 B 0.96
StackExchange_Set_Large 202.3 μs 16.40 μs 0.90 μs 201.3 μs 203.0 μs 202.6 μs 203.0 μs 1.08 0.01 416 B 2.26
Respire_Get 186.7 μs 62.25 μs 3.41 μs 182.8 μs 188.8 μs 188.6 μs 188.8 μs 1.00 0.02 224 B 1.22
StackExchange_Get 189.8 μs 38.18 μs 2.09 μs 187.6 μs 191.7 μs 190.2 μs 191.5 μs 1.02 0.01 424 B 2.30
StackExchange_MGet 192.5 μs 24.21 μs 1.33 μs 191.4 μs 193.9 μs 192.1 μs 193.7 μs 1.03 0.01 656 B 3.57
StackExchange_Pipeline_5_Sets 217.9 μs 88.48 μs 4.85 μs 215.0 μs 223.5 μs 215.2 μs 222.6 μs 1.17 0.02 2888 B 15.70
Respire_Exists 184.5 μs 38.51 μs 2.11 μs 182.0 μs 186.0 μs 185.3 μs 185.9 μs 0.99 0.01 176 B 0.96
StackExchange_Exists 189.4 μs 29.60 μs 1.62 μs 188.4 μs 191.2 μs 188.4 μs 191.0 μs 1.01 0.01 400 B 2.17
Respire_Del 359.7 μs 40.11 μs 2.20 μs 357.2 μs 361.3 μs 360.6 μs 361.2 μs 1.92 0.02 200 B 1.09
StackExchange_Del 367.4 μs 1.88 μs 0.10 μs 367.3 μs 367.5 μs 367.4 μs 367.5 μs 1.96 0.01 648 B 3.52
Respire_Incr 187.4 μs 16.50 μs 0.90 μs 186.4 μs 188.2 μs 187.6 μs 188.1 μs 1.00 0.01 183 B 0.99
StackExchange_Incr 188.8 μs 44.02 μs 2.41 μs 186.2 μs 191.0 μs 189.1 μs 190.8 μs 1.01 0.01 400 B 2.17
Respire_Transaction_2 195.2 μs 24.57 μs 1.35 μs 193.7 μs 196.3 μs 195.7 μs 196.2 μs 1.04 0.01 1064 B 5.78
View raw benchmark output

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.45GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host]     : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3

Runtime=.NET 8.0  IterationCount=3  WarmupCount=1  

Method                        | Mean     | Error    | StdDev  | Min      | Max      | Median   | P95      | Ratio | RatioSD | Allocated | Alloc Ratio |
------------------------------ |---------:|---------:|--------:|---------:|---------:|---------:|---------:|------:|--------:|----------:|------------:|
Respire_Ping                  | 187.0 μs | 24.11 μs | 1.32 μs | 185.9 μs | 188.4 μs | 186.6 μs | 188.3 μs |  1.00 |    0.01 |     184 B |        1.00 |
StackExchange_Ping            | 188.9 μs | 43.73 μs | 2.40 μs | 186.1 μs | 190.5 μs | 190.0 μs | 190.4 μs |  1.01 |    0.01 |     408 B |        2.22 |
Respire_Set_Small             | 189.4 μs |  7.98 μs | 0.44 μs | 189.1 μs | 189.9 μs | 189.2 μs | 189.8 μs |  1.01 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Small       | 190.7 μs | 13.88 μs | 0.76 μs | 189.9 μs | 191.4 μs | 190.8 μs | 191.3 μs |  1.02 |    0.01 |     416 B |        2.26 |
Respire_Set_Medium            | 191.9 μs | 14.64 μs | 0.80 μs | 191.0 μs | 192.4 μs | 192.3 μs | 192.4 μs |  1.03 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Medium      | 193.0 μs | 28.41 μs | 1.56 μs | 191.9 μs | 194.7 μs | 192.2 μs | 194.5 μs |  1.03 |    0.01 |     416 B |        2.26 |
Respire_Set_Large             | 198.5 μs | 12.89 μs | 0.71 μs | 197.7 μs | 199.0 μs | 198.9 μs | 199.0 μs |  1.06 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Large       | 202.3 μs | 16.40 μs | 0.90 μs | 201.3 μs | 203.0 μs | 202.6 μs | 203.0 μs |  1.08 |    0.01 |     416 B |        2.26 |
Respire_Get                   | 186.7 μs | 62.25 μs | 3.41 μs | 182.8 μs | 188.8 μs | 188.6 μs | 188.8 μs |  1.00 |    0.02 |     224 B |        1.22 |
StackExchange_Get             | 189.8 μs | 38.18 μs | 2.09 μs | 187.6 μs | 191.7 μs | 190.2 μs | 191.5 μs |  1.02 |    0.01 |     424 B |        2.30 |
StackExchange_MGet            | 192.5 μs | 24.21 μs | 1.33 μs | 191.4 μs | 193.9 μs | 192.1 μs | 193.7 μs |  1.03 |    0.01 |     656 B |        3.57 |
StackExchange_Pipeline_5_Sets | 217.9 μs | 88.48 μs | 4.85 μs | 215.0 μs | 223.5 μs | 215.2 μs | 222.6 μs |  1.17 |    0.02 |    2888 B |       15.70 |
Respire_Exists                | 184.5 μs | 38.51 μs | 2.11 μs | 182.0 μs | 186.0 μs | 185.3 μs | 185.9 μs |  0.99 |    0.01 |     176 B |        0.96 |
StackExchange_Exists          | 189.4 μs | 29.60 μs | 1.62 μs | 188.4 μs | 191.2 μs | 188.4 μs | 191.0 μs |  1.01 |    0.01 |     400 B |        2.17 |
Respire_Del                   | 359.7 μs | 40.11 μs | 2.20 μs | 357.2 μs | 361.3 μs | 360.6 μs | 361.2 μs |  1.92 |    0.02 |     200 B |        1.09 |
StackExchange_Del             | 367.4 μs |  1.88 μs | 0.10 μs | 367.3 μs | 367.5 μs | 367.4 μs | 367.5 μs |  1.96 |    0.01 |     648 B |        3.52 |
Respire_Incr                  | 187.4 μs | 16.50 μs | 0.90 μs | 186.4 μs | 188.2 μs | 187.6 μs | 188.1 μs |  1.00 |    0.01 |     183 B |        0.99 |
StackExchange_Incr            | 188.8 μs | 44.02 μs | 2.41 μs | 186.2 μs | 191.0 μs | 189.1 μs | 190.8 μs |  1.01 |    0.01 |     400 B |        2.17 |
Respire_Transaction_2         | 195.2 μs | 24.57 μs | 1.35 μs | 193.7 μs | 196.3 μs | 195.7 μs | 196.2 μs |  1.04 |    0.01 |    1064 B |        5.78 |

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Redis Container Benchmark Results (net8.0)

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
INTEL XEON PLATINUM 8573C 2.98GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host] : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v4
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v4

Runtime=.NET 8.0 IterationCount=3 WarmupCount=1

Method Mean Error StdDev Min Max Median P95 Ratio RatioSD Allocated Alloc Ratio
Respire_Ping 98.11 μs 9.827 μs 0.539 μs 97.62 μs 98.69 μs 98.02 μs 98.62 μs 1.00 0.01 183 B 1.00
StackExchange_Ping 101.64 μs 1.490 μs 0.082 μs 101.56 μs 101.72 μs 101.66 μs 101.71 μs 1.04 0.00 408 B 2.23
Respire_Set_Small 101.91 μs 27.999 μs 1.535 μs 100.65 μs 103.62 μs 101.47 μs 103.40 μs 1.04 0.01 176 B 0.96
StackExchange_Set_Small 105.61 μs 10.132 μs 0.555 μs 104.99 μs 106.07 μs 105.76 μs 106.04 μs 1.08 0.01 416 B 2.27
Respire_Set_Medium 102.40 μs 22.121 μs 1.213 μs 101.24 μs 103.66 μs 102.31 μs 103.52 μs 1.04 0.01 176 B 0.96
StackExchange_Set_Medium 104.89 μs 1.928 μs 0.106 μs 104.78 μs 104.98 μs 104.92 μs 104.98 μs 1.07 0.01 416 B 2.27
Respire_Set_Large 108.99 μs 19.708 μs 1.080 μs 107.79 μs 109.89 μs 109.30 μs 109.83 μs 1.11 0.01 176 B 0.96
StackExchange_Set_Large 113.89 μs 114.500 μs 6.276 μs 107.16 μs 119.58 μs 114.93 μs 119.11 μs 1.16 0.06 416 B 2.27
Respire_Get 99.30 μs 3.639 μs 0.199 μs 99.13 μs 99.52 μs 99.25 μs 99.49 μs 1.01 0.01 224 B 1.22
StackExchange_Get 97.50 μs 31.412 μs 1.722 μs 95.74 μs 99.19 μs 97.56 μs 99.02 μs 0.99 0.02 424 B 2.32
StackExchange_MGet 105.46 μs 17.435 μs 0.956 μs 104.89 μs 106.56 μs 104.92 μs 106.40 μs 1.07 0.01 655 B 3.58
StackExchange_Pipeline_5_Sets 115.91 μs 53.748 μs 2.946 μs 112.90 μs 118.78 μs 116.05 μs 118.51 μs 1.18 0.03 2888 B 15.78
Respire_Exists 99.16 μs 18.082 μs 0.991 μs 98.04 μs 99.93 μs 99.52 μs 99.89 μs 1.01 0.01 176 B 0.96
StackExchange_Exists 99.87 μs 25.353 μs 1.390 μs 98.27 μs 100.76 μs 100.57 μs 100.74 μs 1.02 0.01 400 B 2.19
Respire_Del 189.64 μs 47.549 μs 2.606 μs 187.60 μs 192.58 μs 188.75 μs 192.20 μs 1.93 0.02 199 B 1.09
StackExchange_Del 193.54 μs 37.362 μs 2.048 μs 191.23 μs 195.14 μs 194.25 μs 195.05 μs 1.97 0.02 648 B 3.54
Respire_Incr 99.42 μs 4.708 μs 0.258 μs 99.16 μs 99.67 μs 99.42 μs 99.65 μs 1.01 0.01 184 B 1.01
StackExchange_Incr 101.46 μs 13.722 μs 0.752 μs 100.60 μs 101.93 μs 101.87 μs 101.92 μs 1.03 0.01 400 B 2.19
Respire_Transaction_2 106.00 μs 6.409 μs 0.351 μs 105.72 μs 106.40 μs 105.88 μs 106.34 μs 1.08 0.01 1063 B 5.81
View raw benchmark output

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
INTEL XEON PLATINUM 8573C 2.98GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host]     : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v4
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v4

Runtime=.NET 8.0  IterationCount=3  WarmupCount=1  

Method                        | Mean      | Error      | StdDev   | Min       | Max       | Median    | P95       | Ratio | RatioSD | Allocated | Alloc Ratio |
------------------------------ |----------:|-----------:|---------:|----------:|----------:|----------:|----------:|------:|--------:|----------:|------------:|
Respire_Ping                  |  98.11 μs |   9.827 μs | 0.539 μs |  97.62 μs |  98.69 μs |  98.02 μs |  98.62 μs |  1.00 |    0.01 |     183 B |        1.00 |
StackExchange_Ping            | 101.64 μs |   1.490 μs | 0.082 μs | 101.56 μs | 101.72 μs | 101.66 μs | 101.71 μs |  1.04 |    0.00 |     408 B |        2.23 |
Respire_Set_Small             | 101.91 μs |  27.999 μs | 1.535 μs | 100.65 μs | 103.62 μs | 101.47 μs | 103.40 μs |  1.04 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Small       | 105.61 μs |  10.132 μs | 0.555 μs | 104.99 μs | 106.07 μs | 105.76 μs | 106.04 μs |  1.08 |    0.01 |     416 B |        2.27 |
Respire_Set_Medium            | 102.40 μs |  22.121 μs | 1.213 μs | 101.24 μs | 103.66 μs | 102.31 μs | 103.52 μs |  1.04 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Medium      | 104.89 μs |   1.928 μs | 0.106 μs | 104.78 μs | 104.98 μs | 104.92 μs | 104.98 μs |  1.07 |    0.01 |     416 B |        2.27 |
Respire_Set_Large             | 108.99 μs |  19.708 μs | 1.080 μs | 107.79 μs | 109.89 μs | 109.30 μs | 109.83 μs |  1.11 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Large       | 113.89 μs | 114.500 μs | 6.276 μs | 107.16 μs | 119.58 μs | 114.93 μs | 119.11 μs |  1.16 |    0.06 |     416 B |        2.27 |
Respire_Get                   |  99.30 μs |   3.639 μs | 0.199 μs |  99.13 μs |  99.52 μs |  99.25 μs |  99.49 μs |  1.01 |    0.01 |     224 B |        1.22 |
StackExchange_Get             |  97.50 μs |  31.412 μs | 1.722 μs |  95.74 μs |  99.19 μs |  97.56 μs |  99.02 μs |  0.99 |    0.02 |     424 B |        2.32 |
StackExchange_MGet            | 105.46 μs |  17.435 μs | 0.956 μs | 104.89 μs | 106.56 μs | 104.92 μs | 106.40 μs |  1.07 |    0.01 |     655 B |        3.58 |
StackExchange_Pipeline_5_Sets | 115.91 μs |  53.748 μs | 2.946 μs | 112.90 μs | 118.78 μs | 116.05 μs | 118.51 μs |  1.18 |    0.03 |    2888 B |       15.78 |
Respire_Exists                |  99.16 μs |  18.082 μs | 0.991 μs |  98.04 μs |  99.93 μs |  99.52 μs |  99.89 μs |  1.01 |    0.01 |     176 B |        0.96 |
StackExchange_Exists          |  99.87 μs |  25.353 μs | 1.390 μs |  98.27 μs | 100.76 μs | 100.57 μs | 100.74 μs |  1.02 |    0.01 |     400 B |        2.19 |
Respire_Del                   | 189.64 μs |  47.549 μs | 2.606 μs | 187.60 μs | 192.58 μs | 188.75 μs | 192.20 μs |  1.93 |    0.02 |     199 B |        1.09 |
StackExchange_Del             | 193.54 μs |  37.362 μs | 2.048 μs | 191.23 μs | 195.14 μs | 194.25 μs | 195.05 μs |  1.97 |    0.02 |     648 B |        3.54 |
Respire_Incr                  |  99.42 μs |   4.708 μs | 0.258 μs |  99.16 μs |  99.67 μs |  99.42 μs |  99.65 μs |  1.01 |    0.01 |     184 B |        1.01 |
StackExchange_Incr            | 101.46 μs |  13.722 μs | 0.752 μs | 100.60 μs | 101.93 μs | 101.87 μs | 101.92 μs |  1.03 |    0.01 |     400 B |        2.19 |
Respire_Transaction_2         | 106.00 μs |   6.409 μs | 0.351 μs | 105.72 μs | 106.40 μs | 105.88 μs | 106.34 μs |  1.08 |    0.01 |    1063 B |        5.81 |

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4203a9778f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Respire/RespireBatch.cs Outdated
var clusterTasks = new Task<Exception?>[_ops.Count];
for (var i = 0; i < _ops.Count; i++)
{
clusterTasks[i] = _ops[i].RunClusterAsync(_client, cancellationToken);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve same-slot batch ordering

When cluster mode uses Connections > 1, launching every operation independently lets two commands for the same slot be round-robined onto different connections to the same master. Redis can then process a queued GetStringAsync(key) before the preceding SetAsync(key), returning stale data even though the batch documentation only permits reordering across different slots. Commands sharing a slot need to retain queue order, for example by grouping them onto one connection.

Useful? React with 👍 / 👎.

Comment thread src/Respire/RespireClient.cs Outdated
Comment on lines +164 to +165
if (_core.Cluster is { } cluster
&& DynamicCommandRouting.IsClusterWideFunctionMutation(command.Name, args))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Fan out raw script-cache mutations

When callers use the advertised catalog API for RespireCommands.Scripting.SCRIPT_LOAD (or raw SCRIPT LOAD/SCRIPT FLUSH) in cluster mode, this condition is false and the unkeyed command reaches only one master. A subsequent EVALSHA on another master can therefore return NOSCRIPT, and SCRIPT FLUSH leaves inconsistent caches. Fresh evidence beyond the fixed IScriptCommands.LoadAsync path is that the raw and catalog branches gate cluster fan-out solely on IsClusterWideFunctionMutation; script-cache mutations need the same treatment.

Useful? React with 👍 / 👎.

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

🧪 Integration Test Results (net10.0)

ℹ️ Execution Details

  • Framework: net10.0
  • Test Runner: TUnit
  • Runner: Linux - X64
  • Status: success
  • Timestamp: 2026-08-09T21:42:15.397Z

Automated integration testing with Redis containers using TUnit

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

🧪 Integration Test Results (net8.0)

ℹ️ Execution Details

  • Framework: net8.0
  • Test Runner: TUnit
  • Runner: Linux - X64
  • Status: success
  • Timestamp: 2026-08-09T21:42:44.226Z

Automated integration testing with Redis containers using TUnit

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Redis Container Benchmark Results (net10.0)

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 9V74 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host] : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3

Runtime=.NET 8.0 IterationCount=3 WarmupCount=1

Method Mean Error StdDev Min Max Median P95 Ratio RatioSD Allocated Alloc Ratio
Respire_Ping 114.59 μs 108.40 μs 5.942 μs 110.94 μs 121.45 μs 111.38 μs 120.44 μs 1.00 0.06 163 B 1.00
StackExchange_Ping 108.41 μs 195.58 μs 10.720 μs 97.46 μs 118.89 μs 108.89 μs 117.89 μs 0.95 0.09 381 B 2.34
Respire_Set_Small 95.71 μs 86.73 μs 4.754 μs 90.23 μs 98.55 μs 98.37 μs 98.53 μs 0.84 0.05 151 B 0.93
StackExchange_Set_Small 121.32 μs 86.45 μs 4.738 μs 117.36 μs 126.57 μs 120.03 μs 125.92 μs 1.06 0.06 400 B 2.45
Respire_Set_Medium 117.25 μs 79.26 μs 4.345 μs 112.47 μs 120.95 μs 118.32 μs 120.69 μs 1.02 0.06 159 B 0.98
StackExchange_Set_Medium 123.77 μs 281.94 μs 15.454 μs 107.59 μs 138.38 μs 125.34 μs 137.08 μs 1.08 0.13 403 B 2.47
Respire_Set_Large 135.05 μs 39.35 μs 2.157 μs 132.78 μs 137.08 μs 135.29 μs 136.90 μs 1.18 0.05 159 B 0.98
StackExchange_Set_Large 137.30 μs 113.51 μs 6.222 μs 130.39 μs 142.47 μs 139.03 μs 142.13 μs 1.20 0.07 401 B 2.46
Respire_Get 112.47 μs 114.46 μs 6.274 μs 105.22 μs 116.13 μs 116.05 μs 116.12 μs 0.98 0.06 207 B 1.27
StackExchange_Get 121.04 μs 155.13 μs 8.503 μs 115.51 μs 130.84 μs 116.79 μs 129.43 μs 1.06 0.08 412 B 2.53
StackExchange_MGet 125.49 μs 15.30 μs 0.839 μs 124.52 μs 125.97 μs 125.97 μs 125.97 μs 1.10 0.05 643 B 3.94
StackExchange_Pipeline_5_Sets 160.99 μs 25.58 μs 1.402 μs 159.75 μs 162.51 μs 160.72 μs 162.33 μs 1.41 0.06 2887 B 17.71
Respire_Exists 107.58 μs 221.66 μs 12.150 μs 94.21 μs 117.95 μs 110.57 μs 117.21 μs 0.94 0.10 161 B 0.99
StackExchange_Exists 120.80 μs 22.81 μs 1.250 μs 119.35 μs 121.57 μs 121.46 μs 121.56 μs 1.06 0.05 394 B 2.42
Respire_Del 230.23 μs 307.94 μs 16.879 μs 210.85 μs 241.69 μs 238.16 μs 241.33 μs 2.01 0.16 199 B 1.22
StackExchange_Del 233.05 μs 314.88 μs 17.260 μs 218.01 μs 251.89 μs 229.25 μs 249.63 μs 2.04 0.16 643 B 3.94
Respire_Incr 107.35 μs 49.63 μs 2.721 μs 104.25 μs 109.36 μs 108.44 μs 109.27 μs 0.94 0.05 173 B 1.06
StackExchange_Incr 110.66 μs 166.76 μs 9.141 μs 100.54 μs 118.33 μs 113.10 μs 117.81 μs 0.97 0.08 390 B 2.39
Respire_Transaction_2 129.24 μs 73.37 μs 4.021 μs 126.29 μs 133.82 μs 127.60 μs 133.20 μs 1.13 0.06 1058 B 6.49
View raw benchmark output

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 9V74 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host]     : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3

Runtime=.NET 8.0  IterationCount=3  WarmupCount=1  

Method                        | Mean      | Error     | StdDev    | Min       | Max       | Median    | P95       | Ratio | RatioSD | Allocated | Alloc Ratio |
------------------------------ |----------:|----------:|----------:|----------:|----------:|----------:|----------:|------:|--------:|----------:|------------:|
Respire_Ping                  | 114.59 μs | 108.40 μs |  5.942 μs | 110.94 μs | 121.45 μs | 111.38 μs | 120.44 μs |  1.00 |    0.06 |     163 B |        1.00 |
StackExchange_Ping            | 108.41 μs | 195.58 μs | 10.720 μs |  97.46 μs | 118.89 μs | 108.89 μs | 117.89 μs |  0.95 |    0.09 |     381 B |        2.34 |
Respire_Set_Small             |  95.71 μs |  86.73 μs |  4.754 μs |  90.23 μs |  98.55 μs |  98.37 μs |  98.53 μs |  0.84 |    0.05 |     151 B |        0.93 |
StackExchange_Set_Small       | 121.32 μs |  86.45 μs |  4.738 μs | 117.36 μs | 126.57 μs | 120.03 μs | 125.92 μs |  1.06 |    0.06 |     400 B |        2.45 |
Respire_Set_Medium            | 117.25 μs |  79.26 μs |  4.345 μs | 112.47 μs | 120.95 μs | 118.32 μs | 120.69 μs |  1.02 |    0.06 |     159 B |        0.98 |
StackExchange_Set_Medium      | 123.77 μs | 281.94 μs | 15.454 μs | 107.59 μs | 138.38 μs | 125.34 μs | 137.08 μs |  1.08 |    0.13 |     403 B |        2.47 |
Respire_Set_Large             | 135.05 μs |  39.35 μs |  2.157 μs | 132.78 μs | 137.08 μs | 135.29 μs | 136.90 μs |  1.18 |    0.05 |     159 B |        0.98 |
StackExchange_Set_Large       | 137.30 μs | 113.51 μs |  6.222 μs | 130.39 μs | 142.47 μs | 139.03 μs | 142.13 μs |  1.20 |    0.07 |     401 B |        2.46 |
Respire_Get                   | 112.47 μs | 114.46 μs |  6.274 μs | 105.22 μs | 116.13 μs | 116.05 μs | 116.12 μs |  0.98 |    0.06 |     207 B |        1.27 |
StackExchange_Get             | 121.04 μs | 155.13 μs |  8.503 μs | 115.51 μs | 130.84 μs | 116.79 μs | 129.43 μs |  1.06 |    0.08 |     412 B |        2.53 |
StackExchange_MGet            | 125.49 μs |  15.30 μs |  0.839 μs | 124.52 μs | 125.97 μs | 125.97 μs | 125.97 μs |  1.10 |    0.05 |     643 B |        3.94 |
StackExchange_Pipeline_5_Sets | 160.99 μs |  25.58 μs |  1.402 μs | 159.75 μs | 162.51 μs | 160.72 μs | 162.33 μs |  1.41 |    0.06 |    2887 B |       17.71 |
Respire_Exists                | 107.58 μs | 221.66 μs | 12.150 μs |  94.21 μs | 117.95 μs | 110.57 μs | 117.21 μs |  0.94 |    0.10 |     161 B |        0.99 |
StackExchange_Exists          | 120.80 μs |  22.81 μs |  1.250 μs | 119.35 μs | 121.57 μs | 121.46 μs | 121.56 μs |  1.06 |    0.05 |     394 B |        2.42 |
Respire_Del                   | 230.23 μs | 307.94 μs | 16.879 μs | 210.85 μs | 241.69 μs | 238.16 μs | 241.33 μs |  2.01 |    0.16 |     199 B |        1.22 |
StackExchange_Del             | 233.05 μs | 314.88 μs | 17.260 μs | 218.01 μs | 251.89 μs | 229.25 μs | 249.63 μs |  2.04 |    0.16 |     643 B |        3.94 |
Respire_Incr                  | 107.35 μs |  49.63 μs |  2.721 μs | 104.25 μs | 109.36 μs | 108.44 μs | 109.27 μs |  0.94 |    0.05 |     173 B |        1.06 |
StackExchange_Incr            | 110.66 μs | 166.76 μs |  9.141 μs | 100.54 μs | 118.33 μs | 113.10 μs | 117.81 μs |  0.97 |    0.08 |     390 B |        2.39 |
Respire_Transaction_2         | 129.24 μs |  73.37 μs |  4.021 μs | 126.29 μs | 133.82 μs | 127.60 μs | 133.20 μs |  1.13 |    0.06 |    1058 B |        6.49 |

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Redis Container Benchmark Results (net8.0)

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 9V74 3.69GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host] : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v4
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v4

Runtime=.NET 8.0 IterationCount=3 WarmupCount=1

Method Mean Error StdDev Min Max Median P95 Ratio RatioSD Allocated Alloc Ratio
Respire_Ping 82.37 μs 63.84 μs 3.499 μs 79.91 μs 86.37 μs 80.82 μs 85.82 μs 1.00 0.05 163 B 1.00
StackExchange_Ping 86.33 μs 24.02 μs 1.317 μs 85.39 μs 87.83 μs 85.76 μs 87.63 μs 1.05 0.04 391 B 2.40
Respire_Set_Small 78.44 μs 96.22 μs 5.274 μs 74.08 μs 84.30 μs 76.94 μs 83.57 μs 0.95 0.07 157 B 0.96
StackExchange_Set_Small 77.37 μs 37.74 μs 2.069 μs 75.63 μs 79.66 μs 76.82 μs 79.37 μs 0.94 0.04 394 B 2.42
Respire_Set_Medium 80.40 μs 118.35 μs 6.487 μs 74.80 μs 87.51 μs 78.88 μs 86.64 μs 0.98 0.08 159 B 0.98
StackExchange_Set_Medium 80.48 μs 96.20 μs 5.273 μs 74.47 μs 84.36 μs 82.60 μs 84.18 μs 0.98 0.07 405 B 2.48
Respire_Set_Large 107.33 μs 120.64 μs 6.613 μs 100.85 μs 114.07 μs 107.08 μs 113.37 μs 1.30 0.08 168 B 1.03
StackExchange_Set_Large 113.77 μs 95.28 μs 5.222 μs 108.73 μs 119.15 μs 113.41 μs 118.58 μs 1.38 0.07 409 B 2.51
Respire_Get 79.25 μs 49.11 μs 2.692 μs 77.27 μs 82.32 μs 78.18 μs 81.90 μs 0.96 0.04 205 B 1.26
StackExchange_Get 79.02 μs 141.96 μs 7.781 μs 70.42 μs 85.58 μs 81.05 μs 85.13 μs 0.96 0.09 396 B 2.43
StackExchange_MGet 89.83 μs 202.37 μs 11.092 μs 77.05 μs 96.86 μs 95.60 μs 96.73 μs 1.09 0.12 642 B 3.94
StackExchange_Pipeline_5_Sets 125.65 μs 103.44 μs 5.670 μs 119.49 μs 130.65 μs 126.81 μs 130.27 μs 1.53 0.08 2887 B 17.71
Respire_Exists 80.31 μs 46.24 μs 2.534 μs 78.45 μs 83.20 μs 79.27 μs 82.80 μs 0.98 0.04 156 B 0.96
StackExchange_Exists 84.95 μs 10.33 μs 0.566 μs 84.30 μs 85.32 μs 85.23 μs 85.31 μs 1.03 0.04 382 B 2.34
Respire_Del 174.89 μs 151.47 μs 8.303 μs 168.82 μs 184.35 μs 171.49 μs 183.06 μs 2.13 0.12 194 B 1.19
StackExchange_Del 179.16 μs 78.05 μs 4.278 μs 174.27 μs 182.25 μs 180.95 μs 182.12 μs 2.18 0.09 642 B 3.94
Respire_Incr 73.67 μs 203.61 μs 11.161 μs 66.03 μs 86.47 μs 68.49 μs 84.68 μs 0.90 0.12 161 B 0.99
StackExchange_Incr 82.72 μs 185.85 μs 10.187 μs 76.27 μs 94.47 μs 77.43 μs 92.76 μs 1.01 0.11 383 B 2.35
Respire_Transaction_2 89.67 μs 63.60 μs 3.486 μs 87.04 μs 93.63 μs 88.36 μs 93.10 μs 1.09 0.05 1055 B 6.47
View raw benchmark output

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 9V74 3.69GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host]     : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v4
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v4

Runtime=.NET 8.0  IterationCount=3  WarmupCount=1  

Method                        | Mean      | Error     | StdDev    | Min       | Max       | Median    | P95       | Ratio | RatioSD | Allocated | Alloc Ratio |
------------------------------ |----------:|----------:|----------:|----------:|----------:|----------:|----------:|------:|--------:|----------:|------------:|
Respire_Ping                  |  82.37 μs |  63.84 μs |  3.499 μs |  79.91 μs |  86.37 μs |  80.82 μs |  85.82 μs |  1.00 |    0.05 |     163 B |        1.00 |
StackExchange_Ping            |  86.33 μs |  24.02 μs |  1.317 μs |  85.39 μs |  87.83 μs |  85.76 μs |  87.63 μs |  1.05 |    0.04 |     391 B |        2.40 |
Respire_Set_Small             |  78.44 μs |  96.22 μs |  5.274 μs |  74.08 μs |  84.30 μs |  76.94 μs |  83.57 μs |  0.95 |    0.07 |     157 B |        0.96 |
StackExchange_Set_Small       |  77.37 μs |  37.74 μs |  2.069 μs |  75.63 μs |  79.66 μs |  76.82 μs |  79.37 μs |  0.94 |    0.04 |     394 B |        2.42 |
Respire_Set_Medium            |  80.40 μs | 118.35 μs |  6.487 μs |  74.80 μs |  87.51 μs |  78.88 μs |  86.64 μs |  0.98 |    0.08 |     159 B |        0.98 |
StackExchange_Set_Medium      |  80.48 μs |  96.20 μs |  5.273 μs |  74.47 μs |  84.36 μs |  82.60 μs |  84.18 μs |  0.98 |    0.07 |     405 B |        2.48 |
Respire_Set_Large             | 107.33 μs | 120.64 μs |  6.613 μs | 100.85 μs | 114.07 μs | 107.08 μs | 113.37 μs |  1.30 |    0.08 |     168 B |        1.03 |
StackExchange_Set_Large       | 113.77 μs |  95.28 μs |  5.222 μs | 108.73 μs | 119.15 μs | 113.41 μs | 118.58 μs |  1.38 |    0.07 |     409 B |        2.51 |
Respire_Get                   |  79.25 μs |  49.11 μs |  2.692 μs |  77.27 μs |  82.32 μs |  78.18 μs |  81.90 μs |  0.96 |    0.04 |     205 B |        1.26 |
StackExchange_Get             |  79.02 μs | 141.96 μs |  7.781 μs |  70.42 μs |  85.58 μs |  81.05 μs |  85.13 μs |  0.96 |    0.09 |     396 B |        2.43 |
StackExchange_MGet            |  89.83 μs | 202.37 μs | 11.092 μs |  77.05 μs |  96.86 μs |  95.60 μs |  96.73 μs |  1.09 |    0.12 |     642 B |        3.94 |
StackExchange_Pipeline_5_Sets | 125.65 μs | 103.44 μs |  5.670 μs | 119.49 μs | 130.65 μs | 126.81 μs | 130.27 μs |  1.53 |    0.08 |    2887 B |       17.71 |
Respire_Exists                |  80.31 μs |  46.24 μs |  2.534 μs |  78.45 μs |  83.20 μs |  79.27 μs |  82.80 μs |  0.98 |    0.04 |     156 B |        0.96 |
StackExchange_Exists          |  84.95 μs |  10.33 μs |  0.566 μs |  84.30 μs |  85.32 μs |  85.23 μs |  85.31 μs |  1.03 |    0.04 |     382 B |        2.34 |
Respire_Del                   | 174.89 μs | 151.47 μs |  8.303 μs | 168.82 μs | 184.35 μs | 171.49 μs | 183.06 μs |  2.13 |    0.12 |     194 B |        1.19 |
StackExchange_Del             | 179.16 μs |  78.05 μs |  4.278 μs | 174.27 μs | 182.25 μs | 180.95 μs | 182.12 μs |  2.18 |    0.09 |     642 B |        3.94 |
Respire_Incr                  |  73.67 μs | 203.61 μs | 11.161 μs |  66.03 μs |  86.47 μs |  68.49 μs |  84.68 μs |  0.90 |    0.12 |     161 B |        0.99 |
StackExchange_Incr            |  82.72 μs | 185.85 μs | 10.187 μs |  76.27 μs |  94.47 μs |  77.43 μs |  92.76 μs |  1.01 |    0.11 |     383 B |        2.35 |
Respire_Transaction_2         |  89.67 μs |  63.60 μs |  3.486 μs |  87.04 μs |  93.63 μs |  88.36 μs |  93.10 μs |  1.09 |    0.05 |    1055 B |        6.47 |

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

🧪 Integration Test Results (net10.0)

ℹ️ Execution Details

  • Framework: net10.0
  • Test Runner: TUnit
  • Runner: Linux - X64
  • Status: success
  • Timestamp: 2026-08-09T21:49:19.687Z

Automated integration testing with Redis containers using TUnit

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

🧪 Integration Test Results (net8.0)

ℹ️ Execution Details

  • Framework: net8.0
  • Test Runner: TUnit
  • Runner: Linux - X64
  • Status: success
  • Timestamp: 2026-08-09T21:49:38.032Z

Automated integration testing with Redis containers using TUnit

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Redis Container Benchmark Results (net8.0)

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 9V74 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host] : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3

Runtime=.NET 8.0 IterationCount=3 WarmupCount=1

Method Mean Error StdDev Min Max Median P95 Ratio RatioSD Allocated Alloc Ratio
Respire_Ping 105.29 μs 6.459 μs 0.354 μs 104.89 μs 105.6 μs 105.43 μs 105.5 μs 1.00 0.00 166 B 1.00
StackExchange_Ping 99.60 μs 92.421 μs 5.066 μs 94.01 μs 103.9 μs 100.89 μs 103.6 μs 0.95 0.04 385 B 2.32
Respire_Set_Small 105.68 μs 225.854 μs 12.380 μs 91.74 μs 115.4 μs 109.87 μs 114.9 μs 1.00 0.10 138 B 0.83
StackExchange_Set_Small 109.35 μs 196.349 μs 10.763 μs 98.85 μs 120.4 μs 108.83 μs 119.2 μs 1.04 0.09 394 B 2.37
Respire_Set_Medium 118.61 μs 187.384 μs 10.271 μs 109.00 μs 129.4 μs 117.39 μs 128.2 μs 1.13 0.08 162 B 0.98
StackExchange_Set_Medium 116.63 μs 115.330 μs 6.322 μs 109.91 μs 122.5 μs 117.52 μs 122.0 μs 1.11 0.05 387 B 2.33
Respire_Set_Large 113.90 μs 148.892 μs 8.161 μs 104.88 μs 120.8 μs 116.06 μs 120.3 μs 1.08 0.07 162 B 0.98
StackExchange_Set_Large 129.66 μs 127.783 μs 7.004 μs 122.28 μs 136.2 μs 130.49 μs 135.6 μs 1.23 0.06 406 B 2.45
Respire_Get 117.18 μs 84.126 μs 4.611 μs 113.72 μs 122.4 μs 115.40 μs 121.7 μs 1.11 0.04 203 B 1.22
StackExchange_Get 119.41 μs 140.334 μs 7.692 μs 112.87 μs 127.9 μs 117.49 μs 126.8 μs 1.13 0.06 406 B 2.45
StackExchange_MGet 113.77 μs 116.682 μs 6.396 μs 107.15 μs 119.9 μs 114.25 μs 119.3 μs 1.08 0.05 646 B 3.89
StackExchange_Pipeline_5_Sets 151.81 μs 51.709 μs 2.834 μs 148.76 μs 154.4 μs 152.31 μs 154.2 μs 1.44 0.02 2887 B 17.39
Respire_Exists 96.65 μs 177.523 μs 9.731 μs 87.97 μs 107.2 μs 94.81 μs 105.9 μs 0.92 0.08 158 B 0.95
StackExchange_Exists 109.46 μs 125.024 μs 6.853 μs 101.55 μs 113.7 μs 113.10 μs 113.7 μs 1.04 0.06 388 B 2.34
Respire_Del 224.87 μs 506.943 μs 27.787 μs 204.00 μs 256.4 μs 214.19 μs 252.2 μs 2.14 0.23 197 B 1.19
StackExchange_Del 231.52 μs 695.327 μs 38.113 μs 203.83 μs 275.0 μs 215.75 μs 269.1 μs 2.20 0.31 642 B 3.87
Respire_Incr 112.76 μs 123.868 μs 6.790 μs 107.84 μs 120.5 μs 109.93 μs 119.4 μs 1.07 0.06 144 B 0.87
StackExchange_Incr 118.07 μs 289.818 μs 15.886 μs 104.83 μs 135.7 μs 113.70 μs 133.5 μs 1.12 0.13 385 B 2.32
Respire_Transaction_2 122.95 μs 100.681 μs 5.519 μs 116.61 μs 126.7 μs 125.53 μs 126.6 μs 1.17 0.05 1054 B 6.35
View raw benchmark output

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 9V74 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host]     : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3

Runtime=.NET 8.0  IterationCount=3  WarmupCount=1  

Method                        | Mean      | Error      | StdDev    | Min       | Max      | Median    | P95      | Ratio | RatioSD | Allocated | Alloc Ratio |
------------------------------ |----------:|-----------:|----------:|----------:|---------:|----------:|---------:|------:|--------:|----------:|------------:|
Respire_Ping                  | 105.29 μs |   6.459 μs |  0.354 μs | 104.89 μs | 105.6 μs | 105.43 μs | 105.5 μs |  1.00 |    0.00 |     166 B |        1.00 |
StackExchange_Ping            |  99.60 μs |  92.421 μs |  5.066 μs |  94.01 μs | 103.9 μs | 100.89 μs | 103.6 μs |  0.95 |    0.04 |     385 B |        2.32 |
Respire_Set_Small             | 105.68 μs | 225.854 μs | 12.380 μs |  91.74 μs | 115.4 μs | 109.87 μs | 114.9 μs |  1.00 |    0.10 |     138 B |        0.83 |
StackExchange_Set_Small       | 109.35 μs | 196.349 μs | 10.763 μs |  98.85 μs | 120.4 μs | 108.83 μs | 119.2 μs |  1.04 |    0.09 |     394 B |        2.37 |
Respire_Set_Medium            | 118.61 μs | 187.384 μs | 10.271 μs | 109.00 μs | 129.4 μs | 117.39 μs | 128.2 μs |  1.13 |    0.08 |     162 B |        0.98 |
StackExchange_Set_Medium      | 116.63 μs | 115.330 μs |  6.322 μs | 109.91 μs | 122.5 μs | 117.52 μs | 122.0 μs |  1.11 |    0.05 |     387 B |        2.33 |
Respire_Set_Large             | 113.90 μs | 148.892 μs |  8.161 μs | 104.88 μs | 120.8 μs | 116.06 μs | 120.3 μs |  1.08 |    0.07 |     162 B |        0.98 |
StackExchange_Set_Large       | 129.66 μs | 127.783 μs |  7.004 μs | 122.28 μs | 136.2 μs | 130.49 μs | 135.6 μs |  1.23 |    0.06 |     406 B |        2.45 |
Respire_Get                   | 117.18 μs |  84.126 μs |  4.611 μs | 113.72 μs | 122.4 μs | 115.40 μs | 121.7 μs |  1.11 |    0.04 |     203 B |        1.22 |
StackExchange_Get             | 119.41 μs | 140.334 μs |  7.692 μs | 112.87 μs | 127.9 μs | 117.49 μs | 126.8 μs |  1.13 |    0.06 |     406 B |        2.45 |
StackExchange_MGet            | 113.77 μs | 116.682 μs |  6.396 μs | 107.15 μs | 119.9 μs | 114.25 μs | 119.3 μs |  1.08 |    0.05 |     646 B |        3.89 |
StackExchange_Pipeline_5_Sets | 151.81 μs |  51.709 μs |  2.834 μs | 148.76 μs | 154.4 μs | 152.31 μs | 154.2 μs |  1.44 |    0.02 |    2887 B |       17.39 |
Respire_Exists                |  96.65 μs | 177.523 μs |  9.731 μs |  87.97 μs | 107.2 μs |  94.81 μs | 105.9 μs |  0.92 |    0.08 |     158 B |        0.95 |
StackExchange_Exists          | 109.46 μs | 125.024 μs |  6.853 μs | 101.55 μs | 113.7 μs | 113.10 μs | 113.7 μs |  1.04 |    0.06 |     388 B |        2.34 |
Respire_Del                   | 224.87 μs | 506.943 μs | 27.787 μs | 204.00 μs | 256.4 μs | 214.19 μs | 252.2 μs |  2.14 |    0.23 |     197 B |        1.19 |
StackExchange_Del             | 231.52 μs | 695.327 μs | 38.113 μs | 203.83 μs | 275.0 μs | 215.75 μs | 269.1 μs |  2.20 |    0.31 |     642 B |        3.87 |
Respire_Incr                  | 112.76 μs | 123.868 μs |  6.790 μs | 107.84 μs | 120.5 μs | 109.93 μs | 119.4 μs |  1.07 |    0.06 |     144 B |        0.87 |
StackExchange_Incr            | 118.07 μs | 289.818 μs | 15.886 μs | 104.83 μs | 135.7 μs | 113.70 μs | 133.5 μs |  1.12 |    0.13 |     385 B |        2.32 |
Respire_Transaction_2         | 122.95 μs | 100.681 μs |  5.519 μs | 116.61 μs | 126.7 μs | 125.53 μs | 126.6 μs |  1.17 |    0.05 |    1054 B |        6.35 |

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Redis Container Benchmark Results (net10.0)

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 9V74 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host] : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3

Runtime=.NET 8.0 IterationCount=3 WarmupCount=1

Method Mean Error StdDev Min Max Median P95 Ratio RatioSD Allocated Alloc Ratio
Respire_Ping 104.3 μs 32.46 μs 1.78 μs 102.77 μs 106.2 μs 103.82 μs 106.0 μs 1.00 0.02 160 B 1.00
StackExchange_Ping 105.9 μs 93.05 μs 5.10 μs 102.64 μs 111.8 μs 103.23 μs 110.9 μs 1.02 0.04 384 B 2.40
Respire_Set_Small 110.2 μs 32.36 μs 1.77 μs 108.15 μs 111.5 μs 110.84 μs 111.4 μs 1.06 0.02 159 B 0.99
StackExchange_Set_Small 107.5 μs 14.73 μs 0.81 μs 106.56 μs 108.0 μs 107.86 μs 108.0 μs 1.03 0.02 404 B 2.52
Respire_Set_Medium 117.0 μs 111.73 μs 6.12 μs 109.92 μs 120.9 μs 120.15 μs 120.8 μs 1.12 0.05 169 B 1.06
StackExchange_Set_Medium 122.0 μs 128.16 μs 7.02 μs 115.20 μs 129.2 μs 121.57 μs 128.5 μs 1.17 0.06 414 B 2.59
Respire_Set_Large 125.1 μs 100.14 μs 5.49 μs 120.35 μs 131.1 μs 123.94 μs 130.4 μs 1.20 0.05 171 B 1.07
StackExchange_Set_Large 130.6 μs 164.64 μs 9.02 μs 120.31 μs 137.2 μs 134.21 μs 136.9 μs 1.25 0.08 407 B 2.54
Respire_Get 100.1 μs 294.21 μs 16.13 μs 87.30 μs 118.2 μs 94.84 μs 115.9 μs 0.96 0.13 199 B 1.24
StackExchange_Get 119.4 μs 83.66 μs 4.59 μs 114.11 μs 122.1 μs 121.97 μs 122.1 μs 1.15 0.04 413 B 2.58
StackExchange_MGet 114.9 μs 245.64 μs 13.46 μs 103.71 μs 129.8 μs 111.06 μs 127.9 μs 1.10 0.11 637 B 3.98
StackExchange_Pipeline_5_Sets 150.8 μs 109.05 μs 5.98 μs 144.04 μs 155.3 μs 153.19 μs 155.1 μs 1.45 0.05 2887 B 18.04
Respire_Exists 108.7 μs 71.99 μs 3.95 μs 104.20 μs 111.7 μs 110.06 μs 111.5 μs 1.04 0.04 143 B 0.89
StackExchange_Exists 112.5 μs 49.02 μs 2.69 μs 109.97 μs 115.3 μs 112.18 μs 115.0 μs 1.08 0.03 369 B 2.31
Respire_Del 211.6 μs 346.96 μs 19.02 μs 192.39 μs 230.4 μs 212.12 μs 228.6 μs 2.03 0.16 195 B 1.22
StackExchange_Del 226.8 μs 432.93 μs 23.73 μs 209.35 μs 253.9 μs 217.34 μs 250.2 μs 2.18 0.20 643 B 4.02
Respire_Incr 104.8 μs 155.35 μs 8.52 μs 95.94 μs 112.9 μs 105.56 μs 112.2 μs 1.01 0.07 151 B 0.94
StackExchange_Incr 109.7 μs 92.97 μs 5.10 μs 104.40 μs 114.6 μs 110.08 μs 114.1 μs 1.05 0.05 388 B 2.42
Respire_Transaction_2 125.7 μs 74.84 μs 4.10 μs 121.40 μs 129.6 μs 126.16 μs 129.2 μs 1.21 0.04 1056 B 6.60
View raw benchmark output

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 9V74 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.302
[Host]     : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3
Job-YBPNYS : .NET 8.0.29 (8.0.29, 8.0.2926.32403), X64 RyuJIT x86-64-v3

Runtime=.NET 8.0  IterationCount=3  WarmupCount=1  

Method                        | Mean     | Error     | StdDev   | Min       | Max      | Median    | P95      | Ratio | RatioSD | Allocated | Alloc Ratio |
------------------------------ |---------:|----------:|---------:|----------:|---------:|----------:|---------:|------:|--------:|----------:|------------:|
Respire_Ping                  | 104.3 μs |  32.46 μs |  1.78 μs | 102.77 μs | 106.2 μs | 103.82 μs | 106.0 μs |  1.00 |    0.02 |     160 B |        1.00 |
StackExchange_Ping            | 105.9 μs |  93.05 μs |  5.10 μs | 102.64 μs | 111.8 μs | 103.23 μs | 110.9 μs |  1.02 |    0.04 |     384 B |        2.40 |
Respire_Set_Small             | 110.2 μs |  32.36 μs |  1.77 μs | 108.15 μs | 111.5 μs | 110.84 μs | 111.4 μs |  1.06 |    0.02 |     159 B |        0.99 |
StackExchange_Set_Small       | 107.5 μs |  14.73 μs |  0.81 μs | 106.56 μs | 108.0 μs | 107.86 μs | 108.0 μs |  1.03 |    0.02 |     404 B |        2.52 |
Respire_Set_Medium            | 117.0 μs | 111.73 μs |  6.12 μs | 109.92 μs | 120.9 μs | 120.15 μs | 120.8 μs |  1.12 |    0.05 |     169 B |        1.06 |
StackExchange_Set_Medium      | 122.0 μs | 128.16 μs |  7.02 μs | 115.20 μs | 129.2 μs | 121.57 μs | 128.5 μs |  1.17 |    0.06 |     414 B |        2.59 |
Respire_Set_Large             | 125.1 μs | 100.14 μs |  5.49 μs | 120.35 μs | 131.1 μs | 123.94 μs | 130.4 μs |  1.20 |    0.05 |     171 B |        1.07 |
StackExchange_Set_Large       | 130.6 μs | 164.64 μs |  9.02 μs | 120.31 μs | 137.2 μs | 134.21 μs | 136.9 μs |  1.25 |    0.08 |     407 B |        2.54 |
Respire_Get                   | 100.1 μs | 294.21 μs | 16.13 μs |  87.30 μs | 118.2 μs |  94.84 μs | 115.9 μs |  0.96 |    0.13 |     199 B |        1.24 |
StackExchange_Get             | 119.4 μs |  83.66 μs |  4.59 μs | 114.11 μs | 122.1 μs | 121.97 μs | 122.1 μs |  1.15 |    0.04 |     413 B |        2.58 |
StackExchange_MGet            | 114.9 μs | 245.64 μs | 13.46 μs | 103.71 μs | 129.8 μs | 111.06 μs | 127.9 μs |  1.10 |    0.11 |     637 B |        3.98 |
StackExchange_Pipeline_5_Sets | 150.8 μs | 109.05 μs |  5.98 μs | 144.04 μs | 155.3 μs | 153.19 μs | 155.1 μs |  1.45 |    0.05 |    2887 B |       18.04 |
Respire_Exists                | 108.7 μs |  71.99 μs |  3.95 μs | 104.20 μs | 111.7 μs | 110.06 μs | 111.5 μs |  1.04 |    0.04 |     143 B |        0.89 |
StackExchange_Exists          | 112.5 μs |  49.02 μs |  2.69 μs | 109.97 μs | 115.3 μs | 112.18 μs | 115.0 μs |  1.08 |    0.03 |     369 B |        2.31 |
Respire_Del                   | 211.6 μs | 346.96 μs | 19.02 μs | 192.39 μs | 230.4 μs | 212.12 μs | 228.6 μs |  2.03 |    0.16 |     195 B |        1.22 |
StackExchange_Del             | 226.8 μs | 432.93 μs | 23.73 μs | 209.35 μs | 253.9 μs | 217.34 μs | 250.2 μs |  2.18 |    0.20 |     643 B |        4.02 |
Respire_Incr                  | 104.8 μs | 155.35 μs |  8.52 μs |  95.94 μs | 112.9 μs | 105.56 μs | 112.2 μs |  1.01 |    0.07 |     151 B |        0.94 |
StackExchange_Incr            | 109.7 μs |  92.97 μs |  5.10 μs | 104.40 μs | 114.6 μs | 110.08 μs | 114.1 μs |  1.05 |    0.05 |     388 B |        2.42 |
Respire_Transaction_2         | 125.7 μs |  74.84 μs |  4.10 μs | 121.40 μs | 129.6 μs | 126.16 μs | 129.2 μs |  1.21 |    0.04 |    1056 B |        6.60 |

This branch was previously deployed

1 inactive deployment
Pull Requests — 884f9754 Deployed Aug 9, 2026 by thomhurst via modularpipeline #403
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support redis clusters

1 participant