Skip to content

Report subscriber reconnect state - #67

Merged
thomhurst merged 9 commits into
mainfrom
agent/issue-5-subscriber-resilience
Aug 9, 2026
Merged

thomhurst merged 9 commits into
mainfrom
agent/issue-5-subscriber-resilience

Conversation

@thomhurst

@thomhurst thomhurst commented Aug 9, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • publish Reconnecting when the dedicated subscriber connection dies
  • publish Connected only after all active channel/pattern/sharded routes are replayed
  • broaden connection-state event documentation to all client-owned connections
  • add a Redis integration test that kills the subscriber socket and verifies resubscription

Root cause

The subscriber already reconnected with exponential backoff and replayed its route registry, but that path bypassed the public ConnectionStateChanged event. Applications could not detect the message-delivery gap requested by the issue.

Validation

  • dotnet test tests/Respire.IntegrationTests/Respire.IntegrationTests.csproj -c Release --no-restore
  • 160 tests passed across net8.0 and net10.0
  • test confirms delivery before disconnect, Reconnecting → Connected, and delivery after replay

Closes #5

Summary by CodeRabbit

  • New Features
    • Improved connection state reporting across command and subscription connections.
    • Connection changes are now reported in the correct order, including during reconnects.
    • Successful reconnection is reported only after all affected connections recover.
  • Bug Fixes
    • Improved subscriber recovery after connection interruptions, including automatic resubscription and continued message delivery.

Subscriber reconnect and route replay already restored delivery, but outages were invisible to applications. Publish Reconnecting and Connected around replay.\n\nCloses #5
@coderabbitai

coderabbitai Bot commented Aug 9, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds slot-specific reconnect notifications, coordinates subscription and command recovery states in ClientCore, serializes aggregate state events, forwards them through RespireClient, and adds unit and integration coverage.

Changes

Connection state recovery

Layer / File(s) Summary
Slot state notifications
src/Respire/Infrastructure/RespireConnectionMultiplexer.cs
The multiplexer emits slot-specific Reconnecting and Connected events. Notification-handler failures are logged without propagation.
Subscription recovery states
src/Respire/PubSub/SubscriptionHub.cs
The subscription hub queues recovery states, suppresses stale Connected notifications, serializes publication, and clears pending states during disposal.
Aggregate client states
src/Respire/Internal/ClientCore.cs, src/Respire/RespireClient.cs
ClientCore combines command-slot and subscription states, publishes transitions in order, and exposes them through RespireClient.
State recovery validation
tests/Respire.Tests/Networking/ClientCoreTests.cs, tests/Respire.IntegrationTests/PubSubIntegrationTests.cs
Tests validate aggregate ordering, multi-slot recovery, subscriber resubscription, message delivery, and concurrent transitions.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RedisConnection
  participant SubscriptionHub
  participant ClientCore
  participant RespireClient
  RedisConnection->>SubscriptionHub: Connection closes
  SubscriptionHub->>ClientCore: Reconnecting
  RedisConnection->>SubscriptionHub: Replacement connection
  SubscriptionHub->>SubscriptionHub: Resubscribe channels
  SubscriptionHub->>ClientCore: Connected
  ClientCore->>RespireClient: Aggregate ConnectionStateChanged
Loading

Poem

I hop through slots as connections mend,
Reconnecting first, then Connected at the end.
Commands and subscriptions join the queue,
Ordered state events now pass cleanly through.
— A watchful rabbit 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 describes the primary change: reporting subscriber reconnect states.
Linked Issues check ✅ Passed The changes reconnect subscribers, replay subscriptions, publish ordered state events, and verify message delivery after recovery for issue #5.
Out of Scope Changes check ✅ Passed The aggregate connection-state tracking and related tests support subscriber recovery reporting and the linked issue objectives.
✨ 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-5-subscriber-resilience

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-09T08:33:44.406Z

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-09T08:34:02.620Z

Automated integration testing with Redis containers using TUnit

@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: e78f059a37

ℹ️ 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/PubSub/SubscriptionHub.cs Outdated
.ConfigureAwait(false);
}

core.Multiplexer.NotifyStateChanged(RespireConnectionState.Connected);

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 Avoid reporting Connected for a superseded subscriber

When the replacement subscriber connection closes after the final resubscribe acknowledgment but before this notification, its newly armed watcher can emit Reconnecting first and then this older watcher emits Connected for the already-dead connection. If the subsequent reconnect attempts fail, consumers are left with Connected as the latest state throughout the outage, defeating the delivery-gap signal this change adds. Tie the notification to the current live connection/generation so a superseded watcher cannot report restoration.

Useful? React with 👍 / 👎.

@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 84.51 μs 29.096 μs 1.595 μs 82.73 μs 85.81 μs 85.00 μs 85.73 μs 1.00 0.02 184 B 1.00
StackExchange_Ping 120.35 μs 21.266 μs 1.166 μs 119.53 μs 121.69 μs 119.85 μs 121.50 μs 1.42 0.03 424 B 2.30
Respire_Set_Small 88.07 μs 23.328 μs 1.279 μs 87.01 μs 89.49 μs 87.70 μs 89.31 μs 1.04 0.02 176 B 0.96
StackExchange_Set_Small 123.51 μs 34.238 μs 1.877 μs 121.69 μs 125.44 μs 123.41 μs 125.23 μs 1.46 0.03 432 B 2.35
Respire_Set_Medium 89.20 μs 40.054 μs 2.196 μs 86.67 μs 90.62 μs 90.31 μs 90.59 μs 1.06 0.03 176 B 0.96
StackExchange_Set_Medium 123.88 μs 15.170 μs 0.832 μs 123.32 μs 124.84 μs 123.48 μs 124.70 μs 1.47 0.03 432 B 2.35
Respire_Set_Large 95.50 μs 13.210 μs 0.724 μs 94.96 μs 96.33 μs 95.22 μs 96.22 μs 1.13 0.02 176 B 0.96
StackExchange_Set_Large 130.79 μs 19.490 μs 1.068 μs 129.67 μs 131.79 μs 130.91 μs 131.71 μs 1.55 0.03 432 B 2.35
Respire_Get 85.27 μs 5.982 μs 0.328 μs 84.91 μs 85.54 μs 85.37 μs 85.52 μs 1.01 0.02 224 B 1.22
StackExchange_Get 123.22 μs 24.606 μs 1.349 μs 122.14 μs 124.73 μs 122.78 μs 124.54 μs 1.46 0.03 504 B 2.74
StackExchange_MGet 126.00 μs 16.584 μs 0.909 μs 125.16 μs 126.97 μs 125.87 μs 126.86 μs 1.49 0.03 896 B 4.87
StackExchange_Pipeline_5_Sets 131.93 μs 17.523 μs 0.960 μs 130.98 μs 132.90 μs 131.90 μs 132.80 μs 1.56 0.03 2968 B 16.13
Respire_Exists 84.22 μs 18.574 μs 1.018 μs 83.06 μs 84.96 μs 84.63 μs 84.93 μs 1.00 0.02 175 B 0.95
StackExchange_Exists 121.20 μs 2.446 μs 0.134 μs 121.05 μs 121.31 μs 121.24 μs 121.30 μs 1.43 0.02 400 B 2.17
Respire_Del 159.57 μs 34.972 μs 1.917 μs 157.42 μs 161.11 μs 160.17 μs 161.01 μs 1.89 0.04 200 B 1.09
StackExchange_Del 237.07 μs 51.959 μs 2.848 μs 234.67 μs 240.22 μs 236.32 μs 239.83 μs 2.81 0.05 664 B 3.61
Respire_Incr 85.57 μs 54.216 μs 2.972 μs 82.14 μs 87.48 μs 87.07 μs 87.44 μs 1.01 0.03 184 B 1.00
StackExchange_Incr 121.87 μs 10.799 μs 0.592 μs 121.47 μs 122.55 μs 121.59 μs 122.45 μs 1.44 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 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                  |  84.51 μs | 29.096 μs | 1.595 μs |  82.73 μs |  85.81 μs |  85.00 μs |  85.73 μs |  1.00 |    0.02 |     184 B |        1.00 |
StackExchange_Ping            | 120.35 μs | 21.266 μs | 1.166 μs | 119.53 μs | 121.69 μs | 119.85 μs | 121.50 μs |  1.42 |    0.03 |     424 B |        2.30 |
Respire_Set_Small             |  88.07 μs | 23.328 μs | 1.279 μs |  87.01 μs |  89.49 μs |  87.70 μs |  89.31 μs |  1.04 |    0.02 |     176 B |        0.96 |
StackExchange_Set_Small       | 123.51 μs | 34.238 μs | 1.877 μs | 121.69 μs | 125.44 μs | 123.41 μs | 125.23 μs |  1.46 |    0.03 |     432 B |        2.35 |
Respire_Set_Medium            |  89.20 μs | 40.054 μs | 2.196 μs |  86.67 μs |  90.62 μs |  90.31 μs |  90.59 μs |  1.06 |    0.03 |     176 B |        0.96 |
StackExchange_Set_Medium      | 123.88 μs | 15.170 μs | 0.832 μs | 123.32 μs | 124.84 μs | 123.48 μs | 124.70 μs |  1.47 |    0.03 |     432 B |        2.35 |
Respire_Set_Large             |  95.50 μs | 13.210 μs | 0.724 μs |  94.96 μs |  96.33 μs |  95.22 μs |  96.22 μs |  1.13 |    0.02 |     176 B |        0.96 |
StackExchange_Set_Large       | 130.79 μs | 19.490 μs | 1.068 μs | 129.67 μs | 131.79 μs | 130.91 μs | 131.71 μs |  1.55 |    0.03 |     432 B |        2.35 |
Respire_Get                   |  85.27 μs |  5.982 μs | 0.328 μs |  84.91 μs |  85.54 μs |  85.37 μs |  85.52 μs |  1.01 |    0.02 |     224 B |        1.22 |
StackExchange_Get             | 123.22 μs | 24.606 μs | 1.349 μs | 122.14 μs | 124.73 μs | 122.78 μs | 124.54 μs |  1.46 |    0.03 |     504 B |        2.74 |
StackExchange_MGet            | 126.00 μs | 16.584 μs | 0.909 μs | 125.16 μs | 126.97 μs | 125.87 μs | 126.86 μs |  1.49 |    0.03 |     896 B |        4.87 |
StackExchange_Pipeline_5_Sets | 131.93 μs | 17.523 μs | 0.960 μs | 130.98 μs | 132.90 μs | 131.90 μs | 132.80 μs |  1.56 |    0.03 |    2968 B |       16.13 |
Respire_Exists                |  84.22 μs | 18.574 μs | 1.018 μs |  83.06 μs |  84.96 μs |  84.63 μs |  84.93 μs |  1.00 |    0.02 |     175 B |        0.95 |
StackExchange_Exists          | 121.20 μs |  2.446 μs | 0.134 μs | 121.05 μs | 121.31 μs | 121.24 μs | 121.30 μs |  1.43 |    0.02 |     400 B |        2.17 |
Respire_Del                   | 159.57 μs | 34.972 μs | 1.917 μs | 157.42 μs | 161.11 μs | 160.17 μs | 161.01 μs |  1.89 |    0.04 |     200 B |        1.09 |
StackExchange_Del             | 237.07 μs | 51.959 μs | 2.848 μs | 234.67 μs | 240.22 μs | 236.32 μs | 239.83 μs |  2.81 |    0.05 |     664 B |        3.61 |
Respire_Incr                  |  85.57 μs | 54.216 μs | 2.972 μs |  82.14 μs |  87.48 μs |  87.07 μs |  87.44 μs |  1.01 |    0.03 |     184 B |        1.00 |
StackExchange_Incr            | 121.87 μs | 10.799 μs | 0.592 μs | 121.47 μs | 122.55 μs | 121.59 μs | 122.45 μs |  1.44 |    0.02 |     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 3.04GHz, 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 184.2 μs 27.37 μs 1.50 μs 182.7 μs 185.7 μs 184.2 μs 185.6 μs 1.00 0.01 180 B 1.00
StackExchange_Ping 238.1 μs 33.38 μs 1.83 μs 236.2 μs 239.8 μs 238.2 μs 239.6 μs 1.29 0.01 424 B 2.36
Respire_Set_Small 186.3 μs 5.98 μs 0.33 μs 186.0 μs 186.6 μs 186.3 μs 186.6 μs 1.01 0.01 176 B 0.98
StackExchange_Set_Small 242.3 μs 118.38 μs 6.49 μs 238.0 μs 249.7 μs 239.1 μs 248.7 μs 1.32 0.03 432 B 2.40
Respire_Set_Medium 188.0 μs 15.38 μs 0.84 μs 187.4 μs 188.9 μs 187.6 μs 188.8 μs 1.02 0.01 176 B 0.98
StackExchange_Set_Medium 240.1 μs 14.22 μs 0.78 μs 239.5 μs 241.0 μs 239.9 μs 240.9 μs 1.30 0.01 432 B 2.40
Respire_Set_Large 197.4 μs 4.56 μs 0.25 μs 197.2 μs 197.7 μs 197.2 μs 197.6 μs 1.07 0.01 176 B 0.98
StackExchange_Set_Large 250.8 μs 49.21 μs 2.70 μs 247.9 μs 253.2 μs 251.4 μs 253.0 μs 1.36 0.02 432 B 2.40
Respire_Get 184.8 μs 18.54 μs 1.02 μs 183.7 μs 185.7 μs 185.0 μs 185.7 μs 1.00 0.01 224 B 1.24
StackExchange_Get 238.4 μs 15.36 μs 0.84 μs 237.5 μs 239.1 μs 238.5 μs 239.1 μs 1.29 0.01 504 B 2.80
StackExchange_MGet 245.6 μs 25.08 μs 1.37 μs 244.1 μs 246.7 μs 246.1 μs 246.7 μs 1.33 0.01 896 B 4.98
StackExchange_Pipeline_5_Sets 255.0 μs 35.10 μs 1.92 μs 252.9 μs 256.5 μs 255.8 μs 256.4 μs 1.38 0.01 2968 B 16.49
Respire_Exists 184.2 μs 4.82 μs 0.26 μs 183.9 μs 184.5 μs 184.2 μs 184.4 μs 1.00 0.01 176 B 0.98
StackExchange_Exists 241.4 μs 26.87 μs 1.47 μs 240.4 μs 243.1 μs 240.7 μs 242.8 μs 1.31 0.01 399 B 2.22
Respire_Del 343.2 μs 120.47 μs 6.60 μs 335.6 μs 347.3 μs 346.7 μs 347.2 μs 1.86 0.03 199 B 1.11
StackExchange_Del 466.0 μs 39.08 μs 2.14 μs 464.4 μs 468.5 μs 465.2 μs 468.2 μs 2.53 0.02 664 B 3.69
Respire_Incr 183.8 μs 15.31 μs 0.84 μs 182.9 μs 184.4 μs 184.2 μs 184.4 μs 1.00 0.01 184 B 1.02
StackExchange_Incr 238.9 μs 51.22 μs 2.81 μs 236.6 μs 242.0 μs 238.0 μs 241.6 μs 1.30 0.02 400 B 2.22
View raw benchmark output

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 3.04GHz, 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                  | 184.2 μs |  27.37 μs | 1.50 μs | 182.7 μs | 185.7 μs | 184.2 μs | 185.6 μs |  1.00 |    0.01 |     180 B |        1.00 |
StackExchange_Ping            | 238.1 μs |  33.38 μs | 1.83 μs | 236.2 μs | 239.8 μs | 238.2 μs | 239.6 μs |  1.29 |    0.01 |     424 B |        2.36 |
Respire_Set_Small             | 186.3 μs |   5.98 μs | 0.33 μs | 186.0 μs | 186.6 μs | 186.3 μs | 186.6 μs |  1.01 |    0.01 |     176 B |        0.98 |
StackExchange_Set_Small       | 242.3 μs | 118.38 μs | 6.49 μs | 238.0 μs | 249.7 μs | 239.1 μs | 248.7 μs |  1.32 |    0.03 |     432 B |        2.40 |
Respire_Set_Medium            | 188.0 μs |  15.38 μs | 0.84 μs | 187.4 μs | 188.9 μs | 187.6 μs | 188.8 μs |  1.02 |    0.01 |     176 B |        0.98 |
StackExchange_Set_Medium      | 240.1 μs |  14.22 μs | 0.78 μs | 239.5 μs | 241.0 μs | 239.9 μs | 240.9 μs |  1.30 |    0.01 |     432 B |        2.40 |
Respire_Set_Large             | 197.4 μs |   4.56 μs | 0.25 μs | 197.2 μs | 197.7 μs | 197.2 μs | 197.6 μs |  1.07 |    0.01 |     176 B |        0.98 |
StackExchange_Set_Large       | 250.8 μs |  49.21 μs | 2.70 μs | 247.9 μs | 253.2 μs | 251.4 μs | 253.0 μs |  1.36 |    0.02 |     432 B |        2.40 |
Respire_Get                   | 184.8 μs |  18.54 μs | 1.02 μs | 183.7 μs | 185.7 μs | 185.0 μs | 185.7 μs |  1.00 |    0.01 |     224 B |        1.24 |
StackExchange_Get             | 238.4 μs |  15.36 μs | 0.84 μs | 237.5 μs | 239.1 μs | 238.5 μs | 239.1 μs |  1.29 |    0.01 |     504 B |        2.80 |
StackExchange_MGet            | 245.6 μs |  25.08 μs | 1.37 μs | 244.1 μs | 246.7 μs | 246.1 μs | 246.7 μs |  1.33 |    0.01 |     896 B |        4.98 |
StackExchange_Pipeline_5_Sets | 255.0 μs |  35.10 μs | 1.92 μs | 252.9 μs | 256.5 μs | 255.8 μs | 256.4 μs |  1.38 |    0.01 |    2968 B |       16.49 |
Respire_Exists                | 184.2 μs |   4.82 μs | 0.26 μs | 183.9 μs | 184.5 μs | 184.2 μs | 184.4 μs |  1.00 |    0.01 |     176 B |        0.98 |
StackExchange_Exists          | 241.4 μs |  26.87 μs | 1.47 μs | 240.4 μs | 243.1 μs | 240.7 μs | 242.8 μs |  1.31 |    0.01 |     399 B |        2.22 |
Respire_Del                   | 343.2 μs | 120.47 μs | 6.60 μs | 335.6 μs | 347.3 μs | 346.7 μs | 347.2 μs |  1.86 |    0.03 |     199 B |        1.11 |
StackExchange_Del             | 466.0 μs |  39.08 μs | 2.14 μs | 464.4 μs | 468.5 μs | 465.2 μs | 468.2 μs |  2.53 |    0.02 |     664 B |        3.69 |
Respire_Incr                  | 183.8 μs |  15.31 μs | 0.84 μs | 182.9 μs | 184.4 μs | 184.2 μs | 184.4 μs |  1.00 |    0.01 |     184 B |        1.02 |
StackExchange_Incr            | 238.9 μs |  51.22 μs | 2.81 μs | 236.6 μs | 242.0 μs | 238.0 μs | 241.6 μs |  1.30 |    0.02 |     400 B |        2.22 |

@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:42:09.513Z

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:42:37.104Z

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 174.3 μs 6.86 μs 0.38 μs 174.1 μs 174.8 μs 174.2 μs 174.7 μs 1.00 0.00 184 B 1.00
StackExchange_Ping 224.7 μs 30.20 μs 1.66 μs 222.8 μs 226.0 μs 225.3 μs 225.9 μs 1.29 0.01 424 B 2.30
Respire_Set_Small 177.1 μs 10.35 μs 0.57 μs 176.5 μs 177.6 μs 177.2 μs 177.6 μs 1.02 0.00 176 B 0.96
StackExchange_Set_Small 227.1 μs 31.45 μs 1.72 μs 225.1 μs 228.3 μs 227.8 μs 228.2 μs 1.30 0.01 432 B 2.35
Respire_Set_Medium 177.8 μs 10.82 μs 0.59 μs 177.2 μs 178.4 μs 177.7 μs 178.3 μs 1.02 0.00 176 B 0.96
StackExchange_Set_Medium 237.7 μs 261.43 μs 14.33 μs 228.6 μs 254.3 μs 230.4 μs 251.9 μs 1.36 0.07 432 B 2.35
Respire_Set_Large 187.1 μs 7.71 μs 0.42 μs 186.6 μs 187.4 μs 187.1 μs 187.4 μs 1.07 0.00 176 B 0.96
StackExchange_Set_Large 235.1 μs 19.89 μs 1.09 μs 233.8 μs 235.9 μs 235.5 μs 235.9 μs 1.35 0.01 432 B 2.35
Respire_Get 177.5 μs 32.15 μs 1.76 μs 175.5 μs 178.8 μs 178.2 μs 178.7 μs 1.02 0.01 224 B 1.22
StackExchange_Get 226.8 μs 14.39 μs 0.79 μs 226.1 μs 227.6 μs 226.7 μs 227.5 μs 1.30 0.00 504 B 2.74
StackExchange_MGet 235.3 μs 98.84 μs 5.42 μs 231.8 μs 241.5 μs 232.4 μs 240.6 μs 1.35 0.03 896 B 4.87
StackExchange_Pipeline_5_Sets 236.4 μs 33.13 μs 1.82 μs 234.7 μs 238.3 μs 236.2 μs 238.1 μs 1.36 0.01 2968 B 16.13
Respire_Exists 174.0 μs 4.64 μs 0.25 μs 173.7 μs 174.2 μs 174.1 μs 174.2 μs 1.00 0.00 176 B 0.96
StackExchange_Exists 225.6 μs 19.98 μs 1.10 μs 224.9 μs 226.8 μs 225.0 μs 226.7 μs 1.29 0.01 400 B 2.17
Respire_Del 333.5 μs 56.41 μs 3.09 μs 330.1 μs 336.1 μs 334.3 μs 336.0 μs 1.91 0.02 200 B 1.09
StackExchange_Del 451.6 μs 545.50 μs 29.90 μs 433.8 μs 486.1 μs 434.9 μs 481.0 μs 2.59 0.15 664 B 3.61
Respire_Incr 176.3 μs 12.39 μs 0.68 μs 175.7 μs 177.1 μs 176.2 μs 177.0 μs 1.01 0.00 184 B 1.00
StackExchange_Incr 224.8 μs 14.69 μs 0.80 μs 223.9 μs 225.3 μs 225.2 μs 225.3 μs 1.29 0.00 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                  | 174.3 μs |   6.86 μs |  0.38 μs | 174.1 μs | 174.8 μs | 174.2 μs | 174.7 μs |  1.00 |    0.00 |     184 B |        1.00 |
StackExchange_Ping            | 224.7 μs |  30.20 μs |  1.66 μs | 222.8 μs | 226.0 μs | 225.3 μs | 225.9 μs |  1.29 |    0.01 |     424 B |        2.30 |
Respire_Set_Small             | 177.1 μs |  10.35 μs |  0.57 μs | 176.5 μs | 177.6 μs | 177.2 μs | 177.6 μs |  1.02 |    0.00 |     176 B |        0.96 |
StackExchange_Set_Small       | 227.1 μs |  31.45 μs |  1.72 μs | 225.1 μs | 228.3 μs | 227.8 μs | 228.2 μs |  1.30 |    0.01 |     432 B |        2.35 |
Respire_Set_Medium            | 177.8 μs |  10.82 μs |  0.59 μs | 177.2 μs | 178.4 μs | 177.7 μs | 178.3 μs |  1.02 |    0.00 |     176 B |        0.96 |
StackExchange_Set_Medium      | 237.7 μs | 261.43 μs | 14.33 μs | 228.6 μs | 254.3 μs | 230.4 μs | 251.9 μs |  1.36 |    0.07 |     432 B |        2.35 |
Respire_Set_Large             | 187.1 μs |   7.71 μs |  0.42 μs | 186.6 μs | 187.4 μs | 187.1 μs | 187.4 μs |  1.07 |    0.00 |     176 B |        0.96 |
StackExchange_Set_Large       | 235.1 μs |  19.89 μs |  1.09 μs | 233.8 μs | 235.9 μs | 235.5 μs | 235.9 μs |  1.35 |    0.01 |     432 B |        2.35 |
Respire_Get                   | 177.5 μs |  32.15 μs |  1.76 μs | 175.5 μs | 178.8 μs | 178.2 μs | 178.7 μs |  1.02 |    0.01 |     224 B |        1.22 |
StackExchange_Get             | 226.8 μs |  14.39 μs |  0.79 μs | 226.1 μs | 227.6 μs | 226.7 μs | 227.5 μs |  1.30 |    0.00 |     504 B |        2.74 |
StackExchange_MGet            | 235.3 μs |  98.84 μs |  5.42 μs | 231.8 μs | 241.5 μs | 232.4 μs | 240.6 μs |  1.35 |    0.03 |     896 B |        4.87 |
StackExchange_Pipeline_5_Sets | 236.4 μs |  33.13 μs |  1.82 μs | 234.7 μs | 238.3 μs | 236.2 μs | 238.1 μs |  1.36 |    0.01 |    2968 B |       16.13 |
Respire_Exists                | 174.0 μs |   4.64 μs |  0.25 μs | 173.7 μs | 174.2 μs | 174.1 μs | 174.2 μs |  1.00 |    0.00 |     176 B |        0.96 |
StackExchange_Exists          | 225.6 μs |  19.98 μs |  1.10 μs | 224.9 μs | 226.8 μs | 225.0 μs | 226.7 μs |  1.29 |    0.01 |     400 B |        2.17 |
Respire_Del                   | 333.5 μs |  56.41 μs |  3.09 μs | 330.1 μs | 336.1 μs | 334.3 μs | 336.0 μs |  1.91 |    0.02 |     200 B |        1.09 |
StackExchange_Del             | 451.6 μs | 545.50 μs | 29.90 μs | 433.8 μs | 486.1 μs | 434.9 μs | 481.0 μs |  2.59 |    0.15 |     664 B |        3.61 |
Respire_Incr                  | 176.3 μs |  12.39 μs |  0.68 μs | 175.7 μs | 177.1 μs | 176.2 μs | 177.0 μs |  1.01 |    0.00 |     184 B |        1.00 |
StackExchange_Incr            | 224.8 μs |  14.69 μs |  0.80 μs | 223.9 μs | 225.3 μs | 225.2 μs | 225.3 μs |  1.29 |    0.00 |     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.75GHz, 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 186.7 μs 34.99 μs 1.92 μs 184.7 μs 188.6 μs 186.9 μs 188.4 μs 1.00 0.01 184 B 1.00
StackExchange_Ping 243.7 μs 36.78 μs 2.02 μs 242.5 μs 246.1 μs 242.7 μs 245.7 μs 1.31 0.01 424 B 2.30
Respire_Set_Small 187.7 μs 39.92 μs 2.19 μs 185.5 μs 189.9 μs 187.8 μs 189.7 μs 1.01 0.01 176 B 0.96
StackExchange_Set_Small 244.8 μs 16.58 μs 0.91 μs 243.8 μs 245.6 μs 245.1 μs 245.5 μs 1.31 0.01 432 B 2.35
Respire_Set_Medium 190.2 μs 4.62 μs 0.25 μs 189.9 μs 190.4 μs 190.2 μs 190.4 μs 1.02 0.01 176 B 0.96
StackExchange_Set_Medium 245.2 μs 13.34 μs 0.73 μs 244.4 μs 245.8 μs 245.3 μs 245.8 μs 1.31 0.01 432 B 2.35
Respire_Set_Large 200.2 μs 14.03 μs 0.77 μs 199.3 μs 200.7 μs 200.6 μs 200.7 μs 1.07 0.01 176 B 0.96
StackExchange_Set_Large 254.3 μs 26.14 μs 1.43 μs 252.9 μs 255.7 μs 254.2 μs 255.6 μs 1.36 0.01 432 B 2.35
Respire_Get 182.5 μs 149.31 μs 8.18 μs 173.1 μs 187.2 μs 187.2 μs 187.2 μs 0.98 0.04 224 B 1.22
StackExchange_Get 244.7 μs 44.47 μs 2.44 μs 242.0 μs 246.8 μs 245.2 μs 246.6 μs 1.31 0.02 504 B 2.74
StackExchange_MGet 248.6 μs 44.96 μs 2.46 μs 245.9 μs 250.7 μs 249.2 μs 250.5 μs 1.33 0.02 896 B 4.87
StackExchange_Pipeline_5_Sets 255.6 μs 57.00 μs 3.12 μs 252.0 μs 257.4 μs 257.4 μs 257.4 μs 1.37 0.02 2968 B 16.13
Respire_Exists 187.4 μs 10.43 μs 0.57 μs 186.8 μs 187.9 μs 187.5 μs 187.9 μs 1.00 0.01 175 B 0.95
StackExchange_Exists 242.7 μs 28.43 μs 1.56 μs 241.0 μs 244.0 μs 243.0 μs 243.9 μs 1.30 0.01 400 B 2.17
Respire_Del 363.9 μs 15.72 μs 0.86 μs 363.3 μs 364.9 μs 363.4 μs 364.7 μs 1.95 0.02 200 B 1.09
StackExchange_Del 471.4 μs 24.19 μs 1.33 μs 470.1 μs 472.7 μs 471.5 μs 472.6 μs 2.52 0.02 664 B 3.61
Respire_Incr 187.4 μs 19.84 μs 1.09 μs 186.2 μs 188.2 μs 187.8 μs 188.2 μs 1.00 0.01 184 B 1.00
StackExchange_Incr 243.7 μs 0.71 μs 0.04 μs 243.7 μs 243.7 μs 243.7 μs 243.7 μs 1.31 0.01 400 B 2.17
View raw benchmark output

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.75GHz, 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                  | 186.7 μs |  34.99 μs | 1.92 μs | 184.7 μs | 188.6 μs | 186.9 μs | 188.4 μs |  1.00 |    0.01 |     184 B |        1.00 |
StackExchange_Ping            | 243.7 μs |  36.78 μs | 2.02 μs | 242.5 μs | 246.1 μs | 242.7 μs | 245.7 μs |  1.31 |    0.01 |     424 B |        2.30 |
Respire_Set_Small             | 187.7 μs |  39.92 μs | 2.19 μs | 185.5 μs | 189.9 μs | 187.8 μs | 189.7 μs |  1.01 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Small       | 244.8 μs |  16.58 μs | 0.91 μs | 243.8 μs | 245.6 μs | 245.1 μs | 245.5 μs |  1.31 |    0.01 |     432 B |        2.35 |
Respire_Set_Medium            | 190.2 μs |   4.62 μs | 0.25 μs | 189.9 μs | 190.4 μs | 190.2 μs | 190.4 μs |  1.02 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Medium      | 245.2 μs |  13.34 μs | 0.73 μs | 244.4 μs | 245.8 μs | 245.3 μs | 245.8 μs |  1.31 |    0.01 |     432 B |        2.35 |
Respire_Set_Large             | 200.2 μs |  14.03 μs | 0.77 μs | 199.3 μs | 200.7 μs | 200.6 μs | 200.7 μs |  1.07 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Large       | 254.3 μs |  26.14 μs | 1.43 μs | 252.9 μs | 255.7 μs | 254.2 μs | 255.6 μs |  1.36 |    0.01 |     432 B |        2.35 |
Respire_Get                   | 182.5 μs | 149.31 μs | 8.18 μs | 173.1 μs | 187.2 μs | 187.2 μs | 187.2 μs |  0.98 |    0.04 |     224 B |        1.22 |
StackExchange_Get             | 244.7 μs |  44.47 μs | 2.44 μs | 242.0 μs | 246.8 μs | 245.2 μs | 246.6 μs |  1.31 |    0.02 |     504 B |        2.74 |
StackExchange_MGet            | 248.6 μs |  44.96 μs | 2.46 μs | 245.9 μs | 250.7 μs | 249.2 μs | 250.5 μs |  1.33 |    0.02 |     896 B |        4.87 |
StackExchange_Pipeline_5_Sets | 255.6 μs |  57.00 μs | 3.12 μs | 252.0 μs | 257.4 μs | 257.4 μs | 257.4 μs |  1.37 |    0.02 |    2968 B |       16.13 |
Respire_Exists                | 187.4 μs |  10.43 μs | 0.57 μs | 186.8 μs | 187.9 μs | 187.5 μs | 187.9 μs |  1.00 |    0.01 |     175 B |        0.95 |
StackExchange_Exists          | 242.7 μs |  28.43 μs | 1.56 μs | 241.0 μs | 244.0 μs | 243.0 μs | 243.9 μs |  1.30 |    0.01 |     400 B |        2.17 |
Respire_Del                   | 363.9 μs |  15.72 μs | 0.86 μs | 363.3 μs | 364.9 μs | 363.4 μs | 364.7 μs |  1.95 |    0.02 |     200 B |        1.09 |
StackExchange_Del             | 471.4 μs |  24.19 μs | 1.33 μs | 470.1 μs | 472.7 μs | 471.5 μs | 472.6 μs |  2.52 |    0.02 |     664 B |        3.61 |
Respire_Incr                  | 187.4 μs |  19.84 μs | 1.09 μs | 186.2 μs | 188.2 μs | 187.8 μs | 188.2 μs |  1.00 |    0.01 |     184 B |        1.00 |
StackExchange_Incr            | 243.7 μs |   0.71 μs | 0.04 μs | 243.7 μs | 243.7 μs | 243.7 μs | 243.7 μs |  1.31 |    0.01 |     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: f4cfc7ba67

ℹ️ 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/PubSub/SubscriptionHub.cs Outdated
&& ReferenceEquals(Volatile.Read(ref _connection), replacement)
&& replacement.IsConnected)
{
core.Multiplexer.NotifyStateChanged(RespireConnectionState.Connected);

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 Keep Connected pending while command sockets are reconnecting

When an outage drops both the command socket and the subscriber, the command reconnect attempt can fail and remain pending until the next command while the subscriber's retry loop later succeeds. This notification then publishes Connected through the shared public event even though the command socket is still dead and IsConnected is false; because the event contains no connection identity, health consumers can incorrectly treat the client as restored. Coordinate reconnect state across the multiplexer and subscriber rather than independently announcing Connected.

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-09T11:24:53.754Z

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:25:26.380Z

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.83GHz, 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 183.4 μs 46.24 μs 2.53 μs 180.8 μs 185.8 μs 183.7 μs 185.6 μs 1.00 0.02 183 B 1.00
StackExchange_Ping 242.4 μs 33.98 μs 1.86 μs 240.2 μs 243.5 μs 243.4 μs 243.5 μs 1.32 0.02 424 B 2.32
Respire_Set_Small 190.4 μs 13.10 μs 0.72 μs 189.6 μs 190.9 μs 190.6 μs 190.9 μs 1.04 0.01 176 B 0.96
StackExchange_Set_Small 245.8 μs 32.64 μs 1.79 μs 243.7 μs 247.0 μs 246.6 μs 246.9 μs 1.34 0.02 432 B 2.36
Respire_Set_Medium 189.1 μs 9.71 μs 0.53 μs 188.5 μs 189.6 μs 189.1 μs 189.6 μs 1.03 0.01 175 B 0.96
StackExchange_Set_Medium 247.9 μs 42.04 μs 2.30 μs 246.3 μs 250.5 μs 246.8 μs 250.1 μs 1.35 0.02 432 B 2.36
Respire_Set_Large 199.5 μs 20.81 μs 1.14 μs 198.5 μs 200.8 μs 199.3 μs 200.6 μs 1.09 0.01 176 B 0.96
StackExchange_Set_Large 256.9 μs 32.45 μs 1.78 μs 255.2 μs 258.8 μs 256.6 μs 258.6 μs 1.40 0.02 432 B 2.36
Respire_Get 187.8 μs 18.18 μs 1.00 μs 187.1 μs 189.0 μs 187.3 μs 188.8 μs 1.02 0.01 224 B 1.22
StackExchange_Get 244.6 μs 12.37 μs 0.68 μs 243.9 μs 245.3 μs 244.7 μs 245.2 μs 1.33 0.02 504 B 2.75
StackExchange_MGet 249.4 μs 52.89 μs 2.90 μs 246.1 μs 251.4 μs 250.7 μs 251.3 μs 1.36 0.02 896 B 4.90
StackExchange_Pipeline_5_Sets 253.5 μs 76.10 μs 4.17 μs 248.7 μs 256.5 μs 255.2 μs 256.4 μs 1.38 0.03 2968 B 16.22
Respire_Exists 188.3 μs 14.24 μs 0.78 μs 187.5 μs 189.0 μs 188.3 μs 189.0 μs 1.03 0.01 176 B 0.96
StackExchange_Exists 244.8 μs 12.37 μs 0.68 μs 244.1 μs 245.5 μs 244.7 μs 245.4 μs 1.33 0.02 400 B 2.19
Respire_Del 360.0 μs 67.85 μs 3.72 μs 355.8 μs 362.6 μs 361.6 μs 362.5 μs 1.96 0.03 200 B 1.09
StackExchange_Del 468.2 μs 31.00 μs 1.70 μs 466.3 μs 469.6 μs 468.7 μs 469.5 μs 2.55 0.03 664 B 3.63
Respire_Incr 185.8 μs 13.92 μs 0.76 μs 185.1 μs 186.6 μs 185.8 μs 186.5 μs 1.01 0.01 178 B 0.97
StackExchange_Incr 247.4 μs 90.30 μs 4.95 μs 242.5 μs 252.4 μs 247.2 μs 251.9 μs 1.35 0.03 400 B 2.19
View raw benchmark output

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.83GHz, 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                  | 183.4 μs | 46.24 μs | 2.53 μs | 180.8 μs | 185.8 μs | 183.7 μs | 185.6 μs |  1.00 |    0.02 |     183 B |        1.00 |
StackExchange_Ping            | 242.4 μs | 33.98 μs | 1.86 μs | 240.2 μs | 243.5 μs | 243.4 μs | 243.5 μs |  1.32 |    0.02 |     424 B |        2.32 |
Respire_Set_Small             | 190.4 μs | 13.10 μs | 0.72 μs | 189.6 μs | 190.9 μs | 190.6 μs | 190.9 μs |  1.04 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Small       | 245.8 μs | 32.64 μs | 1.79 μs | 243.7 μs | 247.0 μs | 246.6 μs | 246.9 μs |  1.34 |    0.02 |     432 B |        2.36 |
Respire_Set_Medium            | 189.1 μs |  9.71 μs | 0.53 μs | 188.5 μs | 189.6 μs | 189.1 μs | 189.6 μs |  1.03 |    0.01 |     175 B |        0.96 |
StackExchange_Set_Medium      | 247.9 μs | 42.04 μs | 2.30 μs | 246.3 μs | 250.5 μs | 246.8 μs | 250.1 μs |  1.35 |    0.02 |     432 B |        2.36 |
Respire_Set_Large             | 199.5 μs | 20.81 μs | 1.14 μs | 198.5 μs | 200.8 μs | 199.3 μs | 200.6 μs |  1.09 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Large       | 256.9 μs | 32.45 μs | 1.78 μs | 255.2 μs | 258.8 μs | 256.6 μs | 258.6 μs |  1.40 |    0.02 |     432 B |        2.36 |
Respire_Get                   | 187.8 μs | 18.18 μs | 1.00 μs | 187.1 μs | 189.0 μs | 187.3 μs | 188.8 μs |  1.02 |    0.01 |     224 B |        1.22 |
StackExchange_Get             | 244.6 μs | 12.37 μs | 0.68 μs | 243.9 μs | 245.3 μs | 244.7 μs | 245.2 μs |  1.33 |    0.02 |     504 B |        2.75 |
StackExchange_MGet            | 249.4 μs | 52.89 μs | 2.90 μs | 246.1 μs | 251.4 μs | 250.7 μs | 251.3 μs |  1.36 |    0.02 |     896 B |        4.90 |
StackExchange_Pipeline_5_Sets | 253.5 μs | 76.10 μs | 4.17 μs | 248.7 μs | 256.5 μs | 255.2 μs | 256.4 μs |  1.38 |    0.03 |    2968 B |       16.22 |
Respire_Exists                | 188.3 μs | 14.24 μs | 0.78 μs | 187.5 μs | 189.0 μs | 188.3 μs | 189.0 μs |  1.03 |    0.01 |     176 B |        0.96 |
StackExchange_Exists          | 244.8 μs | 12.37 μs | 0.68 μs | 244.1 μs | 245.5 μs | 244.7 μs | 245.4 μs |  1.33 |    0.02 |     400 B |        2.19 |
Respire_Del                   | 360.0 μs | 67.85 μs | 3.72 μs | 355.8 μs | 362.6 μs | 361.6 μs | 362.5 μs |  1.96 |    0.03 |     200 B |        1.09 |
StackExchange_Del             | 468.2 μs | 31.00 μs | 1.70 μs | 466.3 μs | 469.6 μs | 468.7 μs | 469.5 μs |  2.55 |    0.03 |     664 B |        3.63 |
Respire_Incr                  | 185.8 μs | 13.92 μs | 0.76 μs | 185.1 μs | 186.6 μs | 185.8 μs | 186.5 μs |  1.01 |    0.01 |     178 B |        0.97 |
StackExchange_Incr            | 247.4 μs | 90.30 μs | 4.95 μs | 242.5 μs | 252.4 μs | 247.2 μs | 251.9 μs |  1.35 |    0.03 |     400 B |        2.19 |

@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 184.3 μs 30.11 μs 1.65 μs 182.5 μs 185.8 μs 184.4 μs 185.7 μs 1.00 0.01 184 B 1.00
StackExchange_Ping 238.4 μs 10.00 μs 0.55 μs 237.7 μs 238.7 μs 238.6 μs 238.7 μs 1.29 0.01 424 B 2.30
Respire_Set_Small 186.1 μs 9.37 μs 0.51 μs 185.7 μs 186.7 μs 186.0 μs 186.6 μs 1.01 0.01 176 B 0.96
StackExchange_Set_Small 241.3 μs 17.52 μs 0.96 μs 240.3 μs 242.2 μs 241.3 μs 242.1 μs 1.31 0.01 432 B 2.35
Respire_Set_Medium 188.2 μs 2.19 μs 0.12 μs 188.1 μs 188.3 μs 188.1 μs 188.3 μs 1.02 0.01 176 B 0.96
StackExchange_Set_Medium 241.4 μs 20.96 μs 1.15 μs 240.1 μs 242.3 μs 241.7 μs 242.2 μs 1.31 0.01 432 B 2.35
Respire_Set_Large 195.4 μs 9.75 μs 0.53 μs 194.9 μs 195.9 μs 195.5 μs 195.9 μs 1.06 0.01 176 B 0.96
StackExchange_Set_Large 252.4 μs 88.02 μs 4.82 μs 249.0 μs 257.9 μs 250.2 μs 257.1 μs 1.37 0.03 432 B 2.35
Respire_Get 185.0 μs 42.91 μs 2.35 μs 182.3 μs 186.6 μs 186.1 μs 186.6 μs 1.00 0.01 224 B 1.22
StackExchange_Get 243.6 μs 51.43 μs 2.82 μs 241.8 μs 246.8 μs 242.0 μs 246.3 μs 1.32 0.02 504 B 2.74
StackExchange_MGet 247.1 μs 37.73 μs 2.07 μs 244.7 μs 248.5 μs 248.0 μs 248.5 μs 1.34 0.01 896 B 4.87
StackExchange_Pipeline_5_Sets 256.1 μs 58.51 μs 3.21 μs 253.7 μs 259.8 μs 254.9 μs 259.3 μs 1.39 0.02 2968 B 16.13
Respire_Exists 184.4 μs 45.34 μs 2.49 μs 181.6 μs 186.3 μs 185.4 μs 186.2 μs 1.00 0.01 176 B 0.96
StackExchange_Exists 239.7 μs 26.11 μs 1.43 μs 238.4 μs 241.2 μs 239.6 μs 241.1 μs 1.30 0.01 400 B 2.17
Respire_Del 354.2 μs 20.91 μs 1.15 μs 352.9 μs 355.0 μs 354.8 μs 354.9 μs 1.92 0.02 200 B 1.09
StackExchange_Del 468.8 μs 45.96 μs 2.52 μs 466.3 μs 471.4 μs 468.6 μs 471.1 μs 2.54 0.02 664 B 3.61
Respire_Incr 182.3 μs 79.92 μs 4.38 μs 177.3 μs 185.5 μs 184.1 μs 185.3 μs 0.99 0.02 184 B 1.00
StackExchange_Incr 237.5 μs 9.52 μs 0.52 μs 237.0 μs 238.0 μs 237.6 μs 238.0 μs 1.29 0.01 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                  | 184.3 μs | 30.11 μs | 1.65 μs | 182.5 μs | 185.8 μs | 184.4 μs | 185.7 μs |  1.00 |    0.01 |     184 B |        1.00 |
StackExchange_Ping            | 238.4 μs | 10.00 μs | 0.55 μs | 237.7 μs | 238.7 μs | 238.6 μs | 238.7 μs |  1.29 |    0.01 |     424 B |        2.30 |
Respire_Set_Small             | 186.1 μs |  9.37 μs | 0.51 μs | 185.7 μs | 186.7 μs | 186.0 μs | 186.6 μs |  1.01 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Small       | 241.3 μs | 17.52 μs | 0.96 μs | 240.3 μs | 242.2 μs | 241.3 μs | 242.1 μs |  1.31 |    0.01 |     432 B |        2.35 |
Respire_Set_Medium            | 188.2 μs |  2.19 μs | 0.12 μs | 188.1 μs | 188.3 μs | 188.1 μs | 188.3 μs |  1.02 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Medium      | 241.4 μs | 20.96 μs | 1.15 μs | 240.1 μs | 242.3 μs | 241.7 μs | 242.2 μs |  1.31 |    0.01 |     432 B |        2.35 |
Respire_Set_Large             | 195.4 μs |  9.75 μs | 0.53 μs | 194.9 μs | 195.9 μs | 195.5 μs | 195.9 μs |  1.06 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Large       | 252.4 μs | 88.02 μs | 4.82 μs | 249.0 μs | 257.9 μs | 250.2 μs | 257.1 μs |  1.37 |    0.03 |     432 B |        2.35 |
Respire_Get                   | 185.0 μs | 42.91 μs | 2.35 μs | 182.3 μs | 186.6 μs | 186.1 μs | 186.6 μs |  1.00 |    0.01 |     224 B |        1.22 |
StackExchange_Get             | 243.6 μs | 51.43 μs | 2.82 μs | 241.8 μs | 246.8 μs | 242.0 μs | 246.3 μs |  1.32 |    0.02 |     504 B |        2.74 |
StackExchange_MGet            | 247.1 μs | 37.73 μs | 2.07 μs | 244.7 μs | 248.5 μs | 248.0 μs | 248.5 μs |  1.34 |    0.01 |     896 B |        4.87 |
StackExchange_Pipeline_5_Sets | 256.1 μs | 58.51 μs | 3.21 μs | 253.7 μs | 259.8 μs | 254.9 μs | 259.3 μs |  1.39 |    0.02 |    2968 B |       16.13 |
Respire_Exists                | 184.4 μs | 45.34 μs | 2.49 μs | 181.6 μs | 186.3 μs | 185.4 μs | 186.2 μs |  1.00 |    0.01 |     176 B |        0.96 |
StackExchange_Exists          | 239.7 μs | 26.11 μs | 1.43 μs | 238.4 μs | 241.2 μs | 239.6 μs | 241.1 μs |  1.30 |    0.01 |     400 B |        2.17 |
Respire_Del                   | 354.2 μs | 20.91 μs | 1.15 μs | 352.9 μs | 355.0 μs | 354.8 μs | 354.9 μs |  1.92 |    0.02 |     200 B |        1.09 |
StackExchange_Del             | 468.8 μs | 45.96 μs | 2.52 μs | 466.3 μs | 471.4 μs | 468.6 μs | 471.1 μs |  2.54 |    0.02 |     664 B |        3.61 |
Respire_Incr                  | 182.3 μs | 79.92 μs | 4.38 μs | 177.3 μs | 185.5 μs | 184.1 μs | 185.3 μs |  0.99 |    0.02 |     184 B |        1.00 |
StackExchange_Incr            | 237.5 μs |  9.52 μs | 0.52 μs | 237.0 μs | 238.0 μs | 237.6 μs | 238.0 μs |  1.29 |    0.01 |     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: c195a4b99c

ℹ️ 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/Internal/ClientCore.cs Outdated
}
else
{
_commandState = state;

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 Track reconnect state for every command slot

When Connections > 1 and two command slots reconnect concurrently, RespireConnectionMultiplexer.ScheduleReconnect emits Reconnecting for each slot and ReconnectAsync emits Connected as each slot recovers, but this single _commandState value collapses both reconnecting notifications. The first recovered slot therefore changes the aggregate to Connected while the other slot is still dead, and the second recovery is suppressed; track a reconnect count or per-slot state before publishing restoration.

Useful? React with 👍 / 👎.

Comment thread src/Respire/Internal/ClientCore.cs Outdated

try
{
handlers?.Invoke(aggregate);

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 Serialize state publication with aggregate updates

When command recovery and subscriber failure overlap, a command Connected notification can update _publishedState and pause after releasing _stateGate; the subscriber then updates the aggregate and publishes Reconnecting, after which this invocation publishes the stale Connected value. Unlike the watcher-generation case, the new cross-component aggregator therefore still lets consumers end an active subscriber outage with Connected as their latest event; serialize event delivery in the same order as aggregate transitions.

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-09T11:40:42.765Z

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:40:58.172Z

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 177.3 μs 21.98 μs 1.20 μs 176.0 μs 178.3 μs 177.6 μs 178.3 μs 1.00 0.01 184 B 1.00
StackExchange_Ping 230.8 μs 45.93 μs 2.52 μs 228.6 μs 233.6 μs 230.3 μs 233.2 μs 1.30 0.01 424 B 2.30
Respire_Set_Small 178.7 μs 31.30 μs 1.72 μs 176.8 μs 180.2 μs 179.1 μs 180.1 μs 1.01 0.01 176 B 0.96
StackExchange_Set_Small 231.0 μs 23.49 μs 1.29 μs 230.3 μs 232.5 μs 230.3 μs 232.3 μs 1.30 0.01 432 B 2.35
Respire_Set_Medium 181.5 μs 15.84 μs 0.87 μs 180.6 μs 182.4 μs 181.6 μs 182.3 μs 1.02 0.01 176 B 0.96
StackExchange_Set_Medium 231.7 μs 12.11 μs 0.66 μs 231.0 μs 232.3 μs 231.9 μs 232.2 μs 1.31 0.01 432 B 2.35
Respire_Set_Large 188.0 μs 34.18 μs 1.87 μs 186.0 μs 189.6 μs 188.5 μs 189.5 μs 1.06 0.01 176 B 0.96
StackExchange_Set_Large 240.4 μs 35.50 μs 1.95 μs 238.5 μs 242.4 μs 240.4 μs 242.2 μs 1.36 0.01 432 B 2.35
Respire_Get 177.3 μs 18.01 μs 0.99 μs 176.2 μs 177.9 μs 177.8 μs 177.9 μs 1.00 0.01 224 B 1.22
StackExchange_Get 241.7 μs 267.62 μs 14.67 μs 232.3 μs 258.6 μs 234.3 μs 256.2 μs 1.36 0.07 504 B 2.74
StackExchange_MGet 238.8 μs 114.00 μs 6.25 μs 235.2 μs 246.0 μs 235.2 μs 244.9 μs 1.35 0.03 896 B 4.87
StackExchange_Pipeline_5_Sets 244.5 μs 54.54 μs 2.99 μs 242.0 μs 247.8 μs 243.8 μs 247.4 μs 1.38 0.02 2968 B 16.13
Respire_Exists 177.6 μs 18.16 μs 1.00 μs 176.7 μs 178.6 μs 177.5 μs 178.5 μs 1.00 0.01 176 B 0.96
StackExchange_Exists 227.7 μs 35.35 μs 1.94 μs 225.5 μs 229.0 μs 228.6 μs 229.0 μs 1.28 0.01 400 B 2.17
Respire_Del 341.9 μs 34.30 μs 1.88 μs 340.0 μs 343.8 μs 341.9 μs 343.6 μs 1.93 0.01 200 B 1.09
StackExchange_Del 445.7 μs 45.13 μs 2.47 μs 442.8 μs 447.4 μs 446.9 μs 447.3 μs 2.51 0.02 664 B 3.61
Respire_Incr 176.2 μs 42.49 μs 2.33 μs 173.7 μs 178.4 μs 176.6 μs 178.2 μs 0.99 0.01 184 B 1.00
StackExchange_Incr 228.9 μs 50.06 μs 2.74 μs 225.7 μs 230.8 μs 230.0 μs 230.7 μs 1.29 0.02 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                  | 177.3 μs |  21.98 μs |  1.20 μs | 176.0 μs | 178.3 μs | 177.6 μs | 178.3 μs |  1.00 |    0.01 |     184 B |        1.00 |
StackExchange_Ping            | 230.8 μs |  45.93 μs |  2.52 μs | 228.6 μs | 233.6 μs | 230.3 μs | 233.2 μs |  1.30 |    0.01 |     424 B |        2.30 |
Respire_Set_Small             | 178.7 μs |  31.30 μs |  1.72 μs | 176.8 μs | 180.2 μs | 179.1 μs | 180.1 μs |  1.01 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Small       | 231.0 μs |  23.49 μs |  1.29 μs | 230.3 μs | 232.5 μs | 230.3 μs | 232.3 μs |  1.30 |    0.01 |     432 B |        2.35 |
Respire_Set_Medium            | 181.5 μs |  15.84 μs |  0.87 μs | 180.6 μs | 182.4 μs | 181.6 μs | 182.3 μs |  1.02 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Medium      | 231.7 μs |  12.11 μs |  0.66 μs | 231.0 μs | 232.3 μs | 231.9 μs | 232.2 μs |  1.31 |    0.01 |     432 B |        2.35 |
Respire_Set_Large             | 188.0 μs |  34.18 μs |  1.87 μs | 186.0 μs | 189.6 μs | 188.5 μs | 189.5 μs |  1.06 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Large       | 240.4 μs |  35.50 μs |  1.95 μs | 238.5 μs | 242.4 μs | 240.4 μs | 242.2 μs |  1.36 |    0.01 |     432 B |        2.35 |
Respire_Get                   | 177.3 μs |  18.01 μs |  0.99 μs | 176.2 μs | 177.9 μs | 177.8 μs | 177.9 μs |  1.00 |    0.01 |     224 B |        1.22 |
StackExchange_Get             | 241.7 μs | 267.62 μs | 14.67 μs | 232.3 μs | 258.6 μs | 234.3 μs | 256.2 μs |  1.36 |    0.07 |     504 B |        2.74 |
StackExchange_MGet            | 238.8 μs | 114.00 μs |  6.25 μs | 235.2 μs | 246.0 μs | 235.2 μs | 244.9 μs |  1.35 |    0.03 |     896 B |        4.87 |
StackExchange_Pipeline_5_Sets | 244.5 μs |  54.54 μs |  2.99 μs | 242.0 μs | 247.8 μs | 243.8 μs | 247.4 μs |  1.38 |    0.02 |    2968 B |       16.13 |
Respire_Exists                | 177.6 μs |  18.16 μs |  1.00 μs | 176.7 μs | 178.6 μs | 177.5 μs | 178.5 μs |  1.00 |    0.01 |     176 B |        0.96 |
StackExchange_Exists          | 227.7 μs |  35.35 μs |  1.94 μs | 225.5 μs | 229.0 μs | 228.6 μs | 229.0 μs |  1.28 |    0.01 |     400 B |        2.17 |
Respire_Del                   | 341.9 μs |  34.30 μs |  1.88 μs | 340.0 μs | 343.8 μs | 341.9 μs | 343.6 μs |  1.93 |    0.01 |     200 B |        1.09 |
StackExchange_Del             | 445.7 μs |  45.13 μs |  2.47 μs | 442.8 μs | 447.4 μs | 446.9 μs | 447.3 μs |  2.51 |    0.02 |     664 B |        3.61 |
Respire_Incr                  | 176.2 μs |  42.49 μs |  2.33 μs | 173.7 μs | 178.4 μs | 176.6 μs | 178.2 μs |  0.99 |    0.01 |     184 B |        1.00 |
StackExchange_Incr            | 228.9 μs |  50.06 μs |  2.74 μs | 225.7 μs | 230.8 μs | 230.0 μs | 230.7 μs |  1.29 |    0.02 |     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)
AMD EPYC 7763 2.63GHz, 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 182.6 μs 24.89 μs 1.36 μs 181.8 μs 184.2 μs 181.8 μs 184.0 μs 1.00 0.01 184 B 1.00
StackExchange_Ping 240.1 μs 164.16 μs 9.00 μs 234.7 μs 250.5 μs 235.1 μs 249.0 μs 1.31 0.04 424 B 2.30
Respire_Set_Small 184.0 μs 16.01 μs 0.88 μs 183.1 μs 184.8 μs 184.1 μs 184.7 μs 1.01 0.01 176 B 0.96
StackExchange_Set_Small 235.8 μs 21.51 μs 1.18 μs 234.9 μs 237.2 μs 235.5 μs 237.0 μs 1.29 0.01 432 B 2.35
Respire_Set_Medium 184.3 μs 9.43 μs 0.52 μs 183.9 μs 184.9 μs 184.2 μs 184.9 μs 1.01 0.01 176 B 0.96
StackExchange_Set_Medium 239.4 μs 34.34 μs 1.88 μs 237.2 μs 240.5 μs 240.5 μs 240.5 μs 1.31 0.01 432 B 2.35
Respire_Set_Large 193.0 μs 31.21 μs 1.71 μs 191.3 μs 194.7 μs 193.0 μs 194.5 μs 1.06 0.01 176 B 0.96
StackExchange_Set_Large 248.2 μs 46.54 μs 2.55 μs 246.3 μs 251.1 μs 247.4 μs 250.7 μs 1.36 0.01 432 B 2.35
Respire_Get 180.1 μs 33.80 μs 1.85 μs 178.4 μs 182.0 μs 179.9 μs 181.8 μs 0.99 0.01 224 B 1.22
StackExchange_Get 238.0 μs 43.42 μs 2.38 μs 236.1 μs 240.7 μs 237.3 μs 240.3 μs 1.30 0.01 504 B 2.74
StackExchange_MGet 241.4 μs 37.35 μs 2.05 μs 239.1 μs 242.7 μs 242.6 μs 242.7 μs 1.32 0.01 896 B 4.87
StackExchange_Pipeline_5_Sets 247.9 μs 27.19 μs 1.49 μs 246.4 μs 249.4 μs 247.8 μs 249.3 μs 1.36 0.01 2968 B 16.13
Respire_Exists 183.2 μs 14.71 μs 0.81 μs 182.5 μs 184.1 μs 183.0 μs 184.0 μs 1.00 0.01 176 B 0.96
StackExchange_Exists 235.4 μs 29.71 μs 1.63 μs 233.7 μs 236.9 μs 235.7 μs 236.8 μs 1.29 0.01 400 B 2.17
Respire_Del 353.2 μs 39.12 μs 2.14 μs 351.0 μs 355.3 μs 353.1 μs 355.1 μs 1.93 0.02 200 B 1.09
StackExchange_Del 461.3 μs 33.70 μs 1.85 μs 459.8 μs 463.4 μs 460.7 μs 463.1 μs 2.53 0.02 664 B 3.61
Respire_Incr 183.2 μs 15.03 μs 0.82 μs 182.3 μs 183.9 μs 183.5 μs 183.9 μs 1.00 0.01 183 B 0.99
StackExchange_Incr 235.5 μs 15.03 μs 0.82 μs 234.7 μs 236.3 μs 235.6 μs 236.2 μs 1.29 0.01 400 B 2.17
View raw benchmark output

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 7763 2.63GHz, 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                  | 182.6 μs |  24.89 μs | 1.36 μs | 181.8 μs | 184.2 μs | 181.8 μs | 184.0 μs |  1.00 |    0.01 |     184 B |        1.00 |
StackExchange_Ping            | 240.1 μs | 164.16 μs | 9.00 μs | 234.7 μs | 250.5 μs | 235.1 μs | 249.0 μs |  1.31 |    0.04 |     424 B |        2.30 |
Respire_Set_Small             | 184.0 μs |  16.01 μs | 0.88 μs | 183.1 μs | 184.8 μs | 184.1 μs | 184.7 μs |  1.01 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Small       | 235.8 μs |  21.51 μs | 1.18 μs | 234.9 μs | 237.2 μs | 235.5 μs | 237.0 μs |  1.29 |    0.01 |     432 B |        2.35 |
Respire_Set_Medium            | 184.3 μs |   9.43 μs | 0.52 μs | 183.9 μs | 184.9 μs | 184.2 μs | 184.9 μs |  1.01 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Medium      | 239.4 μs |  34.34 μs | 1.88 μs | 237.2 μs | 240.5 μs | 240.5 μs | 240.5 μs |  1.31 |    0.01 |     432 B |        2.35 |
Respire_Set_Large             | 193.0 μs |  31.21 μs | 1.71 μs | 191.3 μs | 194.7 μs | 193.0 μs | 194.5 μs |  1.06 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Large       | 248.2 μs |  46.54 μs | 2.55 μs | 246.3 μs | 251.1 μs | 247.4 μs | 250.7 μs |  1.36 |    0.01 |     432 B |        2.35 |
Respire_Get                   | 180.1 μs |  33.80 μs | 1.85 μs | 178.4 μs | 182.0 μs | 179.9 μs | 181.8 μs |  0.99 |    0.01 |     224 B |        1.22 |
StackExchange_Get             | 238.0 μs |  43.42 μs | 2.38 μs | 236.1 μs | 240.7 μs | 237.3 μs | 240.3 μs |  1.30 |    0.01 |     504 B |        2.74 |
StackExchange_MGet            | 241.4 μs |  37.35 μs | 2.05 μs | 239.1 μs | 242.7 μs | 242.6 μs | 242.7 μs |  1.32 |    0.01 |     896 B |        4.87 |
StackExchange_Pipeline_5_Sets | 247.9 μs |  27.19 μs | 1.49 μs | 246.4 μs | 249.4 μs | 247.8 μs | 249.3 μs |  1.36 |    0.01 |    2968 B |       16.13 |
Respire_Exists                | 183.2 μs |  14.71 μs | 0.81 μs | 182.5 μs | 184.1 μs | 183.0 μs | 184.0 μs |  1.00 |    0.01 |     176 B |        0.96 |
StackExchange_Exists          | 235.4 μs |  29.71 μs | 1.63 μs | 233.7 μs | 236.9 μs | 235.7 μs | 236.8 μs |  1.29 |    0.01 |     400 B |        2.17 |
Respire_Del                   | 353.2 μs |  39.12 μs | 2.14 μs | 351.0 μs | 355.3 μs | 353.1 μs | 355.1 μs |  1.93 |    0.02 |     200 B |        1.09 |
StackExchange_Del             | 461.3 μs |  33.70 μs | 1.85 μs | 459.8 μs | 463.4 μs | 460.7 μs | 463.1 μs |  2.53 |    0.02 |     664 B |        3.61 |
Respire_Incr                  | 183.2 μs |  15.03 μs | 0.82 μs | 182.3 μs | 183.9 μs | 183.5 μs | 183.9 μs |  1.00 |    0.01 |     183 B |        0.99 |
StackExchange_Incr            | 235.5 μs |  15.03 μs | 0.82 μs | 234.7 μs | 236.3 μs | 235.6 μs | 236.2 μs |  1.29 |    0.01 |     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: c0377e911e

ℹ️ 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/PubSub/SubscriptionHub.cs Outdated
Comment on lines +312 to +316
if (reconnectGeneration == Volatile.Read(ref _reconnectGeneration)
&& ReferenceEquals(Volatile.Read(ref _connection), replacement)
&& replacement.IsConnected)
{
core.NotifySubscriptionStateChanged(RespireConnectionState.Connected);

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 Serialize generation validation with Connected publication

Fresh evidence in the revised code is the remaining check-to-notification window: if the replacement closes after these generation/current/health checks pass, its watcher can publish Reconnecting before this watcher reaches NotifySubscriptionStateChanged, after which this stale invocation publishes Connected and leaves consumers seeing a healthy state during the outage. Make generation validation and publication atomic with respect to newer watcher transitions.

Useful? React with 👍 / 👎.

Comment on lines +166 to +168
states.Should().ContainInOrder(
RespireConnectionState.Reconnecting,
RespireConnectionState.Connected);

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 Await the Connected event before asserting it

When Redis activates the resubscription, a subsequent message can be processed before the reconnect watcher resumes from the subscription acknowledgment and emits Connected; receiving after-reconnect therefore does not guarantee that this list already contains the restoration event. Under that scheduling the assertion flakes, so wait with a timeout for the expected state transition instead of checking immediately after message delivery.

Useful? React with 👍 / 👎.

Guard generation validation and state publication with one lock, and await the Connected event in the integration test.
@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:08:47.893Z

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:09:00.807Z

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.86GHz, 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.1 μs 19.15 μs 1.05 μs 104.02 μs 106.1 μs 105.0 μs 106.0 μs 1.00 0.01 163 B 1.00
StackExchange_Ping 195.4 μs 6.79 μs 0.37 μs 194.92 μs 195.6 μs 195.5 μs 195.6 μs 1.86 0.02 424 B 2.60
Respire_Set_Small 107.9 μs 303.77 μs 16.65 μs 89.28 μs 121.4 μs 112.9 μs 120.6 μs 1.03 0.14 167 B 1.02
StackExchange_Set_Small 198.1 μs 19.08 μs 1.05 μs 197.07 μs 199.2 μs 198.0 μs 199.0 μs 1.89 0.02 430 B 2.64
Respire_Set_Medium 103.9 μs 123.35 μs 6.76 μs 96.10 μs 108.2 μs 107.3 μs 108.2 μs 0.99 0.06 164 B 1.01
StackExchange_Set_Medium 200.3 μs 34.06 μs 1.87 μs 198.14 μs 201.5 μs 201.2 μs 201.5 μs 1.91 0.02 432 B 2.65
Respire_Set_Large 132.0 μs 86.40 μs 4.74 μs 128.45 μs 137.4 μs 130.2 μs 136.7 μs 1.26 0.04 173 B 1.06
StackExchange_Set_Large 217.2 μs 130.58 μs 7.16 μs 212.78 μs 225.5 μs 213.4 μs 224.3 μs 2.07 0.06 432 B 2.65
Respire_Get 116.2 μs 41.52 μs 2.28 μs 114.10 μs 118.6 μs 116.0 μs 118.4 μs 1.11 0.02 213 B 1.31
StackExchange_Get 196.8 μs 12.04 μs 0.66 μs 196.07 μs 197.3 μs 197.1 μs 197.3 μs 1.87 0.02 504 B 3.09
StackExchange_MGet 209.0 μs 136.09 μs 7.46 μs 203.04 μs 217.4 μs 206.6 μs 216.3 μs 1.99 0.06 896 B 5.50
StackExchange_Pipeline_5_Sets 224.4 μs 355.59 μs 19.49 μs 213.11 μs 246.9 μs 213.2 μs 243.6 μs 2.14 0.16 2967 B 18.20
Respire_Exists 116.5 μs 92.72 μs 5.08 μs 112.29 μs 122.1 μs 115.0 μs 121.4 μs 1.11 0.04 167 B 1.02
StackExchange_Exists 192.3 μs 20.93 μs 1.15 μs 191.31 μs 193.5 μs 191.9 μs 193.4 μs 1.83 0.02 399 B 2.45
Respire_Del 221.3 μs 112.41 μs 6.16 μs 215.78 μs 228.0 μs 220.2 μs 227.2 μs 2.11 0.05 197 B 1.21
StackExchange_Del 376.5 μs 7.04 μs 0.39 μs 376.17 μs 376.9 μs 376.5 μs 376.9 μs 3.58 0.03 664 B 4.07
Respire_Incr 116.3 μs 106.24 μs 5.82 μs 112.92 μs 123.0 μs 112.9 μs 122.0 μs 1.11 0.05 166 B 1.02
StackExchange_Incr 195.6 μs 15.11 μs 0.83 μs 194.68 μs 196.2 μs 196.0 μs 196.2 μs 1.86 0.02 400 B 2.45
View raw benchmark output

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
AMD EPYC 9V74 2.86GHz, 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.1 μs |  19.15 μs |  1.05 μs | 104.02 μs | 106.1 μs | 105.0 μs | 106.0 μs |  1.00 |    0.01 |     163 B |        1.00 |
StackExchange_Ping            | 195.4 μs |   6.79 μs |  0.37 μs | 194.92 μs | 195.6 μs | 195.5 μs | 195.6 μs |  1.86 |    0.02 |     424 B |        2.60 |
Respire_Set_Small             | 107.9 μs | 303.77 μs | 16.65 μs |  89.28 μs | 121.4 μs | 112.9 μs | 120.6 μs |  1.03 |    0.14 |     167 B |        1.02 |
StackExchange_Set_Small       | 198.1 μs |  19.08 μs |  1.05 μs | 197.07 μs | 199.2 μs | 198.0 μs | 199.0 μs |  1.89 |    0.02 |     430 B |        2.64 |
Respire_Set_Medium            | 103.9 μs | 123.35 μs |  6.76 μs |  96.10 μs | 108.2 μs | 107.3 μs | 108.2 μs |  0.99 |    0.06 |     164 B |        1.01 |
StackExchange_Set_Medium      | 200.3 μs |  34.06 μs |  1.87 μs | 198.14 μs | 201.5 μs | 201.2 μs | 201.5 μs |  1.91 |    0.02 |     432 B |        2.65 |
Respire_Set_Large             | 132.0 μs |  86.40 μs |  4.74 μs | 128.45 μs | 137.4 μs | 130.2 μs | 136.7 μs |  1.26 |    0.04 |     173 B |        1.06 |
StackExchange_Set_Large       | 217.2 μs | 130.58 μs |  7.16 μs | 212.78 μs | 225.5 μs | 213.4 μs | 224.3 μs |  2.07 |    0.06 |     432 B |        2.65 |
Respire_Get                   | 116.2 μs |  41.52 μs |  2.28 μs | 114.10 μs | 118.6 μs | 116.0 μs | 118.4 μs |  1.11 |    0.02 |     213 B |        1.31 |
StackExchange_Get             | 196.8 μs |  12.04 μs |  0.66 μs | 196.07 μs | 197.3 μs | 197.1 μs | 197.3 μs |  1.87 |    0.02 |     504 B |        3.09 |
StackExchange_MGet            | 209.0 μs | 136.09 μs |  7.46 μs | 203.04 μs | 217.4 μs | 206.6 μs | 216.3 μs |  1.99 |    0.06 |     896 B |        5.50 |
StackExchange_Pipeline_5_Sets | 224.4 μs | 355.59 μs | 19.49 μs | 213.11 μs | 246.9 μs | 213.2 μs | 243.6 μs |  2.14 |    0.16 |    2967 B |       18.20 |
Respire_Exists                | 116.5 μs |  92.72 μs |  5.08 μs | 112.29 μs | 122.1 μs | 115.0 μs | 121.4 μs |  1.11 |    0.04 |     167 B |        1.02 |
StackExchange_Exists          | 192.3 μs |  20.93 μs |  1.15 μs | 191.31 μs | 193.5 μs | 191.9 μs | 193.4 μs |  1.83 |    0.02 |     399 B |        2.45 |
Respire_Del                   | 221.3 μs | 112.41 μs |  6.16 μs | 215.78 μs | 228.0 μs | 220.2 μs | 227.2 μs |  2.11 |    0.05 |     197 B |        1.21 |
StackExchange_Del             | 376.5 μs |   7.04 μs |  0.39 μs | 376.17 μs | 376.9 μs | 376.5 μs | 376.9 μs |  3.58 |    0.03 |     664 B |        4.07 |
Respire_Incr                  | 116.3 μs | 106.24 μs |  5.82 μs | 112.92 μs | 123.0 μs | 112.9 μs | 122.0 μs |  1.11 |    0.05 |     166 B |        1.02 |
StackExchange_Incr            | 195.6 μs |  15.11 μs |  0.83 μs | 194.68 μs | 196.2 μs | 196.0 μs | 196.2 μs |  1.86 |    0.02 |     400 B |        2.45 |

@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 185.1 μs 111.18 μs 6.09 μs 178.1 μs 189.3 μs 187.9 μs 189.2 μs 1.00 0.04 184 B 1.00
StackExchange_Ping 246.1 μs 27.33 μs 1.50 μs 245.2 μs 247.8 μs 245.3 μs 247.6 μs 1.33 0.04 424 B 2.30
Respire_Set_Small 191.8 μs 23.56 μs 1.29 μs 190.6 μs 193.2 μs 191.6 μs 193.0 μs 1.04 0.03 176 B 0.96
StackExchange_Set_Small 247.0 μs 18.00 μs 0.99 μs 245.9 μs 247.9 μs 247.3 μs 247.8 μs 1.34 0.04 432 B 2.35
Respire_Set_Medium 192.4 μs 19.15 μs 1.05 μs 191.2 μs 193.2 μs 192.9 μs 193.1 μs 1.04 0.03 176 B 0.96
StackExchange_Set_Medium 250.0 μs 36.02 μs 1.97 μs 248.4 μs 252.2 μs 249.3 μs 251.9 μs 1.35 0.04 432 B 2.35
Respire_Set_Large 200.9 μs 4.89 μs 0.27 μs 200.7 μs 201.2 μs 200.7 μs 201.1 μs 1.09 0.03 176 B 0.96
StackExchange_Set_Large 256.6 μs 20.16 μs 1.11 μs 255.9 μs 257.9 μs 256.1 μs 257.7 μs 1.39 0.04 432 B 2.35
Respire_Get 190.7 μs 35.41 μs 1.94 μs 188.8 μs 192.7 μs 190.6 μs 192.4 μs 1.03 0.03 224 B 1.22
StackExchange_Get 248.8 μs 30.87 μs 1.69 μs 247.1 μs 250.5 μs 248.6 μs 250.3 μs 1.34 0.04 504 B 2.74
StackExchange_MGet 251.2 μs 31.03 μs 1.70 μs 249.3 μs 252.6 μs 251.6 μs 252.5 μs 1.36 0.04 896 B 4.87
StackExchange_Pipeline_5_Sets 262.1 μs 21.89 μs 1.20 μs 261.4 μs 263.5 μs 261.5 μs 263.3 μs 1.42 0.04 2968 B 16.13
Respire_Exists 189.0 μs 17.14 μs 0.94 μs 188.0 μs 189.9 μs 189.2 μs 189.8 μs 1.02 0.03 176 B 0.96
StackExchange_Exists 244.8 μs 17.91 μs 0.98 μs 243.9 μs 245.8 μs 244.9 μs 245.7 μs 1.32 0.04 400 B 2.17
Respire_Del 364.7 μs 65.59 μs 3.59 μs 360.8 μs 367.8 μs 365.6 μs 367.6 μs 1.97 0.06 200 B 1.09
StackExchange_Del 477.9 μs 133.90 μs 7.34 μs 472.8 μs 486.3 μs 474.6 μs 485.2 μs 2.58 0.08 664 B 3.61
Respire_Incr 191.0 μs 4.89 μs 0.27 μs 190.8 μs 191.3 μs 190.8 μs 191.2 μs 1.03 0.03 184 B 1.00
StackExchange_Incr 248.5 μs 20.94 μs 1.15 μs 247.4 μs 249.7 μs 248.4 μs 249.6 μs 1.34 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                  | 185.1 μs | 111.18 μs | 6.09 μs | 178.1 μs | 189.3 μs | 187.9 μs | 189.2 μs |  1.00 |    0.04 |     184 B |        1.00 |
StackExchange_Ping            | 246.1 μs |  27.33 μs | 1.50 μs | 245.2 μs | 247.8 μs | 245.3 μs | 247.6 μs |  1.33 |    0.04 |     424 B |        2.30 |
Respire_Set_Small             | 191.8 μs |  23.56 μs | 1.29 μs | 190.6 μs | 193.2 μs | 191.6 μs | 193.0 μs |  1.04 |    0.03 |     176 B |        0.96 |
StackExchange_Set_Small       | 247.0 μs |  18.00 μs | 0.99 μs | 245.9 μs | 247.9 μs | 247.3 μs | 247.8 μs |  1.34 |    0.04 |     432 B |        2.35 |
Respire_Set_Medium            | 192.4 μs |  19.15 μs | 1.05 μs | 191.2 μs | 193.2 μs | 192.9 μs | 193.1 μs |  1.04 |    0.03 |     176 B |        0.96 |
StackExchange_Set_Medium      | 250.0 μs |  36.02 μs | 1.97 μs | 248.4 μs | 252.2 μs | 249.3 μs | 251.9 μs |  1.35 |    0.04 |     432 B |        2.35 |
Respire_Set_Large             | 200.9 μs |   4.89 μs | 0.27 μs | 200.7 μs | 201.2 μs | 200.7 μs | 201.1 μs |  1.09 |    0.03 |     176 B |        0.96 |
StackExchange_Set_Large       | 256.6 μs |  20.16 μs | 1.11 μs | 255.9 μs | 257.9 μs | 256.1 μs | 257.7 μs |  1.39 |    0.04 |     432 B |        2.35 |
Respire_Get                   | 190.7 μs |  35.41 μs | 1.94 μs | 188.8 μs | 192.7 μs | 190.6 μs | 192.4 μs |  1.03 |    0.03 |     224 B |        1.22 |
StackExchange_Get             | 248.8 μs |  30.87 μs | 1.69 μs | 247.1 μs | 250.5 μs | 248.6 μs | 250.3 μs |  1.34 |    0.04 |     504 B |        2.74 |
StackExchange_MGet            | 251.2 μs |  31.03 μs | 1.70 μs | 249.3 μs | 252.6 μs | 251.6 μs | 252.5 μs |  1.36 |    0.04 |     896 B |        4.87 |
StackExchange_Pipeline_5_Sets | 262.1 μs |  21.89 μs | 1.20 μs | 261.4 μs | 263.5 μs | 261.5 μs | 263.3 μs |  1.42 |    0.04 |    2968 B |       16.13 |
Respire_Exists                | 189.0 μs |  17.14 μs | 0.94 μs | 188.0 μs | 189.9 μs | 189.2 μs | 189.8 μs |  1.02 |    0.03 |     176 B |        0.96 |
StackExchange_Exists          | 244.8 μs |  17.91 μs | 0.98 μs | 243.9 μs | 245.8 μs | 244.9 μs | 245.7 μs |  1.32 |    0.04 |     400 B |        2.17 |
Respire_Del                   | 364.7 μs |  65.59 μs | 3.59 μs | 360.8 μs | 367.8 μs | 365.6 μs | 367.6 μs |  1.97 |    0.06 |     200 B |        1.09 |
StackExchange_Del             | 477.9 μs | 133.90 μs | 7.34 μs | 472.8 μs | 486.3 μs | 474.6 μs | 485.2 μs |  2.58 |    0.08 |     664 B |        3.61 |
Respire_Incr                  | 191.0 μs |   4.89 μs | 0.27 μs | 190.8 μs | 191.3 μs | 190.8 μs | 191.2 μs |  1.03 |    0.03 |     184 B |        1.00 |
StackExchange_Incr            | 248.5 μs |  20.94 μs | 1.15 μs | 247.4 μs | 249.7 μs | 248.4 μs | 249.6 μs |  1.34 |    0.04 |     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: 42919cc1be

ℹ️ 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/PubSub/SubscriptionHub.cs Outdated
Comment on lines +288 to +291
lock (_reconnectStateGate)
{
reconnectGeneration = ++_reconnectGeneration;
core.NotifySubscriptionStateChanged(RespireConnectionState.Reconnecting);

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 Suppress reconnect events once disposal begins

If the subscriber closes and this watcher passes the _disposed check just before DisposeAsync sets the flag, disposal can complete before the watcher enters this block; the fire-and-forget watcher then publishes Reconnecting, exits at the subsequent while (!_disposed) check, and never publishes Connected. A health listener can therefore receive a permanent reconnecting state, potentially even after DisposeAsync has returned. Synchronize disposal with this transition or revalidate disposal while publishing it.

Useful? React with 👍 / 👎.

Synchronize disposal with reconnect state transitions so background watchers cannot publish after hub teardown.
@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:20:09.246Z

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:20:25.775Z

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-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 86.11 μs 43.00 μs 2.357 μs 83.39 μs 87.59 μs 87.34 μs 87.56 μs 1.00 0.03 149 B 1.00
StackExchange_Ping 79.59 μs 101.25 μs 5.550 μs 73.31 μs 83.84 μs 81.62 μs 83.62 μs 0.92 0.06 382 B 2.56
Respire_Set_Small 80.60 μs 140.37 μs 7.694 μs 72.08 μs 87.06 μs 82.65 μs 86.62 μs 0.94 0.08 165 B 1.11
StackExchange_Set_Small 80.43 μs 112.60 μs 6.172 μs 74.66 μs 86.94 μs 79.71 μs 86.21 μs 0.93 0.07 397 B 2.66
Respire_Set_Medium 83.38 μs 71.47 μs 3.917 μs 79.55 μs 87.38 μs 83.22 μs 86.97 μs 0.97 0.05 167 B 1.12
StackExchange_Set_Medium 84.95 μs 276.97 μs 15.182 μs 68.82 μs 98.96 μs 87.08 μs 97.77 μs 0.99 0.15 395 B 2.65
Respire_Set_Large 98.77 μs 107.06 μs 5.868 μs 92.65 μs 104.35 μs 99.29 μs 103.85 μs 1.15 0.07 159 B 1.07
StackExchange_Set_Large 116.12 μs 17.96 μs 0.984 μs 115.02 μs 116.91 μs 116.44 μs 116.86 μs 1.35 0.03 411 B 2.76
Respire_Get 74.95 μs 112.63 μs 6.174 μs 68.55 μs 80.87 μs 75.43 μs 80.32 μs 0.87 0.07 198 B 1.33
StackExchange_Get 66.40 μs 149.83 μs 8.213 μs 61.13 μs 75.86 μs 62.20 μs 74.49 μs 0.77 0.08 370 B 2.48
StackExchange_MGet 103.62 μs 164.31 μs 9.006 μs 95.93 μs 113.53 μs 101.41 μs 112.32 μs 1.20 0.10 647 B 4.34
StackExchange_Pipeline_5_Sets 122.70 μs 42.41 μs 2.325 μs 120.25 μs 124.87 μs 122.99 μs 124.68 μs 1.43 0.04 2871 B 19.27
Respire_Exists 73.41 μs 30.30 μs 1.661 μs 71.74 μs 75.06 μs 73.44 μs 74.89 μs 0.85 0.03 159 B 1.07
StackExchange_Exists 81.83 μs 60.70 μs 3.327 μs 78.64 μs 85.28 μs 81.56 μs 84.91 μs 0.95 0.04 381 B 2.56
Respire_Del 151.18 μs 56.86 μs 3.117 μs 149.04 μs 154.75 μs 149.73 μs 154.25 μs 1.76 0.05 194 B 1.30
StackExchange_Del 174.03 μs 93.99 μs 5.152 μs 168.44 μs 178.59 μs 175.05 μs 178.23 μs 2.02 0.07 640 B 4.30
Respire_Incr 72.12 μs 22.50 μs 1.234 μs 71.02 μs 73.45 μs 71.88 μs 73.30 μs 0.84 0.02 158 B 1.06
StackExchange_Incr 77.59 μs 50.56 μs 2.772 μs 74.68 μs 80.20 μs 77.90 μs 79.97 μs 0.90 0.04 383 B 2.57
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-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                  |  86.11 μs |  43.00 μs |  2.357 μs |  83.39 μs |  87.59 μs |  87.34 μs |  87.56 μs |  1.00 |    0.03 |     149 B |        1.00 |
StackExchange_Ping            |  79.59 μs | 101.25 μs |  5.550 μs |  73.31 μs |  83.84 μs |  81.62 μs |  83.62 μs |  0.92 |    0.06 |     382 B |        2.56 |
Respire_Set_Small             |  80.60 μs | 140.37 μs |  7.694 μs |  72.08 μs |  87.06 μs |  82.65 μs |  86.62 μs |  0.94 |    0.08 |     165 B |        1.11 |
StackExchange_Set_Small       |  80.43 μs | 112.60 μs |  6.172 μs |  74.66 μs |  86.94 μs |  79.71 μs |  86.21 μs |  0.93 |    0.07 |     397 B |        2.66 |
Respire_Set_Medium            |  83.38 μs |  71.47 μs |  3.917 μs |  79.55 μs |  87.38 μs |  83.22 μs |  86.97 μs |  0.97 |    0.05 |     167 B |        1.12 |
StackExchange_Set_Medium      |  84.95 μs | 276.97 μs | 15.182 μs |  68.82 μs |  98.96 μs |  87.08 μs |  97.77 μs |  0.99 |    0.15 |     395 B |        2.65 |
Respire_Set_Large             |  98.77 μs | 107.06 μs |  5.868 μs |  92.65 μs | 104.35 μs |  99.29 μs | 103.85 μs |  1.15 |    0.07 |     159 B |        1.07 |
StackExchange_Set_Large       | 116.12 μs |  17.96 μs |  0.984 μs | 115.02 μs | 116.91 μs | 116.44 μs | 116.86 μs |  1.35 |    0.03 |     411 B |        2.76 |
Respire_Get                   |  74.95 μs | 112.63 μs |  6.174 μs |  68.55 μs |  80.87 μs |  75.43 μs |  80.32 μs |  0.87 |    0.07 |     198 B |        1.33 |
StackExchange_Get             |  66.40 μs | 149.83 μs |  8.213 μs |  61.13 μs |  75.86 μs |  62.20 μs |  74.49 μs |  0.77 |    0.08 |     370 B |        2.48 |
StackExchange_MGet            | 103.62 μs | 164.31 μs |  9.006 μs |  95.93 μs | 113.53 μs | 101.41 μs | 112.32 μs |  1.20 |    0.10 |     647 B |        4.34 |
StackExchange_Pipeline_5_Sets | 122.70 μs |  42.41 μs |  2.325 μs | 120.25 μs | 124.87 μs | 122.99 μs | 124.68 μs |  1.43 |    0.04 |    2871 B |       19.27 |
Respire_Exists                |  73.41 μs |  30.30 μs |  1.661 μs |  71.74 μs |  75.06 μs |  73.44 μs |  74.89 μs |  0.85 |    0.03 |     159 B |        1.07 |
StackExchange_Exists          |  81.83 μs |  60.70 μs |  3.327 μs |  78.64 μs |  85.28 μs |  81.56 μs |  84.91 μs |  0.95 |    0.04 |     381 B |        2.56 |
Respire_Del                   | 151.18 μs |  56.86 μs |  3.117 μs | 149.04 μs | 154.75 μs | 149.73 μs | 154.25 μs |  1.76 |    0.05 |     194 B |        1.30 |
StackExchange_Del             | 174.03 μs |  93.99 μs |  5.152 μs | 168.44 μs | 178.59 μs | 175.05 μs | 178.23 μs |  2.02 |    0.07 |     640 B |        4.30 |
Respire_Incr                  |  72.12 μs |  22.50 μs |  1.234 μs |  71.02 μs |  73.45 μs |  71.88 μs |  73.30 μs |  0.84 |    0.02 |     158 B |        1.06 |
StackExchange_Incr            |  77.59 μs |  50.56 μs |  2.772 μs |  74.68 μs |  80.20 μs |  77.90 μs |  79.97 μs |  0.90 |    0.04 |     383 B |        2.57 |

@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 175.5 μs 14.87 μs 0.81 μs 175.0 μs 176.4 μs 175.1 μs 176.3 μs 1.00 0.01 184 B 1.00
StackExchange_Ping 176.1 μs 28.03 μs 1.54 μs 174.5 μs 177.5 μs 176.3 μs 177.4 μs 1.00 0.01 408 B 2.22
Respire_Set_Small 177.8 μs 4.30 μs 0.24 μs 177.6 μs 178.0 μs 177.6 μs 178.0 μs 1.01 0.00 176 B 0.96
StackExchange_Set_Small 178.5 μs 23.28 μs 1.28 μs 177.0 μs 179.4 μs 179.1 μs 179.3 μs 1.02 0.01 415 B 2.26
Respire_Set_Medium 177.7 μs 13.79 μs 0.76 μs 177.1 μs 178.6 μs 177.6 μs 178.5 μs 1.01 0.01 176 B 0.96
StackExchange_Set_Medium 180.2 μs 12.63 μs 0.69 μs 179.7 μs 181.0 μs 179.9 μs 180.9 μs 1.03 0.01 416 B 2.26
Respire_Set_Large 186.4 μs 3.64 μs 0.20 μs 186.2 μs 186.6 μs 186.5 μs 186.6 μs 1.06 0.00 176 B 0.96
StackExchange_Set_Large 189.8 μs 24.32 μs 1.33 μs 188.3 μs 190.9 μs 190.2 μs 190.8 μs 1.08 0.01 416 B 2.26
Respire_Get 176.6 μs 37.36 μs 2.05 μs 174.7 μs 178.7 μs 176.3 μs 178.5 μs 1.01 0.01 224 B 1.22
StackExchange_Get 177.9 μs 44.42 μs 2.43 μs 175.6 μs 180.5 μs 177.7 μs 180.2 μs 1.01 0.01 424 B 2.30
StackExchange_MGet 182.0 μs 40.79 μs 2.24 μs 179.4 μs 183.4 μs 183.2 μs 183.4 μs 1.04 0.01 656 B 3.57
StackExchange_Pipeline_5_Sets 211.7 μs 277.98 μs 15.24 μs 200.6 μs 229.0 μs 205.4 μs 226.7 μs 1.21 0.08 2886 B 15.68
Respire_Exists 175.7 μs 15.50 μs 0.85 μs 174.8 μs 176.5 μs 175.8 μs 176.5 μs 1.00 0.01 175 B 0.95
StackExchange_Exists 178.9 μs 11.89 μs 0.65 μs 178.4 μs 179.6 μs 178.6 μs 179.5 μs 1.02 0.01 400 B 2.17
Respire_Del 332.6 μs 20.58 μs 1.13 μs 331.8 μs 333.9 μs 332.0 μs 333.7 μs 1.89 0.01 200 B 1.09
StackExchange_Del 337.0 μs 20.56 μs 1.13 μs 336.3 μs 338.3 μs 336.4 μs 338.1 μs 1.92 0.01 648 B 3.52
Respire_Incr 176.0 μs 10.24 μs 0.56 μs 175.6 μs 176.7 μs 175.8 μs 176.6 μs 1.00 0.00 184 B 1.00
StackExchange_Incr 178.3 μs 4.44 μs 0.24 μs 178.0 μs 178.5 μs 178.2 μs 178.5 μs 1.02 0.00 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                  | 175.5 μs |  14.87 μs |  0.81 μs | 175.0 μs | 176.4 μs | 175.1 μs | 176.3 μs |  1.00 |    0.01 |     184 B |        1.00 |
StackExchange_Ping            | 176.1 μs |  28.03 μs |  1.54 μs | 174.5 μs | 177.5 μs | 176.3 μs | 177.4 μs |  1.00 |    0.01 |     408 B |        2.22 |
Respire_Set_Small             | 177.8 μs |   4.30 μs |  0.24 μs | 177.6 μs | 178.0 μs | 177.6 μs | 178.0 μs |  1.01 |    0.00 |     176 B |        0.96 |
StackExchange_Set_Small       | 178.5 μs |  23.28 μs |  1.28 μs | 177.0 μs | 179.4 μs | 179.1 μs | 179.3 μs |  1.02 |    0.01 |     415 B |        2.26 |
Respire_Set_Medium            | 177.7 μs |  13.79 μs |  0.76 μs | 177.1 μs | 178.6 μs | 177.6 μs | 178.5 μs |  1.01 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Medium      | 180.2 μs |  12.63 μs |  0.69 μs | 179.7 μs | 181.0 μs | 179.9 μs | 180.9 μs |  1.03 |    0.01 |     416 B |        2.26 |
Respire_Set_Large             | 186.4 μs |   3.64 μs |  0.20 μs | 186.2 μs | 186.6 μs | 186.5 μs | 186.6 μs |  1.06 |    0.00 |     176 B |        0.96 |
StackExchange_Set_Large       | 189.8 μs |  24.32 μs |  1.33 μs | 188.3 μs | 190.9 μs | 190.2 μs | 190.8 μs |  1.08 |    0.01 |     416 B |        2.26 |
Respire_Get                   | 176.6 μs |  37.36 μs |  2.05 μs | 174.7 μs | 178.7 μs | 176.3 μs | 178.5 μs |  1.01 |    0.01 |     224 B |        1.22 |
StackExchange_Get             | 177.9 μs |  44.42 μs |  2.43 μs | 175.6 μs | 180.5 μs | 177.7 μs | 180.2 μs |  1.01 |    0.01 |     424 B |        2.30 |
StackExchange_MGet            | 182.0 μs |  40.79 μs |  2.24 μs | 179.4 μs | 183.4 μs | 183.2 μs | 183.4 μs |  1.04 |    0.01 |     656 B |        3.57 |
StackExchange_Pipeline_5_Sets | 211.7 μs | 277.98 μs | 15.24 μs | 200.6 μs | 229.0 μs | 205.4 μs | 226.7 μs |  1.21 |    0.08 |    2886 B |       15.68 |
Respire_Exists                | 175.7 μs |  15.50 μs |  0.85 μs | 174.8 μs | 176.5 μs | 175.8 μs | 176.5 μs |  1.00 |    0.01 |     175 B |        0.95 |
StackExchange_Exists          | 178.9 μs |  11.89 μs |  0.65 μs | 178.4 μs | 179.6 μs | 178.6 μs | 179.5 μs |  1.02 |    0.01 |     400 B |        2.17 |
Respire_Del                   | 332.6 μs |  20.58 μs |  1.13 μs | 331.8 μs | 333.9 μs | 332.0 μs | 333.7 μs |  1.89 |    0.01 |     200 B |        1.09 |
StackExchange_Del             | 337.0 μs |  20.56 μs |  1.13 μs | 336.3 μs | 338.3 μs | 336.4 μs | 338.1 μs |  1.92 |    0.01 |     648 B |        3.52 |
Respire_Incr                  | 176.0 μs |  10.24 μs |  0.56 μs | 175.6 μs | 176.7 μs | 175.8 μs | 176.6 μs |  1.00 |    0.00 |     184 B |        1.00 |
StackExchange_Incr            | 178.3 μs |   4.44 μs |  0.24 μs | 178.0 μs | 178.5 μs | 178.2 μs | 178.5 μs |  1.02 |    0.00 |     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: 5cea61f818

ℹ️ 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/PubSub/SubscriptionHub.cs Outdated
Comment on lines +295 to +296
reconnectGeneration = ++_reconnectGeneration;
core.NotifySubscriptionStateChanged(RespireConnectionState.Reconnecting);

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 Invoke state handlers after releasing the reconnect gate

When a ConnectionStateChanged handler acquires an application lock that is already held by a thread concurrently disposing the client, this call executes that handler while _reconnectStateGate is held, while SubscriptionHub.DisposeAsync waits for the same gate; the handler and disposer can therefore deadlock. The Connected notification below has the same issue. Record/enqueue the transition under this gate, but drain the public callbacks only after releasing it.

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-09T12:39:05.984Z

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:39:15.108Z

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 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 74.93 μs 101.445 μs 5.561 μs 69.85 μs 80.87 μs 74.08 μs 80.19 μs 1.00 0.09 160 B 1.00
StackExchange_Ping 76.63 μs 7.891 μs 0.433 μs 76.22 μs 77.09 μs 76.59 μs 77.04 μs 1.03 0.07 365 B 2.28
Respire_Set_Small 76.57 μs 63.990 μs 3.508 μs 72.52 μs 78.70 μs 78.48 μs 78.68 μs 1.03 0.08 141 B 0.88
StackExchange_Set_Small 76.81 μs 14.439 μs 0.791 μs 76.29 μs 77.72 μs 76.41 μs 77.59 μs 1.03 0.07 387 B 2.42
Respire_Set_Medium 72.50 μs 68.421 μs 3.750 μs 69.05 μs 76.49 μs 71.95 μs 76.04 μs 0.97 0.08 129 B 0.81
StackExchange_Set_Medium 75.78 μs 110.874 μs 6.077 μs 71.28 μs 82.69 μs 73.37 μs 81.76 μs 1.01 0.10 389 B 2.43
Respire_Set_Large 97.08 μs 52.508 μs 2.878 μs 93.76 μs 98.88 μs 98.59 μs 98.85 μs 1.30 0.09 135 B 0.84
StackExchange_Set_Large 105.18 μs 136.170 μs 7.464 μs 97.73 μs 112.65 μs 105.17 μs 111.91 μs 1.41 0.12 412 B 2.58
Respire_Get 77.73 μs 49.017 μs 2.687 μs 74.68 μs 79.74 μs 78.76 μs 79.64 μs 1.04 0.07 200 B 1.25
StackExchange_Get 83.39 μs 57.204 μs 3.136 μs 79.90 μs 85.98 μs 84.28 μs 85.81 μs 1.12 0.08 390 B 2.44
StackExchange_MGet 80.81 μs 68.562 μs 3.758 μs 76.73 μs 84.13 μs 81.57 μs 83.87 μs 1.08 0.08 622 B 3.89
StackExchange_Pipeline_5_Sets 125.05 μs 205.440 μs 11.261 μs 117.13 μs 137.94 μs 120.09 μs 136.16 μs 1.67 0.17 2888 B 18.05
Respire_Exists 75.15 μs 67.815 μs 3.717 μs 71.70 μs 79.09 μs 74.66 μs 78.65 μs 1.01 0.08 151 B 0.94
StackExchange_Exists 75.85 μs 37.333 μs 2.046 μs 74.19 μs 78.14 μs 75.23 μs 77.85 μs 1.02 0.07 368 B 2.30
Respire_Del 153.61 μs 80.232 μs 4.398 μs 149.50 μs 158.25 μs 153.07 μs 157.73 μs 2.06 0.14 193 B 1.21
StackExchange_Del 157.58 μs 58.162 μs 3.188 μs 155.66 μs 161.26 μs 155.82 μs 160.71 μs 2.11 0.14 645 B 4.03
Respire_Incr 73.60 μs 37.045 μs 2.031 μs 71.29 μs 75.09 μs 74.41 μs 75.03 μs 0.99 0.07 141 B 0.88
StackExchange_Incr 69.08 μs 128.354 μs 7.036 μs 64.16 μs 77.14 μs 65.95 μs 76.02 μs 0.93 0.10 377 B 2.36
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                  |  74.93 μs | 101.445 μs |  5.561 μs |  69.85 μs |  80.87 μs |  74.08 μs |  80.19 μs |  1.00 |    0.09 |     160 B |        1.00 |
StackExchange_Ping            |  76.63 μs |   7.891 μs |  0.433 μs |  76.22 μs |  77.09 μs |  76.59 μs |  77.04 μs |  1.03 |    0.07 |     365 B |        2.28 |
Respire_Set_Small             |  76.57 μs |  63.990 μs |  3.508 μs |  72.52 μs |  78.70 μs |  78.48 μs |  78.68 μs |  1.03 |    0.08 |     141 B |        0.88 |
StackExchange_Set_Small       |  76.81 μs |  14.439 μs |  0.791 μs |  76.29 μs |  77.72 μs |  76.41 μs |  77.59 μs |  1.03 |    0.07 |     387 B |        2.42 |
Respire_Set_Medium            |  72.50 μs |  68.421 μs |  3.750 μs |  69.05 μs |  76.49 μs |  71.95 μs |  76.04 μs |  0.97 |    0.08 |     129 B |        0.81 |
StackExchange_Set_Medium      |  75.78 μs | 110.874 μs |  6.077 μs |  71.28 μs |  82.69 μs |  73.37 μs |  81.76 μs |  1.01 |    0.10 |     389 B |        2.43 |
Respire_Set_Large             |  97.08 μs |  52.508 μs |  2.878 μs |  93.76 μs |  98.88 μs |  98.59 μs |  98.85 μs |  1.30 |    0.09 |     135 B |        0.84 |
StackExchange_Set_Large       | 105.18 μs | 136.170 μs |  7.464 μs |  97.73 μs | 112.65 μs | 105.17 μs | 111.91 μs |  1.41 |    0.12 |     412 B |        2.58 |
Respire_Get                   |  77.73 μs |  49.017 μs |  2.687 μs |  74.68 μs |  79.74 μs |  78.76 μs |  79.64 μs |  1.04 |    0.07 |     200 B |        1.25 |
StackExchange_Get             |  83.39 μs |  57.204 μs |  3.136 μs |  79.90 μs |  85.98 μs |  84.28 μs |  85.81 μs |  1.12 |    0.08 |     390 B |        2.44 |
StackExchange_MGet            |  80.81 μs |  68.562 μs |  3.758 μs |  76.73 μs |  84.13 μs |  81.57 μs |  83.87 μs |  1.08 |    0.08 |     622 B |        3.89 |
StackExchange_Pipeline_5_Sets | 125.05 μs | 205.440 μs | 11.261 μs | 117.13 μs | 137.94 μs | 120.09 μs | 136.16 μs |  1.67 |    0.17 |    2888 B |       18.05 |
Respire_Exists                |  75.15 μs |  67.815 μs |  3.717 μs |  71.70 μs |  79.09 μs |  74.66 μs |  78.65 μs |  1.01 |    0.08 |     151 B |        0.94 |
StackExchange_Exists          |  75.85 μs |  37.333 μs |  2.046 μs |  74.19 μs |  78.14 μs |  75.23 μs |  77.85 μs |  1.02 |    0.07 |     368 B |        2.30 |
Respire_Del                   | 153.61 μs |  80.232 μs |  4.398 μs | 149.50 μs | 158.25 μs | 153.07 μs | 157.73 μs |  2.06 |    0.14 |     193 B |        1.21 |
StackExchange_Del             | 157.58 μs |  58.162 μs |  3.188 μs | 155.66 μs | 161.26 μs | 155.82 μs | 160.71 μs |  2.11 |    0.14 |     645 B |        4.03 |
Respire_Incr                  |  73.60 μs |  37.045 μs |  2.031 μs |  71.29 μs |  75.09 μs |  74.41 μs |  75.03 μs |  0.99 |    0.07 |     141 B |        0.88 |
StackExchange_Incr            |  69.08 μs | 128.354 μs |  7.036 μs |  64.16 μs |  77.14 μs |  65.95 μs |  76.02 μs |  0.93 |    0.10 |     377 B |        2.36 |

@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 186.5 μs 25.31 μs 1.39 μs 184.9 μs 187.4 μs 187.2 μs 187.4 μs 1.00 0.01 184 B 1.00
StackExchange_Ping 191.3 μs 18.32 μs 1.00 μs 190.2 μs 192.2 μs 191.6 μs 192.1 μs 1.03 0.01 408 B 2.22
Respire_Set_Small 190.3 μs 15.93 μs 0.87 μs 189.3 μs 190.9 μs 190.7 μs 190.9 μs 1.02 0.01 176 B 0.96
StackExchange_Set_Small 192.9 μs 35.81 μs 1.96 μs 190.7 μs 194.4 μs 193.7 μs 194.3 μs 1.03 0.01 416 B 2.26
Respire_Set_Medium 190.0 μs 47.21 μs 2.59 μs 187.1 μs 192.1 μs 190.7 μs 192.0 μs 1.02 0.01 176 B 0.96
StackExchange_Set_Medium 193.6 μs 61.08 μs 3.35 μs 189.7 μs 195.6 μs 195.5 μs 195.6 μs 1.04 0.02 416 B 2.26
Respire_Set_Large 200.8 μs 13.57 μs 0.74 μs 200.0 μs 201.5 μs 200.9 μs 201.4 μs 1.08 0.01 175 B 0.95
StackExchange_Set_Large 203.6 μs 28.20 μs 1.55 μs 202.0 μs 205.1 μs 203.5 μs 205.0 μs 1.09 0.01 416 B 2.26
Respire_Get 189.3 μs 15.62 μs 0.86 μs 188.4 μs 190.1 μs 189.4 μs 190.0 μs 1.02 0.01 224 B 1.22
StackExchange_Get 189.8 μs 16.77 μs 0.92 μs 188.9 μs 190.7 μs 189.8 μs 190.6 μs 1.02 0.01 424 B 2.30
StackExchange_MGet 194.5 μs 42.87 μs 2.35 μs 192.8 μs 197.2 μs 193.5 μs 196.8 μs 1.04 0.01 656 B 3.57
StackExchange_Pipeline_5_Sets 227.4 μs 268.55 μs 14.72 μs 217.8 μs 244.3 μs 220.0 μs 241.9 μs 1.22 0.07 2888 B 15.70
Respire_Exists 188.2 μs 33.30 μs 1.83 μs 186.1 μs 189.6 μs 188.8 μs 189.5 μs 1.01 0.01 176 B 0.96
StackExchange_Exists 189.7 μs 14.25 μs 0.78 μs 188.9 μs 190.5 μs 189.7 μs 190.4 μs 1.02 0.01 400 B 2.17
Respire_Del 357.8 μs 134.96 μs 7.40 μs 349.4 μs 363.0 μs 361.1 μs 362.8 μs 1.92 0.04 200 B 1.09
StackExchange_Del 367.2 μs 44.05 μs 2.41 μs 365.3 μs 369.9 μs 366.4 μs 369.6 μs 1.97 0.02 648 B 3.52
Respire_Incr 185.1 μs 32.25 μs 1.77 μs 183.1 μs 186.2 μs 186.1 μs 186.2 μs 0.99 0.01 184 B 1.00
StackExchange_Incr 186.9 μs 33.73 μs 1.85 μs 184.7 μs 188.0 μs 187.9 μs 188.0 μs 1.00 0.01 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                  | 186.5 μs |  25.31 μs |  1.39 μs | 184.9 μs | 187.4 μs | 187.2 μs | 187.4 μs |  1.00 |    0.01 |     184 B |        1.00 |
StackExchange_Ping            | 191.3 μs |  18.32 μs |  1.00 μs | 190.2 μs | 192.2 μs | 191.6 μs | 192.1 μs |  1.03 |    0.01 |     408 B |        2.22 |
Respire_Set_Small             | 190.3 μs |  15.93 μs |  0.87 μs | 189.3 μs | 190.9 μs | 190.7 μs | 190.9 μs |  1.02 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Small       | 192.9 μs |  35.81 μs |  1.96 μs | 190.7 μs | 194.4 μs | 193.7 μs | 194.3 μs |  1.03 |    0.01 |     416 B |        2.26 |
Respire_Set_Medium            | 190.0 μs |  47.21 μs |  2.59 μs | 187.1 μs | 192.1 μs | 190.7 μs | 192.0 μs |  1.02 |    0.01 |     176 B |        0.96 |
StackExchange_Set_Medium      | 193.6 μs |  61.08 μs |  3.35 μs | 189.7 μs | 195.6 μs | 195.5 μs | 195.6 μs |  1.04 |    0.02 |     416 B |        2.26 |
Respire_Set_Large             | 200.8 μs |  13.57 μs |  0.74 μs | 200.0 μs | 201.5 μs | 200.9 μs | 201.4 μs |  1.08 |    0.01 |     175 B |        0.95 |
StackExchange_Set_Large       | 203.6 μs |  28.20 μs |  1.55 μs | 202.0 μs | 205.1 μs | 203.5 μs | 205.0 μs |  1.09 |    0.01 |     416 B |        2.26 |
Respire_Get                   | 189.3 μs |  15.62 μs |  0.86 μs | 188.4 μs | 190.1 μs | 189.4 μs | 190.0 μs |  1.02 |    0.01 |     224 B |        1.22 |
StackExchange_Get             | 189.8 μs |  16.77 μs |  0.92 μs | 188.9 μs | 190.7 μs | 189.8 μs | 190.6 μs |  1.02 |    0.01 |     424 B |        2.30 |
StackExchange_MGet            | 194.5 μs |  42.87 μs |  2.35 μs | 192.8 μs | 197.2 μs | 193.5 μs | 196.8 μs |  1.04 |    0.01 |     656 B |        3.57 |
StackExchange_Pipeline_5_Sets | 227.4 μs | 268.55 μs | 14.72 μs | 217.8 μs | 244.3 μs | 220.0 μs | 241.9 μs |  1.22 |    0.07 |    2888 B |       15.70 |
Respire_Exists                | 188.2 μs |  33.30 μs |  1.83 μs | 186.1 μs | 189.6 μs | 188.8 μs | 189.5 μs |  1.01 |    0.01 |     176 B |        0.96 |
StackExchange_Exists          | 189.7 μs |  14.25 μs |  0.78 μs | 188.9 μs | 190.5 μs | 189.7 μs | 190.4 μs |  1.02 |    0.01 |     400 B |        2.17 |
Respire_Del                   | 357.8 μs | 134.96 μs |  7.40 μs | 349.4 μs | 363.0 μs | 361.1 μs | 362.8 μs |  1.92 |    0.04 |     200 B |        1.09 |
StackExchange_Del             | 367.2 μs |  44.05 μs |  2.41 μs | 365.3 μs | 369.9 μs | 366.4 μs | 369.6 μs |  1.97 |    0.02 |     648 B |        3.52 |
Respire_Incr                  | 185.1 μs |  32.25 μs |  1.77 μs | 183.1 μs | 186.2 μs | 186.1 μs | 186.2 μs |  0.99 |    0.01 |     184 B |        1.00 |
StackExchange_Incr            | 186.9 μs |  33.73 μs |  1.85 μs | 184.7 μs | 188.0 μs | 187.9 μs | 188.0 μs |  1.00 |    0.01 |     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-09T13:17:30.864Z

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-09T13:17:42.881Z

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 3.69GHz, 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-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 79.18 μs 39.42 μs 2.161 μs 77.48 μs 81.61 μs 78.45 μs 81.30 μs 1.00 0.03 164 B 1.00
StackExchange_Ping 70.47 μs 80.72 μs 4.425 μs 65.44 μs 73.75 μs 72.22 μs 73.60 μs 0.89 0.05 389 B 2.37
Respire_Set_Small 74.19 μs 63.73 μs 3.493 μs 70.43 μs 77.33 μs 74.81 μs 77.08 μs 0.94 0.04 155 B 0.95
StackExchange_Set_Small 81.18 μs 85.81 μs 4.703 μs 76.24 μs 85.60 μs 81.72 μs 85.21 μs 1.03 0.06 390 B 2.38
Respire_Set_Medium 85.59 μs 47.60 μs 2.609 μs 82.97 μs 88.18 μs 85.63 μs 87.93 μs 1.08 0.04 153 B 0.93
StackExchange_Set_Medium 82.06 μs 94.11 μs 5.158 μs 76.26 μs 86.12 μs 83.80 μs 85.89 μs 1.04 0.06 404 B 2.46
Respire_Set_Large 92.01 μs 49.78 μs 2.728 μs 88.88 μs 93.86 μs 93.29 μs 93.80 μs 1.16 0.04 166 B 1.01
StackExchange_Set_Large 102.47 μs 55.21 μs 3.026 μs 100.35 μs 105.93 μs 101.12 μs 105.45 μs 1.29 0.04 377 B 2.30
Respire_Get 79.34 μs 59.05 μs 3.236 μs 77.08 μs 83.05 μs 77.90 μs 82.53 μs 1.00 0.04 201 B 1.23
StackExchange_Get 76.76 μs 135.86 μs 7.447 μs 68.16 μs 81.21 μs 80.90 μs 81.18 μs 0.97 0.08 412 B 2.51
StackExchange_MGet 76.62 μs 107.68 μs 5.903 μs 70.34 μs 82.06 μs 77.46 μs 81.60 μs 0.97 0.07 636 B 3.88
StackExchange_Pipeline_5_Sets 127.20 μs 204.89 μs 11.231 μs 120.14 μs 140.15 μs 121.31 μs 138.27 μs 1.61 0.13 2885 B 17.59
Respire_Exists 71.85 μs 60.36 μs 3.308 μs 68.43 μs 75.04 μs 72.08 μs 74.74 μs 0.91 0.04 155 B 0.95
StackExchange_Exists 78.40 μs 49.50 μs 2.713 μs 75.31 μs 80.41 μs 79.47 μs 80.32 μs 0.99 0.04 371 B 2.26
Respire_Del 149.96 μs 177.45 μs 9.727 μs 138.90 μs 157.20 μs 153.79 μs 156.85 μs 1.89 0.12 195 B 1.19
StackExchange_Del 155.82 μs 146.12 μs 8.009 μs 151.16 μs 165.07 μs 151.24 μs 163.69 μs 1.97 0.10 642 B 3.91
Respire_Incr 75.16 μs 58.25 μs 3.193 μs 73.26 μs 78.84 μs 73.37 μs 78.30 μs 0.95 0.04 166 B 1.01
StackExchange_Incr 76.46 μs 116.79 μs 6.402 μs 69.63 μs 82.33 μs 77.41 μs 81.84 μs 0.97 0.07 353 B 2.15
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 10.0.10 (10.0.10, 10.0.1026.32716), 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                  |  79.18 μs |  39.42 μs |  2.161 μs |  77.48 μs |  81.61 μs |  78.45 μs |  81.30 μs |  1.00 |    0.03 |     164 B |        1.00 |
StackExchange_Ping            |  70.47 μs |  80.72 μs |  4.425 μs |  65.44 μs |  73.75 μs |  72.22 μs |  73.60 μs |  0.89 |    0.05 |     389 B |        2.37 |
Respire_Set_Small             |  74.19 μs |  63.73 μs |  3.493 μs |  70.43 μs |  77.33 μs |  74.81 μs |  77.08 μs |  0.94 |    0.04 |     155 B |        0.95 |
StackExchange_Set_Small       |  81.18 μs |  85.81 μs |  4.703 μs |  76.24 μs |  85.60 μs |  81.72 μs |  85.21 μs |  1.03 |    0.06 |     390 B |        2.38 |
Respire_Set_Medium            |  85.59 μs |  47.60 μs |  2.609 μs |  82.97 μs |  88.18 μs |  85.63 μs |  87.93 μs |  1.08 |    0.04 |     153 B |        0.93 |
StackExchange_Set_Medium      |  82.06 μs |  94.11 μs |  5.158 μs |  76.26 μs |  86.12 μs |  83.80 μs |  85.89 μs |  1.04 |    0.06 |     404 B |        2.46 |
Respire_Set_Large             |  92.01 μs |  49.78 μs |  2.728 μs |  88.88 μs |  93.86 μs |  93.29 μs |  93.80 μs |  1.16 |    0.04 |     166 B |        1.01 |
StackExchange_Set_Large       | 102.47 μs |  55.21 μs |  3.026 μs | 100.35 μs | 105.93 μs | 101.12 μs | 105.45 μs |  1.29 |    0.04 |     377 B |        2.30 |
Respire_Get                   |  79.34 μs |  59.05 μs |  3.236 μs |  77.08 μs |  83.05 μs |  77.90 μs |  82.53 μs |  1.00 |    0.04 |     201 B |        1.23 |
StackExchange_Get             |  76.76 μs | 135.86 μs |  7.447 μs |  68.16 μs |  81.21 μs |  80.90 μs |  81.18 μs |  0.97 |    0.08 |     412 B |        2.51 |
StackExchange_MGet            |  76.62 μs | 107.68 μs |  5.903 μs |  70.34 μs |  82.06 μs |  77.46 μs |  81.60 μs |  0.97 |    0.07 |     636 B |        3.88 |
StackExchange_Pipeline_5_Sets | 127.20 μs | 204.89 μs | 11.231 μs | 120.14 μs | 140.15 μs | 121.31 μs | 138.27 μs |  1.61 |    0.13 |    2885 B |       17.59 |
Respire_Exists                |  71.85 μs |  60.36 μs |  3.308 μs |  68.43 μs |  75.04 μs |  72.08 μs |  74.74 μs |  0.91 |    0.04 |     155 B |        0.95 |
StackExchange_Exists          |  78.40 μs |  49.50 μs |  2.713 μs |  75.31 μs |  80.41 μs |  79.47 μs |  80.32 μs |  0.99 |    0.04 |     371 B |        2.26 |
Respire_Del                   | 149.96 μs | 177.45 μs |  9.727 μs | 138.90 μs | 157.20 μs | 153.79 μs | 156.85 μs |  1.89 |    0.12 |     195 B |        1.19 |
StackExchange_Del             | 155.82 μs | 146.12 μs |  8.009 μs | 151.16 μs | 165.07 μs | 151.24 μs | 163.69 μs |  1.97 |    0.10 |     642 B |        3.91 |
Respire_Incr                  |  75.16 μs |  58.25 μs |  3.193 μs |  73.26 μs |  78.84 μs |  73.37 μs |  78.30 μs |  0.95 |    0.04 |     166 B |        1.01 |
StackExchange_Incr            |  76.46 μs | 116.79 μs |  6.402 μs |  69.63 μs |  82.33 μs |  77.41 μs |  81.84 μs |  0.97 |    0.07 |     353 B |        2.15 |

@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

🧹 Nitpick comments (2)
src/Respire/Internal/ClientCore.cs (1)

21-21: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Rename _publishedState to reflect that it tracks the last queued state.

QueueAggregateStateLocked assigns _publishedState when it enqueues, not when PublishQueuedStates invokes handlers. The deduplication logic is correct, but the name suggests the value tracks delivered states. _lastQueuedState describes the actual role.

🤖 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` at line 21, Rename the
RespireConnectionState field _publishedState to _lastQueuedState throughout
ClientCore, including assignments and deduplication checks in
QueueAggregateStateLocked and any reads in PublishQueuedStates or related logic.
Preserve the existing state-tracking behavior.
src/Respire/PubSub/SubscriptionHub.cs (1)

343-361: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add a _disposed re-check before queuing Connected.

The Reconnecting path at lines 297-306 re-checks _disposed inside _reconnectStateGate. This Connected path does not. If DisposeAsync clears the queue just before this block acquires the gate, this watcher enqueues and publishes Connected after disposal. Today SubscriptionHub is only disposed by ClientCore.DisposeAsync, so the impact is limited to shutdown. The check keeps both paths symmetric and protects against future callers that dispose the hub independently.

♻️ Proposed symmetry fix
                 lock (_reconnectStateGate)
                 {
-                    if (reconnectGeneration == _reconnectGeneration
+                    if (!_disposed
+                        && reconnectGeneration == _reconnectGeneration
                         && ReferenceEquals(Volatile.Read(ref _connection), replacement)
                         && replacement.IsConnected)
                     {
                         publishState = QueueReconnectStateLocked(RespireConnectionState.Connected);
                     }
                 }
🤖 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/PubSub/SubscriptionHub.cs` around lines 343 - 361, Add a
`_disposed` check inside the `_reconnectStateGate` critical section in the
Connected path before calling `QueueReconnectStateLocked`, matching the existing
Reconnecting path guard. Only queue and publish `Connected` when the hub is not
disposed, the reconnect generation and connection still match, and the
replacement remains connected.
🤖 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/RespireClient.cs`:
- Around line 76-81: Update the XML summary for
RespireClient.ConnectionStateChanged to document that it reflects aggregate
state across all command slots and the dedicated subscriber connection, with
Connected published only after every reconnecting component has recovered; align
the wording with RespireConnectionMultiplexer.ConnectionStateChanged.

In `@tests/Respire.Tests/Networking/ClientCoreTests.cs`:
- Around line 25-26: Update the transition-sequence assertion in ClientCoreTests
to use the order-sensitive CollectionOrdering.Matching option with
IsEquivalentTo, ensuring Reconnecting is asserted before Connected. Apply the
same ordering option to each transition-sequence assertion in the test file.

---

Nitpick comments:
In `@src/Respire/Internal/ClientCore.cs`:
- Line 21: Rename the RespireConnectionState field _publishedState to
_lastQueuedState throughout ClientCore, including assignments and deduplication
checks in QueueAggregateStateLocked and any reads in PublishQueuedStates or
related logic. Preserve the existing state-tracking behavior.

In `@src/Respire/PubSub/SubscriptionHub.cs`:
- Around line 343-361: Add a `_disposed` check inside the `_reconnectStateGate`
critical section in the Connected path before calling
`QueueReconnectStateLocked`, matching the existing Reconnecting path guard. Only
queue and publish `Connected` when the hub is not disposed, the reconnect
generation and connection still match, and the replacement remains connected.
🪄 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: c09547e2-6040-4d68-a964-d0675073c02b

📥 Commits

Reviewing files that changed from the base of the PR and between 4e1183f and 9561074.

📒 Files selected for processing (6)
  • src/Respire/Infrastructure/RespireConnectionMultiplexer.cs
  • src/Respire/Internal/ClientCore.cs
  • src/Respire/PubSub/SubscriptionHub.cs
  • src/Respire/RespireClient.cs
  • tests/Respire.IntegrationTests/PubSubIntegrationTests.cs
  • tests/Respire.Tests/Networking/ClientCoreTests.cs

Comment on lines 76 to 81
/// <summary>Raised when a dead connection is noticed and again when its replacement lands.</summary>
public event Action<RespireConnectionState>? ConnectionStateChanged
{
add => _core.Multiplexer.StateChanged += value;
remove => _core.Multiplexer.StateChanged -= value;
add => _core.ConnectionStateChanged += value;
remove => _core.ConnectionStateChanged -= value;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the XML doc to describe the aggregate semantics.

The event now forwards ClientCore.ConnectionStateChanged, which aggregates every command slot and the dedicated subscriber connection. Connected is published only after all reconnecting parts recover. The current summary still describes a single dead connection and its replacement. The equivalent doc in src/Respire/Infrastructure/RespireConnectionMultiplexer.cs at line 44 was updated; this public one was not.

📝 Proposed doc update
-    /// <summary>Raised when a dead connection is noticed and again when its replacement lands.</summary>
+    /// <summary>
+    /// Raised when any client-owned connection (a command connection or the dedicated
+    /// subscriber connection) is noticed dead, and again once every affected connection is
+    /// restored and its subscriptions are replayed.
+    /// </summary>
     public event Action<RespireConnectionState>? ConnectionStateChanged
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// <summary>Raised when a dead connection is noticed and again when its replacement lands.</summary>
public event Action<RespireConnectionState>? ConnectionStateChanged
{
add => _core.Multiplexer.StateChanged += value;
remove => _core.Multiplexer.StateChanged -= value;
add => _core.ConnectionStateChanged += value;
remove => _core.ConnectionStateChanged -= value;
}
/// <summary>
/// Raised when any client-owned connection (a command connection or the dedicated
/// subscriber connection) is noticed dead, and again once every affected connection is
/// restored and its subscriptions are replayed.
/// </summary>
public event Action<RespireConnectionState>? ConnectionStateChanged
{
add => _core.ConnectionStateChanged += value;
remove => _core.ConnectionStateChanged -= value;
}
🤖 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 76 - 81, Update the XML summary
for RespireClient.ConnectionStateChanged to document that it reflects aggregate
state across all command slots and the dedicated subscriber connection, with
Connected published only after every reconnecting component has recovered; align
the wording with RespireConnectionMultiplexer.ConnectionStateChanged.

Comment on lines +25 to +26
await Assert.That(states).IsEquivalentTo(
[RespireConnectionState.Reconnecting, RespireConnectionState.Connected]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

TUnit assertions IsEquivalentTo collection order sensitivity

💡 Result:

In the TUnit testing framework, the IsEquivalentTo assertion is order-insensitive by default [1][2]. This means that the assertion checks whether two collections contain the same elements with the same frequency, regardless of their position [2]. To perform an order-sensitive comparison, you must explicitly pass CollectionOrdering.Matching as a parameter to the IsEquivalentTo method [1][2]. Examples of usage: Order-agnostic comparison (default): await Assert.That(new[] { 1, 2, 3 }).IsEquivalentTo(new[] { 3, 2, 1 }); // Passes [1] Order-sensitive comparison: await Assert.That(new[] { 1, 2, 3 }).IsEquivalentTo(new[] { 1, 2, 3 }, CollectionOrdering.Matching); // Passes [1][2] await Assert.That(new[] { 1, 2, 3 }).IsEquivalentTo(new[] { 3, 2, 1 }, CollectionOrdering.Matching); // Fails [1] There is no IgnoringOrder() method in TUnit; because order is already ignored by default, such a method is unnecessary [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- candidate test ---'
sed -n '1,90p' tests/Respire.Tests/Networking/ClientCoreTests.cs

printf '%s\n' '--- TUnit references and ordering options ---'
rg -n -i --glob '*.csproj' --glob '*.props' --glob '*.targets' --glob '*.cs' \
  'TUnit|CollectionOrdering|IsEquivalentTo' .

Repository: thomhurst/Respire

Length of output: 14169


Use an order-sensitive collection assertion. TUnit’s IsEquivalentTo ignores element order by default. Pass CollectionOrdering.Matching to each transition-sequence assertion so the tests require Reconnecting before Connected.

🤖 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 `@tests/Respire.Tests/Networking/ClientCoreTests.cs` around lines 25 - 26,
Update the transition-sequence assertion in ClientCoreTests to use the
order-sensitive CollectionOrdering.Matching option with IsEquivalentTo, ensuring
Reconnecting is asserted before Connected. Apply the same ordering option to
each transition-sequence assertion in the test file.

@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 110.2 μs 108.94 μs 5.97 μs 104.30 μs 116.2 μs 109.92 μs 115.6 μs 1.00 0.07 169 B 1.00
StackExchange_Ping 115.1 μs 94.99 μs 5.21 μs 109.52 μs 119.8 μs 115.89 μs 119.4 μs 1.05 0.06 393 B 2.33
Respire_Set_Small 113.8 μs 223.10 μs 12.23 μs 99.69 μs 121.7 μs 119.89 μs 121.6 μs 1.03 0.11 152 B 0.90
StackExchange_Set_Small 110.8 μs 134.46 μs 7.37 μs 102.28 μs 115.5 μs 114.58 μs 115.4 μs 1.01 0.07 406 B 2.40
Respire_Set_Medium 121.6 μs 313.69 μs 17.19 μs 102.30 μs 135.2 μs 127.35 μs 134.4 μs 1.11 0.15 155 B 0.92
StackExchange_Set_Medium 118.8 μs 80.92 μs 4.44 μs 114.34 μs 123.2 μs 118.94 μs 122.8 μs 1.08 0.06 405 B 2.40
Respire_Set_Large 144.1 μs 36.12 μs 1.98 μs 141.84 μs 145.6 μs 144.85 μs 145.5 μs 1.31 0.06 175 B 1.04
StackExchange_Set_Large 139.7 μs 34.31 μs 1.88 μs 137.95 μs 141.7 μs 139.45 μs 141.5 μs 1.27 0.06 412 B 2.44
Respire_Get 112.6 μs 30.23 μs 1.66 μs 111.03 μs 114.3 μs 112.56 μs 114.2 μs 1.02 0.05 202 B 1.20
StackExchange_Get 113.3 μs 182.62 μs 10.01 μs 102.83 μs 122.8 μs 114.29 μs 121.9 μs 1.03 0.09 406 B 2.40
StackExchange_MGet 120.4 μs 186.94 μs 10.25 μs 108.83 μs 128.3 μs 124.18 μs 127.9 μs 1.10 0.10 646 B 3.82
StackExchange_Pipeline_5_Sets 165.8 μs 25.49 μs 1.40 μs 164.18 μs 166.7 μs 166.55 μs 166.6 μs 1.51 0.07 2886 B 17.08
Respire_Exists 112.8 μs 216.95 μs 11.89 μs 104.67 μs 126.4 μs 107.20 μs 124.5 μs 1.03 0.11 160 B 0.95
StackExchange_Exists 113.9 μs 247.95 μs 13.59 μs 98.30 μs 123.1 μs 120.28 μs 122.9 μs 1.04 0.12 383 B 2.27
Respire_Del 241.2 μs 273.06 μs 14.97 μs 229.64 μs 258.1 μs 235.83 μs 255.9 μs 2.19 0.16 198 B 1.17
StackExchange_Del 257.8 μs 129.51 μs 7.10 μs 249.75 μs 263.1 μs 260.70 μs 262.8 μs 2.35 0.12 647 B 3.83
Respire_Incr 100.4 μs 50.11 μs 2.75 μs 98.71 μs 103.6 μs 99.01 μs 103.1 μs 0.91 0.05 157 B 0.93
StackExchange_Incr 117.0 μs 183.20 μs 10.04 μs 106.84 μs 126.9 μs 117.10 μs 125.9 μs 1.06 0.09 370 B 2.19
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                  | 110.2 μs | 108.94 μs |  5.97 μs | 104.30 μs | 116.2 μs | 109.92 μs | 115.6 μs |  1.00 |    0.07 |     169 B |        1.00 |
StackExchange_Ping            | 115.1 μs |  94.99 μs |  5.21 μs | 109.52 μs | 119.8 μs | 115.89 μs | 119.4 μs |  1.05 |    0.06 |     393 B |        2.33 |
Respire_Set_Small             | 113.8 μs | 223.10 μs | 12.23 μs |  99.69 μs | 121.7 μs | 119.89 μs | 121.6 μs |  1.03 |    0.11 |     152 B |        0.90 |
StackExchange_Set_Small       | 110.8 μs | 134.46 μs |  7.37 μs | 102.28 μs | 115.5 μs | 114.58 μs | 115.4 μs |  1.01 |    0.07 |     406 B |        2.40 |
Respire_Set_Medium            | 121.6 μs | 313.69 μs | 17.19 μs | 102.30 μs | 135.2 μs | 127.35 μs | 134.4 μs |  1.11 |    0.15 |     155 B |        0.92 |
StackExchange_Set_Medium      | 118.8 μs |  80.92 μs |  4.44 μs | 114.34 μs | 123.2 μs | 118.94 μs | 122.8 μs |  1.08 |    0.06 |     405 B |        2.40 |
Respire_Set_Large             | 144.1 μs |  36.12 μs |  1.98 μs | 141.84 μs | 145.6 μs | 144.85 μs | 145.5 μs |  1.31 |    0.06 |     175 B |        1.04 |
StackExchange_Set_Large       | 139.7 μs |  34.31 μs |  1.88 μs | 137.95 μs | 141.7 μs | 139.45 μs | 141.5 μs |  1.27 |    0.06 |     412 B |        2.44 |
Respire_Get                   | 112.6 μs |  30.23 μs |  1.66 μs | 111.03 μs | 114.3 μs | 112.56 μs | 114.2 μs |  1.02 |    0.05 |     202 B |        1.20 |
StackExchange_Get             | 113.3 μs | 182.62 μs | 10.01 μs | 102.83 μs | 122.8 μs | 114.29 μs | 121.9 μs |  1.03 |    0.09 |     406 B |        2.40 |
StackExchange_MGet            | 120.4 μs | 186.94 μs | 10.25 μs | 108.83 μs | 128.3 μs | 124.18 μs | 127.9 μs |  1.10 |    0.10 |     646 B |        3.82 |
StackExchange_Pipeline_5_Sets | 165.8 μs |  25.49 μs |  1.40 μs | 164.18 μs | 166.7 μs | 166.55 μs | 166.6 μs |  1.51 |    0.07 |    2886 B |       17.08 |
Respire_Exists                | 112.8 μs | 216.95 μs | 11.89 μs | 104.67 μs | 126.4 μs | 107.20 μs | 124.5 μs |  1.03 |    0.11 |     160 B |        0.95 |
StackExchange_Exists          | 113.9 μs | 247.95 μs | 13.59 μs |  98.30 μs | 123.1 μs | 120.28 μs | 122.9 μs |  1.04 |    0.12 |     383 B |        2.27 |
Respire_Del                   | 241.2 μs | 273.06 μs | 14.97 μs | 229.64 μs | 258.1 μs | 235.83 μs | 255.9 μs |  2.19 |    0.16 |     198 B |        1.17 |
StackExchange_Del             | 257.8 μs | 129.51 μs |  7.10 μs | 249.75 μs | 263.1 μs | 260.70 μs | 262.8 μs |  2.35 |    0.12 |     647 B |        3.83 |
Respire_Incr                  | 100.4 μs |  50.11 μs |  2.75 μs |  98.71 μs | 103.6 μs |  99.01 μs | 103.1 μs |  0.91 |    0.05 |     157 B |        0.93 |
StackExchange_Incr            | 117.0 μs | 183.20 μs | 10.04 μs | 106.84 μs | 126.9 μs | 117.10 μs | 125.9 μs |  1.06 |    0.09 |     370 B |        2.19 |

@thomhurst
thomhurst merged commit c8f1c8a into main Aug 9, 2026
14 checks passed

@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: 956107409a

ℹ️ 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".

}
}

core.NotifySubscriptionStateChanged(state);

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 Recheck disposal before forwarding a dequeued state

Fresh evidence in the revised queue is that a state is removed under _reconnectStateGate but forwarded only after releasing the gate. If DisposeAsync runs in that window, it sets _disposed, clears only the states still queued, and can return before this call forwards the already-dequeued Reconnecting or Connected; observers can therefore receive a transition after disposal, and a dequeued Reconnecting can become permanent because its matching Connected was cleared. Coordinate disposal with the in-flight publisher or revalidate disposal before forwarding the state.

Useful? React with 👍 / 👎.

This branch was previously deployed

1 inactive deployment
Pull Requests — 95610740 Deployed Aug 9, 2026 by thomhurst via modularpipeline #364
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.

Subscriber resilience: reconnect and re-subscribe

1 participant