Skip to content

Manual Puback for MQTT5#417

Open
sbSteveK wants to merge 27 commits intomainfrom
manual-puback
Open

Manual Puback for MQTT5#417
sbSteveK wants to merge 27 commits intomainfrom
manual-puback

Conversation

@sbSteveK
Copy link
Contributor

@sbSteveK sbSteveK commented Feb 3, 2026

Implements manual PUBACK feature for the MQTT5 client.

Provides public API calls:
aws_mqtt5_client_acquire_puback()
aws_mqtt5_client_invoke_puback()

Tests for new API

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@codecov-commenter
Copy link

codecov-commenter commented Feb 3, 2026

Codecov Report

❌ Patch coverage is 83.89262% with 24 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.53%. Comparing base (41b6a7d) to head (37741c0).

Files with missing lines Patch % Lines
source/v5/mqtt5_client.c 82.70% 23 Missing ⚠️
source/v5/mqtt5_options_storage.c 91.66% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #417      +/-   ##
==========================================
- Coverage   84.55%   84.53%   -0.03%     
==========================================
  Files          26       26              
  Lines       10833    10974     +141     
==========================================
+ Hits         9160     9277     +117     
- Misses       1673     1697      +24     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@sbSteveK sbSteveK marked this pull request as ready for review February 13, 2026 21:09
Copy link
Contributor

@bretambrose bretambrose left a comment

Choose a reason for hiding this comment

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

Looked at the headers

@@ -0,0 +1,70 @@
#ifndef AWS_MQTT_MANUAL_PUBACK_H
Copy link
Contributor

Choose a reason for hiding this comment

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

Wouldn't this make sense to be in v5 and not "manual-puback"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a remnant of initial implementation where we were considering MQTT3 support. I considered moving it into the v5 folder but felt like I'd rather keep it within mqtt/manual-puback in case we add it later. I'm fine with moving things into v5. Should be a minimal change of folder structure if we want it in v5 instead.

* @return puback_control_id of the PUBLISH packet. This can be used to schedule a PUBACK for the PUBLISH packet
* when used with the aws_mqtt5_manual_puback function call.
*/
AWS_MQTT_API uint64_t aws_mqtt5_take_manual_puback_control(
Copy link
Contributor

Choose a reason for hiding this comment

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

Feels like these declarations should just be in the public mqtt5 client file. Impl can stay separate.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think it needs to be all or nothing but the lines feel blurred between moving these functions into the public mqtt5 client and moving the implementation details out on their own. i.e. current_control_id. I'm beginning to feel like the entire manual puback should simply be a part of the mqtt5 client as it's being handled as a client operation ala Publish, Subscribe, etc.


/*
* One more than the most recently used control packet id.
*/
Copy link
Contributor

@bretambrose bretambrose Feb 13, 2026

Choose a reason for hiding this comment

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

My suggestion to wall off the implementation as a separate system was intended to encapsulate/insulate the client from this kind of change, ie the puback system is an opaque structure whose logic is completely divorced from the client. The only crossover would be whatever function the impl needs in order to queue a puback.

Essentially, publish received would now be the responsibility of this new system. It would have a clean, private API for use by the client and the client itself would have very few changes in it (just creating/destroying and invoking the system).

Copy link
Contributor

Choose a reason for hiding this comment

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

Following up on this:

I'd like to see the puback control an opaque subsystem with a clean/clear API. Its usage within the 5 client would be similar to how the request response client embeds and uses the subscription manager.

Advantages:

  1. Separation of concerns - rather than dropping a bunch of new data structures into the 5 client impl and entangling the implementations, they stay independent
  2. Testability - if we just dump everything into the 5 client then all testing requires a 5 client and a real connection to either a mock or real server. If split off into an opaque subsystem with a clear API, the majority of the testing can be done without a client or an active connection; just use the "public"(private) subsystem API and introspect state
  3. Reusability - if we need to add support to the 311 client, we don't need to reimplement everything from scratch; we just embed the subsystem instance into the 311 client and use its API correctly

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Keeping the manual puback operation in mqtt5_client.c feels like the cleaner way to handle this feature. The client PUBACK operation, manual or otherwise, feels like it belongs within the client as opposed to a separate subsystem. While it made sense to move it into a separate subsystem when originally considering both 3 and 5 clients, that's no longer the case. With everything already setup, it'd take the same amount of work to separate it now as it would to do so in the future if we ever add 3 support. There may also be thread or state related issues that we are not aware of right now that'd result in us redoing the work to support the 3 client that would render the current implementation unusable for the 3 client. I'd rather do that work when it's needed than unnecessarily do it if it's never needed.

From a testing perspective, I've included tests that use it in the same way we would want to test other client operations against a mock server rather. The mock server we have implemented is up to the task of detecting that PUBACKs are withheld and sent as expected. The test coverage feels like it's covering that the new API is doing what's expected, which with the removal of ordered PUBACKs is pretty simple.

Open to discussion and willing to change my mind but that's where I'm currently at.

Copy link
Contributor

Choose a reason for hiding this comment

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

Don't agree; dumping everything into one giant system is something we need to get away from.

Copy link
Contributor

@xiazhvera xiazhvera left a comment

Choose a reason for hiding this comment

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

I temporary finished the main implementation part, still working on the tests...


/**
* Takes manual control of PUBACK for the given PUBLISH packet.
*
Copy link
Contributor

Choose a reason for hiding this comment

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

We should clarify that the function "should only ever be called by the user within the publish received callback" in the comments, otherwise the PUBACK will be sent after the publish callback is invoked.

Also we should also mention that 0 indicates an invalid control.


s_aws_mqtt5_client_shutdown_channel(client, error_code);
}
return;
Copy link
Contributor

Choose a reason for hiding this comment

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

trivial: the return seems unnecessary here.


/* Allows lookup of manual puback entries by packet id. We incref here because the packet_id table also has a ref to
* the manual_puback */
aws_ref_count_acquire(&manual_puback->ref_count);
Copy link
Contributor

Choose a reason for hiding this comment

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

Similarly, though it is unlikely to happen, if aws_hash_table_put failed, we need release the ref_count.

size_t in_flight_unacked_publishes =
aws_hash_table_get_entry_count(&client->operational_state.manual_puback_control_id_table);
if (in_flight_unacked_publishes >= 100) {
AWS_LOGF_WARN(
Copy link
Contributor

Choose a reason for hiding this comment

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

The in-flight message count here might not be accurate, as it may not account for in-flight messages that are waiting for PUBACK in the queue that were not acquired by the user.

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.

5 participants