Skip to content

Conversation

@lukebakken
Copy link
Contributor

@lukebakken lukebakken commented Dec 8, 2025

Traditional publisher confirms in the Java client require manual
tracking of sequence numbers and correlation of Basic.Return messages.
This makes per-message error handling complex and provides no built-in
async pattern, backpressure mechanism, or message correlation support.

This change introduces automatic publisher confirmation tracking with a
CompletableFuture-based API, progressive throttling, and generic
context parameter for message correlation, following the design of the
.NET client's publisher confirmation implementation. When enabled via
ChannelOptions, the library automatically tracks each message's
confirmation status and completes futures when the broker sends
Basic.Ack, Basic.Nack, or Basic.Return.

New API methods:

  • Channel.basicPublishAsync() - Returns CompletableFuture<T> that
    completes with user-provided context when broker confirms the message.
    The generic context parameter (such as a correlation ID) eliminates
    the need for separate tracking structures.
  • Connection.createChannel(ChannelOptions) - Creates channel with
    publisher confirmation tracking enabled

New classes:

  • ChannelOptions - Configuration with builder pattern for channel
    creation with tracking settings and optional RateLimiter for
    backpressure control
  • PublishException - Exception thrown when message is nack'd or
    returned, with full details including sequence number, routing
    information, and user-provided context
  • RateLimiter - Interface for custom rate limiting strategies
  • ThrottlingRateLimiter - Progressive throttling implementation that
    applies delays proportional to capacity usage (0-1000ms) when
    available permits fall below a configurable threshold (default 50%)

The implementation adds a sequence number header (x-seq-no) to each
tracked message, allowing correlation of Basic.Return responses with
specific messages. The ThrottlingRateLimiter uses Semaphore for
concurrency control and calculates delay as percentageUsed * 1000ms,
matching the .NET client's algorithm exactly. Passing null for the
rate limiter disables backpressure for unlimited outstanding
confirmations.

Publisher confirmation state is stored in a single ConcurrentHashMap
containing ConfirmationEntry objects that hold the future, rate
limiter permit, and user context. The map is sized based on the rate
limiter's capacity hint for optimal memory usage.

The feature is opt-in and maintains full backward compatibility.
Existing basicPublish() and waitForConfirms() methods remain
unchanged. Tests include 9 unit tests for ThrottlingRateLimiter and 25
integration tests for publisher confirmation tracking with context
parameter verification and various rate limiting scenarios.

@lukebakken
Copy link
Contributor Author

Hey @acogoluegnes!

Here's a proposal for implementing "automatic" publisher confirmation tracking in a similar manner to the .NET client. As you're the expert here, please suggest better names, a better implementation, etc, as I'm just barely familiar enough with Java and this project to implement this feature with the help of a genie. I did, of course, review the code.

If you like the way this is going, I thought I'd also add rate-throttling in a similar manner to the .NET client, i.e. as the outstanding confirmation window closes, increase delay between publishes to (hopefully) allow the broker to catch up.

@lukebakken lukebakken force-pushed the lukebakken/publisher-confirm-tracking branch 2 times, most recently from 6a31a46 to 714370e Compare December 8, 2025 23:53
@acogoluegnes
Copy link
Contributor

Thanks for this contributon @lukebakken. I think it is on the right track. I added some comments in the code. Just a few more remarks:

  • the new methods in the public interfaces should have a default implementation if we back port the PR to 5.x. This is for backward compatibility. Throwing an exception is good enough.
  • synchronized blocks and Object-based synchronization is considered old-style Java concurrency. We could see if there are more modern utilities in the Java concurrency toolkit to handle what we need in ChannelN. I'm not super opinionated on this topic though and this is an implementation details we can polish later.
  • the bulk of the PR is in ChannelN, it is not rocket science but add some non-trivial logic to a somewhat already complex class. I was wondering if it could be possible to externalize all the logic in a PublishConfirmState class used from ChannelN. This state class would register regular return and confirm listeners on the channel. This could even be an interface with a no-op implementation when the feature is not activated, minimalizing the impact on ChannelN. We can discuss this design when all the features are added.

@lukebakken lukebakken force-pushed the lukebakken/publisher-confirm-tracking branch from 714370e to 44c3aca Compare December 9, 2025 16:36
@lukebakken lukebakken changed the title Add automatic publisher confirmation tracking with async API Add automatic publisher confirmation tracking with throttling Dec 9, 2025
@lukebakken lukebakken force-pushed the lukebakken/publisher-confirm-tracking branch 4 times, most recently from f785dc5 to c17cd9f Compare December 9, 2025 22:09
…text

Traditional publisher confirms in the Java client require manual
tracking of sequence numbers and correlation of Basic.Return messages.
This makes per-message error handling complex and provides no built-in
async pattern, backpressure mechanism, or message correlation support.

This change introduces automatic publisher confirmation tracking with a
`CompletableFuture`-based API, progressive throttling, and generic
context parameter for message correlation, following the design of the
.NET client's publisher confirmation implementation. When enabled via
`ChannelOptions`, the library automatically tracks each message's
confirmation status and completes futures when the broker sends
Basic.Ack, Basic.Nack, or Basic.Return.

New API methods:
- `Channel.basicPublishAsync()` - Returns `CompletableFuture<T>` that
  completes with user-provided context when broker confirms the message.
  The generic context parameter (such as a correlation ID) eliminates
  the need for separate tracking structures.
- `Connection.createChannel(ChannelOptions)` - Creates channel with
  publisher confirmation tracking enabled

New classes:
- `ChannelOptions` - Configuration with builder pattern for channel
  creation with tracking settings and optional `RateLimiter` for
  backpressure control
- `PublishException` - Exception thrown when message is nack'd or
  returned, with full details including sequence number, routing
  information, and user-provided context
- `RateLimiter` - Interface for custom rate limiting strategies
- `ThrottlingRateLimiter` - Progressive throttling implementation that
  applies delays proportional to capacity usage (0-1000ms) when
  available permits fall below a configurable threshold (default 50%)

The implementation adds a sequence number header (`x-seq-no`) to each
tracked message, allowing correlation of Basic.Return responses with
specific messages. The `ThrottlingRateLimiter` uses `Semaphore` for
concurrency control and calculates delay as `percentageUsed * 1000ms`,
matching the .NET client's algorithm exactly. Passing `null` for the
rate limiter disables backpressure for unlimited outstanding
confirmations.

Publisher confirmation state is stored in a single `ConcurrentHashMap`
containing `ConfirmationEntry` objects that hold the future, rate
limiter permit, and user context. The map is sized based on the rate
limiter's capacity hint for optimal memory usage.

The feature is opt-in and maintains full backward compatibility.
Existing `basicPublish()` and `waitForConfirms()` methods remain
unchanged. Tests include 9 unit tests for `ThrottlingRateLimiter` and 25
integration tests for publisher confirmation tracking with context
parameter verification and various rate limiting scenarios.
@lukebakken lukebakken force-pushed the lukebakken/publisher-confirm-tracking branch from c17cd9f to c64f7ba Compare December 9, 2025 22:30
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.

3 participants