Skip to content

Conversation

Copy link

Copilot AI commented Jan 28, 2026

Comprehensive test validation of Pulsar native retry letter queue and dead letter queue functionality following pulsar2 branch rebase and PR #7 cherry-picks.

Test Results

All tests passing (36/36):

  • Unit tests: 32/32 including configuration validation, null safety, and subscription type restrictions
  • Integration tests: 4/4 covering full retry cycles, DLQ-only mode, delay intervals, and attempt tracking

Validated Functionality

Retry Letter Queue:

  • Configurable delay intervals with proper message scheduling
  • Consumer tracking (IsFromRetryConsumer) ensures correct acknowledgment on main vs retry consumers
  • Headers propagate correctly: DELAY_TIME, RECONSUMETIMES, REAL_TOPIC, ORIGIN_MESSAGE_ID
  • Subscription type validation (Shared/KeyShared only)

Dead Letter Queue:

  • Exception details captured in EXCEPTION header with full stack traces
  • Works both after exhausting retries and in DLQ-only mode
  • Proper metadata preservation through retry cycles

Code Review Changes Verified

All 14 changes from the rebase validated:

  • Null-safe GetHashCode() implementations
  • Exception headers written to both message metadata (for Pulsar) and envelope headers (for tracking)
  • getRetryLetterTopicUri checks correct flag (NativeRetryLetterQueueEnabled vs NativeDeadLetterQueueEnabled)
  • Operator precedence fixed in boolean logic
  • Async cancellation patterns
  • Nullable type annotations

Example of key fix - consumer acknowledgment now routes to correct source:

public ValueTask CompleteAsync(Envelope envelope)
{
    if (envelope is PulsarEnvelope e)
    {
        // Acknowledge on appropriate consumer based on message source
        var consumer = e.IsFromRetryConsumer && _retryConsumer != null 
            ? _retryConsumer 
            : _consumer;
        return consumer?.Acknowledge(e.MessageData, _cancellation) ?? ValueTask.CompletedTask;
    }
    return ValueTask.CompletedTask;
}

Documentation Added

  • PULSAR_NATIVE_RETRY_DLQ_TEST_RESULTS.md - Detailed test results with code review validation
  • TESTING_SUMMARY.md - Executive summary with functional verification

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • 127.0.0.11
    • Triggering command: REDACTED, pid is -1 (packet block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

I made some manual changes in pulsar2 branch I manually rebased the main branch on it and cherry picked some commits from @punxrok/wolverine/pull/7 Please test my latest changes and make sure that the pulsar native retry letter queue and DLQ work as envisoned.

SOme changes that were made in pulsar2 branch:
@@ -0,0 +1,516 @@

Pulsar Native Retry and DLQ Support - Code Review Changes

Overview

This document explains the code changes made during the review of the Pulsar Native Retry and Dead Letter Queue (DLQ) support feature, originally developed for Wolverine prior to version 5.0 and now rebased onto the main branch.

Changes Summary

1. Fixed Potential Null Reference Exceptions in GetHashCode()

Files Modified:

  • DeadLetterTopic.cs
  • RetryLetterTopic.cs

Problem:
Both classes had GetHashCode() implementations that could throw NullReferenceException when _topicName was null:

// BEFORE (could throw NullReferenceException)
public override int GetHashCode()
{
    return _topicName.GetHashCode();
}

Solution:
Added null-conditional operator to handle null topic names:

// AFTER (safe)
public override int GetHashCode()
{
    return _topicName?.GetHashCode() ?? 0;
}

Why this matters:

  • DeadLetterTopic.DefaultNative creates an instance without a topic name
  • RetryLetterTopic constructor allows creation without a topic name
  • Using these objects in hash-based collections would throw exceptions

Test Coverage:

[Fact]
public void DeadLetterTopic_GetHashCode_Should_Handle_Null_TopicName()
{
    var dlq = new DeadLetterTopic(DeadLetterTopicMode.Native);
    var hash = dlq.GetHashCode();
    hash.ShouldBe(0); // Should not throw
}

2. Added Description Property to PulsarNativeContinuationSource

File Modified: ErrorHandling/PulsarNativeContinuationSource.cs

Problem:
The Description property was returning null which could cause issues in diagnostic/logging scenarios:

// BEFORE
public string Description { get; } // implicitly null

Solution:

// AFTER
public string Description => "Pulsar native retry/DLQ handling";

Why this matters:

  • IContinuationSource.Description is used for diagnostics and logging
  • A meaningful description helps in troubleshooting error handling flows

3. Fixed DeadLetterTopic.TopicName Property Return Type

File Modified: DeadLetterTopic.cs

Problem:
The property getter returned string but the backing field could be null:

// BEFORE
public string TopicName
{
    get => _topicName;  // Warning: Possible null return
    ...
}

Solution:
Changed to explicit nullable return type:

// AFTER
public string? TopicName
{
    get => _topicName;
    ...
}

4. Fixed Exception Header Being Set on Wrong Object

File Modified: PulsarListener.cs

Problem:
In BuildMessageMetadata, the exception header was being set on the envelope's headers instead of the message metadata that gets sent to Pulsar:

// BEFORE (incorrect - sets on envelope, not on outgoing message)
e.Headers[PulsarEnvelopeConstants.Exception] = exception.ToString();

Solution:

// AFTER (correct - sets on message metadata for Pulsar)
if (exception != null)
{
    messageMetadata[PulsarEnvelopeConstants.Exception] = exception.ToString();
}

Why this matters:

  • The exception information needs to be propagated to the DLQ message in Pulsar
  • Setting it on e.Headers only affects the local envelope, not the Pulsar message

5. Added Consumer Tracking for Proper Message Acknowledgment

Files Modified:

  • PulsarEnvelope.cs
  • PulsarListener.cs

Problem:
When using retry topics, messages can come from either the main consumer or the retry consumer. The original code always acknowledged on the main consumer, which could cause issues:

// BEFORE - always used _consumer
public ValueTask CompleteAsync(Envelope envelope)
{
    if (envelope is PulsarEnvelope e)
    {
        if (_consumer != null)
        {
            return _consumer.Acknowledge(e.MessageData, _cancellation);
        }
    }
    return ValueTask.CompletedTask;
}

Solution:
Added IsFromRetryConsumer property to PulsarEnvelope and updated acknowledgment logic:

// PulsarEnvelope.cs
public class PulsarEnvelope : Envelope
{
    // ...existing code...
    
    /// <summary>
    /// Indicates if this message came from the retry topic consumer
    /// </summary>
    public bool IsFromRetryConsumer { get; set; }
}

// PulsarListener.cs
public ValueTask CompleteAsync(Envelope envelope)
{
    if (envelope is PulsarEnvelope e)
    {
        // Acknowledge on the appropriate consumer based on where the message came from
        var consumer = e.IsFromRetryConsumer && _retryConsumer != null ? _retryConsumer : _consumer;
        if (consumer != null)
        {
            return consumer.Ackno...

</details>



<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs.

Copilot AI and others added 2 commits January 28, 2026 12:02
Co-authored-by: punxrok <6354783+punxrok@users.noreply.github.com>
Co-authored-by: punxrok <6354783+punxrok@users.noreply.github.com>
Copilot AI changed the title [WIP] Test latest changes in pulsar native retry and DLQ Validate and document Pulsar native retry/DLQ implementation Jan 28, 2026
Copilot AI requested a review from punxrok January 28, 2026 12:05
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.

2 participants