Skip to content

feat(rumqttc): add support for unbounded client#1047

Open
imjyotiraditya wants to merge 1 commit into
bytebeamio:mainfrom
imjyotiraditya:main
Open

feat(rumqttc): add support for unbounded client#1047
imjyotiraditya wants to merge 1 commit into
bytebeamio:mainfrom
imjyotiraditya:main

Conversation

@imjyotiraditya
Copy link
Copy Markdown

@imjyotiraditya imjyotiraditya commented Apr 24, 2026

Type of change

  • New feature (non-breaking change which adds functionality)

Description

Add ClientBuilder and AsyncClientBuilder for both v4 and v5 clients.
The channel is bounded by default using the capacity set in MqttOptions,
with opt-in unbounded channels via .unbounded().

Before

let (client, connection) = Client::new(mqttoptions, 10);
let (client, eventloop) = AsyncClient::new(mqttoptions, 10);

After

// bounded with explicit capacity
let (client, connection) = Client::builder(mqttoptions).capacity(10).build();
let (client, eventloop) = AsyncClient::builder(mqttoptions).capacity(10).build_async();

// bounded with capacity from MqttOptions (new default)
let (client, connection) = Client::builder(mqttoptions).build();
let (client, eventloop) = AsyncClient::builder(mqttoptions).build_async();

// unbounded (opt-in, removes backpressure)
let (client, connection) = Client::builder(mqttoptions).unbounded().build();
let (client, eventloop) = AsyncClient::builder(mqttoptions).unbounded().build_async();

The existing Client::new and AsyncClient::new are deprecated but not removed,
so existing code continues to work without changes.

Checklist

  • Formatted with cargo fmt
  • Added entry to CHANGELOG.md

@thehouseisonfire
Copy link
Copy Markdown

Hi @imjyotiraditya, and thanks for the PR.

I do think the builder pattern might be better long term, though with just two options for now it's not that beneficial.

As for the unbounded clients, any reason that setting a really high bound wouldn't cut it?

@imjyotiraditya
Copy link
Copy Markdown
Author

Hi @thehouseisonfire, thanks for the review!

On the builder pattern - agreed it's lightweight with just two options today, but it's
the exact API shape the maintainers requested when closing #670 ("builder pattern where
unbounded is default and users can opt into .capacity()"). This PR is a direct
implementation of that request.

On high bound vs unbounded - a few references worth noting:

  1. eventloop.rs itself already recommends unbounded in a doc comment on the clean()
    method: "For this reason we recommend setting AsyncClient's channel capacity to 0"
    (capacity 0 in flume = unbounded). So the library's own source code agrees.

  2. The official FAQ has a dedicated section "Program is stuck forever on publish /
    subscribe" documenting the bounded channel deadlock - it's an acknowledged footgun,
    not an edge case.

  3. Issue Subscribe potentially locks event loop #263 traces this back to 2021 - calling subscribe on ConnAck deadlocks if the
    channel is full because the event loop can't drain the channel while blocked waiting
    to send to it. A high bound reduces the window but doesn't eliminate this.

  4. We hit this exact bug in production: on network reconnect we flush offline-stored
    messages in the ConnAck callback. With a bounded channel the flush fills the channel
    immediately and all subsequent publishes silently fail - the service appears connected
    but nothing goes through. Unbounded fixed it completely.

Happy to simplify the API further if you have a preference on shape!

@imjyotiraditya
Copy link
Copy Markdown
Author

Hi @thehouseisonfire, just checking in - happy to address any further feedback or simplify the API if needed!

@thehouseisonfire
Copy link
Copy Markdown

Hey @imjyotiraditya,

Sorry for the delay — I was working on a larger feature in my fork at the same time.

I ended up making unbounded clients opt-in with an .unbounded() call rather than the new default, since bounded clients are historically more common and likely still the more commonly used behavior (of course, I may be wrong about that).

That said, I did like the API shape from your PR. It has now been merged and is working in my fork of this crate, so feel free to take a look there and see whether it fits yours need.

Longer term, I do think your API design is better than what the upstream crate currently exposes.

Signed-off-by: Jyotiraditya Panda <jyotiraditya@aospa.co>
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