From e04ccd66259031bcd4a7b801d293a122a2b1aa12 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 2 Apr 2026 11:22:01 -0700 Subject: [PATCH 1/7] manual puback instructions --- documents/MQTT5_Userguide.md | 57 ++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/documents/MQTT5_Userguide.md b/documents/MQTT5_Userguide.md index 431121f17..de8da7b75 100644 --- a/documents/MQTT5_Userguide.md +++ b/documents/MQTT5_Userguide.md @@ -24,6 +24,7 @@ - [Subscribe](#subscribe) - [Unsubscribe](#unsubscribe) - [Publish](#publish) + - [Manual Publish Acknowledgement](#manual-publish-acknowledgement) * [MQTT5 Best Practices](#mqtt5-best-practices) # Introduction @@ -757,6 +758,62 @@ If the PUBLISH was a QoS 1 publish, then the completion callback returns a PubAc ``` +### Manual Publish Acknowledgement + +By default, the MQTT5 client automatically sends a PUBACK for every QoS 1 PUBLISH it receives, immediately after the `OnPublishReceivedHandler` callback returns. Manual publish acknowledgement gives you control over when that PUBACK is sent, allowing you to defer acknowledgement until after your application has fully processed the message. For example; after persisting it to a database or forwarding it to another service. + +To take manual control of the PUBACK, call `eventData.acquirePublishAcknowledgement()` **within** the `OnPublishReceivedHandler` callback. This returns a `ScopedResource` that you can store and use later to send the PUBACK by calling `client->InvokePublishAcknowledgement()`. + +**Important constraints:** +* `acquirePublishAcknowledgement()` must be called within the `OnPublishReceivedHandler` callback. Calling it after the callback returns will return `nullptr`. +* This is only relevant for QoS 1 messages. For QoS 0 messages, `acquirePublishAcknowledgement()` returns `nullptr`. +* If `acquirePublishAcknowledgement()` is not called (or returns `nullptr`), the client will automatically send the PUBACK when the callback returns. + +The following example shows how to acquire the acknowledgement handle within the callback and invoke it later: + +```cpp + // A shared location to store the acknowledgement handle for later use + Crt::ScopedResource pendingAck; + + // Set the publish received callback on the builder + builder->WithPublishReceivedCallback( + [&pendingAck](const Mqtt5::PublishReceivedEventData &eventData) { + if (eventData.publishPacket == nullptr) + return; + + fprintf(stdout, "Publish received on topic %s\n", + eventData.publishPacket->getTopic().c_str()); + + // Acquire manual control of the PUBACK for this QoS 1 message. + // This must be called within the callback. After the callback returns, + // acquirePublishAcknowledgement() will return nullptr. + pendingAck = eventData.acquirePublishAcknowledgement(); + + if (pendingAck == nullptr) + { + // QoS 0 message or acknowledgement already taken — nothing to do. + return; + } + + // The PUBACK will NOT be sent automatically because we acquired the handle. + }); + + std::shared_ptr client = builder->Build(); + + // ... connect, subscribe, and receive messages ... + + // After processing is complete, send the PUBACK by invoking the acknowledgement. + if (pendingAck != nullptr) + { + if (!client->InvokePublishAcknowledgement(pendingAck)) + { + fprintf(stdout, "Failed to invoke publish acknowledgement.\n"); + } + } + +``` + + # MQTT5 Best Practices Below are some best practices for the MQTT5 client that are recommended to follow for the best development experience: From 35a1da0182722ea719c9c913a9e5b42c84b05afe Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Thu, 2 Apr 2026 14:08:29 -0700 Subject: [PATCH 2/7] move publish acknowledgement to an advanced operations section --- documents/MQTT5_Userguide.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/documents/MQTT5_Userguide.md b/documents/MQTT5_Userguide.md index de8da7b75..cf4ef6ae4 100644 --- a/documents/MQTT5_Userguide.md +++ b/documents/MQTT5_Userguide.md @@ -24,7 +24,8 @@ - [Subscribe](#subscribe) - [Unsubscribe](#unsubscribe) - [Publish](#publish) - - [Manual Publish Acknowledgement](#manual-publish-acknowledgement) +* [Advanced Operations and Settings](#advanced-operations-and-settings) + + [Manual Publish Acknowledgement](#manual-publish-acknowledgement) * [MQTT5 Best Practices](#mqtt5-best-practices) # Introduction @@ -758,9 +759,11 @@ If the PUBLISH was a QoS 1 publish, then the completion callback returns a PubAc ``` -### Manual Publish Acknowledgement +# Advanced Operations and Settings -By default, the MQTT5 client automatically sends a PUBACK for every QoS 1 PUBLISH it receives, immediately after the `OnPublishReceivedHandler` callback returns. Manual publish acknowledgement gives you control over when that PUBACK is sent, allowing you to defer acknowledgement until after your application has fully processed the message. For example; after persisting it to a database or forwarding it to another service. +## Manual Publish Acknowledgement + +By default, the MQTT5 client automatically sends a PUBACK for every QoS 1 PUBLISH it receives, immediately after the `OnPublishReceivedHandler` callback returns. Manual publish acknowledgement gives you control over when that PUBACK is sent, allowing you to defer acknowledgement until after your application has fully processed the message — for example, after persisting it to a database or forwarding it to another service. To take manual control of the PUBACK, call `eventData.acquirePublishAcknowledgement()` **within** the `OnPublishReceivedHandler` callback. This returns a `ScopedResource` that you can store and use later to send the PUBACK by calling `client->InvokePublishAcknowledgement()`. From 998111886e56e27a0d529867e8027334667893fe Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 13 Apr 2026 09:49:11 -0700 Subject: [PATCH 3/7] update changes to user guide --- documents/MQTT5_Userguide.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/documents/MQTT5_Userguide.md b/documents/MQTT5_Userguide.md index cf4ef6ae4..08ca3e138 100644 --- a/documents/MQTT5_Userguide.md +++ b/documents/MQTT5_Userguide.md @@ -24,9 +24,9 @@ - [Subscribe](#subscribe) - [Unsubscribe](#unsubscribe) - [Publish](#publish) +* [MQTT5 Best Practices](#mqtt5-best-practices) * [Advanced Operations and Settings](#advanced-operations-and-settings) + [Manual Publish Acknowledgement](#manual-publish-acknowledgement) -* [MQTT5 Best Practices](#mqtt5-best-practices) # Introduction @@ -759,9 +759,9 @@ If the PUBLISH was a QoS 1 publish, then the completion callback returns a PubAc ``` -# Advanced Operations and Settings +## Advanced Operations and Settings -## Manual Publish Acknowledgement +### Manual Publish Acknowledgement By default, the MQTT5 client automatically sends a PUBACK for every QoS 1 PUBLISH it receives, immediately after the `OnPublishReceivedHandler` callback returns. Manual publish acknowledgement gives you control over when that PUBACK is sent, allowing you to defer acknowledgement until after your application has fully processed the message — for example, after persisting it to a database or forwarding it to another service. @@ -769,6 +769,7 @@ To take manual control of the PUBACK, call `eventData.acquirePublishAcknowledgem **Important constraints:** * `acquirePublishAcknowledgement()` must be called within the `OnPublishReceivedHandler` callback. Calling it after the callback returns will return `nullptr`. +* `acquirePublishAcknowledgement()` may only be called once per received PUBLISH. Subsequent calls return `nullptr`. * This is only relevant for QoS 1 messages. For QoS 0 messages, `acquirePublishAcknowledgement()` returns `nullptr`. * If `acquirePublishAcknowledgement()` is not called (or returns `nullptr`), the client will automatically send the PUBACK when the callback returns. @@ -808,7 +809,7 @@ The following example shows how to acquire the acknowledgement handle within the // After processing is complete, send the PUBACK by invoking the acknowledgement. if (pendingAck != nullptr) { - if (!client->InvokePublishAcknowledgement(pendingAck)) + if (!client->InvokePublishAcknowledgement(*pendingAck)) { fprintf(stdout, "Failed to invoke publish acknowledgement.\n"); } From 32edbd526f4f6234fbf058c89fed7ee7af06f33f Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 13 Apr 2026 10:40:48 -0700 Subject: [PATCH 4/7] aws-crt-cpp -> v0.38.5 --- crt/aws-crt-cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crt/aws-crt-cpp b/crt/aws-crt-cpp index a1bf93b83..bca5d26fd 160000 --- a/crt/aws-crt-cpp +++ b/crt/aws-crt-cpp @@ -1 +1 @@ -Subproject commit a1bf93b8363dc1c7fb889fe1a743053a625c176d +Subproject commit bca5d26fd37b0d39ba8fbffb3058560f0d4d193f From da91608fb454d5d44a4045a9abcdcdac49807e7f Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Mon, 13 Apr 2026 10:58:47 -0700 Subject: [PATCH 5/7] add IoT Broker redelivery behavior notes to FAQ --- documents/FAQ.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/documents/FAQ.md b/documents/FAQ.md index f5d011db6..aace393b8 100644 --- a/documents/FAQ.md +++ b/documents/FAQ.md @@ -14,6 +14,7 @@ * [What certificates do I need?](#what-certificates-do-i-need) * [Where can I find MQTT 311 Samples?](#where-can-i-find-mqtt-311-samples) * [Certificate and Private Key Usage Across Different Versions of the SDK on macOS](#certificate-and-private-key-usage-across-different-versions-of-the-sdk-on-macos) +* [Manual Publish Acknowledgement and QoS 1 Redelivery](#manual-publish-acknowledgement-and-qos-1-redelivery) * [I still have more questions about this sdk?](#i-still-have-more-questions-about-this-sdk) ### Where should I start? @@ -180,6 +181,18 @@ The MQTT 3.1.1 samples can be found in the v1.40.0 samples folder [here](https:/ ### Certificate and Private Key Usage Across Different Versions of the SDK on macOS A certificate and private key pair cannot be shared on a macOS device between aws-iot-device-sdk-cpp-v2 v1.41.0 and any other versions. In the update to v1.41.0 we migrated macOS from using Apple's deprecated Security Framework to SecItem API. In doing so, certificate and private keys are imported in a non-backwards compatible manner into the Apple Keychain. +### Manual Publish Acknowledgement and QoS 1 Redelivery + +When using [manual publish acknowledgement](./MQTT5_Userguide.md#manual-publish-acknowledgement), there are two important behaviors to be aware of regarding QoS 1 message redelivery: + +**Broker redelivery of unacknowledged publishes** + +The AWS IoT broker will periodically resend unacknowledged QoS 1 PUBLISH packets. These redeliveries should be treated as duplicates even if the DUP flag in the PUBLISH packet is not set. If the manual publish acknowledgement is not acquired again for a redelivered packet, the acknowledgement will be sent automatically. + +**Session resumption after disconnect/reconnect** + +Upon a disconnect and reconnect of the MQTT5 client, if a session is resumed, any previously acquired acknowledgement handle is void. The broker will resend the unacknowledged PUBLISH packet, and the acknowledgement must be reacquired from that resent packet. If the resent packet is not handled for manual acknowledgement, the acknowledgement will be sent automatically. + ### I still have more questions about this SDK? * [Here](https://docs.aws.amazon.com/iot/latest/developerguide/what-is-aws-iot.html) are the AWS IoT Core docs for more details about IoT Core From d2be7edbf6a1b2fc39c09d1a91e137e6c23cf70a Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 14 Apr 2026 09:43:28 -0700 Subject: [PATCH 6/7] pr changes --- documents/MQTT5_Userguide.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/documents/MQTT5_Userguide.md b/documents/MQTT5_Userguide.md index 08ca3e138..ebaf47414 100644 --- a/documents/MQTT5_Userguide.md +++ b/documents/MQTT5_Userguide.md @@ -24,9 +24,9 @@ - [Subscribe](#subscribe) - [Unsubscribe](#unsubscribe) - [Publish](#publish) -* [MQTT5 Best Practices](#mqtt5-best-practices) * [Advanced Operations and Settings](#advanced-operations-and-settings) + [Manual Publish Acknowledgement](#manual-publish-acknowledgement) +* [MQTT5 Best Practices](#mqtt5-best-practices) # Introduction @@ -768,7 +768,7 @@ By default, the MQTT5 client automatically sends a PUBACK for every QoS 1 PUBLIS To take manual control of the PUBACK, call `eventData.acquirePublishAcknowledgement()` **within** the `OnPublishReceivedHandler` callback. This returns a `ScopedResource` that you can store and use later to send the PUBACK by calling `client->InvokePublishAcknowledgement()`. **Important constraints:** -* `acquirePublishAcknowledgement()` must be called within the `OnPublishReceivedHandler` callback. Calling it after the callback returns will return `nullptr`. +* `acquirePublishAcknowledgement()` must be called within the `OnPublishReceivedHandler` callback. Calling it after the callback returns or from a different thread will return `nullptr`. * `acquirePublishAcknowledgement()` may only be called once per received PUBLISH. Subsequent calls return `nullptr`. * This is only relevant for QoS 1 messages. For QoS 0 messages, `acquirePublishAcknowledgement()` returns `nullptr`. * If `acquirePublishAcknowledgement()` is not called (or returns `nullptr`), the client will automatically send the PUBACK when the callback returns. @@ -817,6 +817,14 @@ The following example shows how to acquire the acknowledgement handle within the ``` +**AWS IoT broker redelivery behavior** + +The AWS IoT broker will periodically resend unacknowledged QoS 1 PUBLISH packets. These redeliveries should be treated as duplicates even if the DUP flag in the PUBLISH packet is not set. If `acquirePublishAcknowledgement()` is not called again for a redelivered packet, the acknowledgement will be sent automatically. + +**Session resumption after disconnect/reconnect** + +Upon a disconnect and reconnect of the MQTT5 client, if a session is resumed, any previously acquired `ScopedResource` is void. The broker will resend the unacknowledged PUBLISH packet, and `acquirePublishAcknowledgement()` must be called again within the callback for that resent packet. If the resent packet is not handled for manual acknowledgement, the acknowledgement will be sent automatically. + # MQTT5 Best Practices From acc7e24c0c94e934e12e4cd9d4af7017a50b9bd1 Mon Sep 17 00:00:00 2001 From: Steve Kim Date: Tue, 14 Apr 2026 13:14:00 -0700 Subject: [PATCH 7/7] add manual puback feature to main README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 01fcdc747..cd66769bf 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ The primary purpose of the AWS IoT Device SDK for C++ v2 is to simplify the proc * Integrated service clients for AWS IoT Core services * Secure device connections to AWS IoT Core using MQTT protocol including MQTT 5.0 * Support for [multiple authentication methods and connection types](./documents/MQTT5_Userguide.md#connecting-to-aws-iot-core) +* Support for [manual publish acknowledgement](./documents/MQTT5_Userguide.md#manual-publish-acknowledgement) for control over QoS 1 PUBACK delivery #### Supported AWS IoT Core services