Skip to content

CritterWatch Integration: DLQ Recovery, Circuit Breaker Hooks, Endpoint Descriptors#2372

Merged
jeremydmiller merged 4 commits intomainfrom
cw-changes
Mar 29, 2026
Merged

CritterWatch Integration: DLQ Recovery, Circuit Breaker Hooks, Endpoint Descriptors#2372
jeremydmiller merged 4 commits intomainfrom
cw-changes

Conversation

@jeremydmiller
Copy link
Copy Markdown
Member

Summary

Four changes to support CritterWatch monitoring features:

  • Circuit breaker observer hooksCircuitBreakerTripped and CircuitBreakerReset methods on IWolverineObserver. When a listener's circuit breaker trips, the observer is notified with the endpoint and options. CritterWatch uses these to raise/resolve alerts.

  • Dead letter queue recovery listenerEnableDeadLetterQueueRecovery() on the RabbitMQ transport starts a background listener that moves messages from RabbitMQ's native DLQ back into Wolverine's PostgreSQL wolverine_dead_letters table. This enables CritterWatch to query and manage dead letters via its existing DLQ explorer UI.

  • Custom queue name overloadEnableDeadLetterQueueRecovery(params string[] queueNames) for services with custom DLQ queue names.

  • Endpoint type descriptorTransportType property on EndpointDescriptor that maps concrete endpoint types to human-readable labels (RabbitMQ Queue, Azure Service Bus Topic, Local Queue, Redis Stream, etc.). Used by CritterWatch's Listeners page to show what kind of endpoint each URI represents.

Test plan

  • Circuit breaker hooks: XUnitObserver updated, BackPressureTests build
  • DLQ recovery: E2E test in Wolverine.RabbitMQ.Tests verifies messages flow through native NACK → RabbitMQ DLX → recovery listener → PostgreSQL dead letters table
  • Custom queue overload: test verifies settings contain custom queue name
  • Endpoint descriptor: verified via CritterWatch TypeScript codegen + UI tests
  • CoreTests: 1192/1193 pass (1 pre-existing flaky TCP forwarding test)

🤖 Generated with Claude Code

jeremydmiller and others added 4 commits March 29, 2026 15:16
Extend IWolverineObserver with CircuitBreakerTripped and
CircuitBreakerReset callbacks. When a listener's circuit breaker
trips due to excessive failures, the observer is now notified with
the endpoint and circuit breaker options. CritterWatch uses these
hooks to raise alerts in the monitoring UI.

- Add observer parameter to CircuitBreaker constructor
- Fire CircuitBreakerTripped from UpdateTotalsAsync
- Pass runtime.Observer when constructing CircuitBreaker in
  ListeningAgent and DurableLocalQueue
- Update XUnitObserver with no-op implementations

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…atabase

New opt-in background service that listens to the RabbitMQ dead letter
queue and recovers messages into Wolverine's persistent dead letter
storage (wolverine_dead_letters table). This bridges RabbitMQ's native
DLX mechanism with Wolverine's database-backed DLQ management.

Enable with: opts.UseRabbitMq().EnableDeadLetterQueueRecovery()

The listener:
- Subscribes to the wolverine-dead-letter-queue RabbitMQ queue
- Reconstructs Wolverine envelope from RabbitMQ message properties
- Extracts x-death headers (original queue, reason, death count)
- Recovers exception info from Wolverine headers when available
  (InteropFriendly mode stamps these; Native mode uses x-death)
- Writes to wolverine_dead_letters database table via IMessageStore
- Requeues messages that fail to write (no data loss)

New types:
- DeadLetterQueueListener: BackgroundService implementation
- DeadLetterRecoveredException: synthetic exception for recovered DLQ
- RabbitMqTransport.EnableDeadLetterQueueRecovery flag

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…d E2E tests

- Add params string[] overload to EnableDeadLetterQueueRecovery for
  listening to custom-named DLQ queues beyond the default
- Introduce DeadLetterQueueRecoverySettings to hold queue name config
- Update DeadLetterQueueListener to subscribe to multiple queues
  from the settings, falling back to the default DLQ queue name
- Fix NullReferenceException: set Destination and SentAt on recovered
  envelopes (required by MoveToDeadLetterStorageAsync)
- Extract original queue from RabbitMQ x-death headers for Destination

E2E tests (3 passing, 1 marked Flaky):
- recovers_native_dlq_message_to_database: single message recovery
- recovers_from_custom_named_dlq: custom DLQ queue name
- settings_contain_custom_queue_name: config verification
- recovers_multiple_messages: bulk recovery (timing-sensitive)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Maps concrete endpoint types to human-readable labels for
CritterWatch UI display (RabbitMQ Queue, Azure Service Bus
Topic, Local Queue, etc.). Falls back to PascalCase splitting
for unknown types.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

1 participant