Skip to content

Commit

Permalink
Core mqtt migration guide update (#227)
Browse files Browse the repository at this point in the history
* Added placeholder for >2.0.0 migration guide.

* Update MigrationGuide.md

* Update MigrationGuide.md

* Update README.md

* Update migration guide.

* Remove CRLF endings.

* Fix extra whitespace.

* Remove trailing whitespaces.

* Update MigrationGuide.md

Co-authored-by: Aniruddha Kanhere <[email protected]>

* Update MigrationGuide.md

Co-authored-by: Aniruddha Kanhere <[email protected]>

* Update MigrationGuide.md

Co-authored-by: Aniruddha Kanhere <[email protected]>

* Update MigrationGuide.md

Co-authored-by: Aniruddha Kanhere <[email protected]>

* Update MigrationGuide.md

Co-authored-by: Jason Carroll <[email protected]>
Co-authored-by: Aniruddha Kanhere <[email protected]>
  • Loading branch information
3 people authored Oct 13, 2022
1 parent 2775242 commit 885db0c
Show file tree
Hide file tree
Showing 2 changed files with 237 additions and 0 deletions.
234 changes: 234 additions & 0 deletions MigrationGuide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
## coreMQTT version >=v2.0.0 Migration Guide

With coreMQTT versions >=v2.0.0, there are some breaking changes that need to be addressed when upgrading.

### Breaking Changes

* The `MQTT_ProcessLoop` function no longer uses a timeout as this led to unavoidable busy-waiting. Thus, the signature of `MQTT_ProcessLoop` changed from `MQTTStatus_t MQTT_ProcessLoop( MQTTContext_t * pContext, uint32_t timeoutMs )` to `MQTTStatus_t MQTT_ProcessLoop( MQTTContext_t * pContext )`. Additionally, `MQTT_ProcessLoop` can now return `MQTTNeedMoreBytes`. A return of `MQTTNeedMoreBytes` means that `MQTT_ProcessLoop` received only a part of an MQTT packet and will need to be called again (probably after a bit of delay) in order to finish receiving the MQTT packet. Thus, to migrate, simply remove the timeout from the `MQTT_ProcessLoop` call and additionally check for if `MQTTNeedMoreBytes` has been returned when checking the status of `MQTT_ProcessLoop`. For example:

**Old Code Snippet**:
```
// Variables used in this example.
MQTTStatus_t status;
uint32_t timeoutMs = 100;
// This context is assumed to be initialized and connected.
MQTTContext_t * pContext;
while( true )
{
status = MQTT_ProcessLoop( pContext, timeoutMs );
if( status != MQTTSuccess )
{
// Determine the error. It's possible we might need to disconnect
// the underlying transport connection.
}
else
{
// Other application functions.
}
}
```
**New Code Snippet**:
```
// Variables used in this example.
MQTTStatus_t status;
// This context is assumed to be initialized and connected.
MQTTContext_t * pContext;
while( true )
{
status = MQTT_ProcessLoop( pContext );
if( status != MQTTSuccess && status != MQTTNeedMoreBytes )
{
// Determine the error. It's possible we might need to disconnect
// the underlying transport connection.
}
/* else if only required if the terminating else is empty and the application doesn't want a scenario akin to busy waiting. */
else if( status == MQTTNeedMoreBytes )
{
/* Only a partial MQTT packet is received. Call MQTT_ProcessLoop again; ideally after a small delay. */
}
else
{
// Other application functions.
}
}
```

* The `MQTT_ReceiveLoop` function no longer uses a timeout as this led to unavoidable busy-waiting. Thus, the signature of `MQTT_ReceiveLoop` changed from `MQTTStatus_t MQTT_ReceiveLoop( MQTTContext_t * pContext, uint32_t timeoutMs )` to `MQTTStatus_t MQTT_ReceiveLoop( MQTTContext_t * pContext )`. Additionally, `MQTT_ReceiveLoop` can now return `MQTTNeedMoreBytes`. A return of `MQTTNeedMoreBytes` means that `MQTT_ReceiveLoop` received only a part of an MQTT packet and will need to be called again (probably after a bit of delay) in order to finish receiving the MQTT packet. Thus, to migrate, simply remove the timeout from the `MQTT_ReceiveLoop` call and additionally check for if `MQTTNeedMoreBytes` has been returned when checking the status of `MQTT_ReceiveLoop`. For example:

**Old Code Snippet**:
```
// Variables used in this example.
MQTTStatus_t status;
uint32_t timeoutMs = 100;
// This context is assumed to be initialized and connected.
MQTTContext_t * pContext;
while( true )
{
status = MQTT_ReceiveLoop( pContext, timeoutMs );
if( status != MQTTSuccess )
{
// Determine the error. It's possible we might need to disconnect
// the underlying transport connection.
}
else
{
// Other application functions.
}
}
```
**New Code Snippet**:
```
// Variables used in this example.
MQTTStatus_t status;
// This context is assumed to be initialized and connected.
MQTTContext_t * pContext;
while( true )
{
status = MQTT_ReceiveLoop( pContext );
if( status != MQTTSuccess && status != MQTTNeedMoreBytes )
{
// Determine the error. It's possible we might need to disconnect
// the underlying transport connection.
}
/* else if only required if the terminating else is empty and the application doesn't want a scenario akin to busy waiting. */
else if( status == MQTTNeedMoreBytes )
{
/* Only a partial MQTT packet is received. Call MQTT_ReceiveLoop again; ideally after a small delay. */
}
else
{
// Other application functions.
}
}
```

* The `TransportInterface_t` structure now has a new member `writev`. It uses scatter-gather approach to send multiple MQTT packet components as a single packet to reduce overhead and improve performance. However, it is COMPLETELY OPTIONAL to implement. To that end, when application(s) initialize a `TransportInterface_t` structure, they **MUST** either set `writev` to a working implementation or set it `NULL`. Not doing this will lead to undefined behavior as the coreMQTT library checks if `writev` is `NULL` to determine if it should be used. For example:

**Old Code Snippet**:
```
TransportInterface_t transport;
// Set transport interface members.
transport.pNetworkInterface = &someNetworkInterface;
transport.send = networkSend;
transport.recv = networkRecv;
```
**New Code Snippet**:
```
TransportInterface_t transport;
// Set transport interface members.
transport.pNetworkInterface = &someNetworkInterface;
transport.send = networkSend;
transport.recv = networkRecv;
transport.writev = NULL;
```

* The `MQTT_Init` function no longer creates buffers to handle QoS > 0 packets, so if planning to use QoS > 0, the `MQTT_InitStatefulQoS` function must also be called on an `MQTTContext_t` after calling `MQTT_Init` on it and before using any other coreMQTT functions with it. If not using QoS > 0, `MQTT_InitStatefulQoS` does not need to be called. For example (code that uses QoS > 0):

**Old Code Snippet**:
```
// Function for obtaining a timestamp.
uint32_t getTimeStampMs();
// Callback function for receiving packets.
void eventCallback(
MQTTContext_t * pContext,
MQTTPacketInfo_t * pPacketInfo,
MQTTDeserializedInfo_t * pDeserializedInfo
);
// Network send.
int32_t networkSend( NetworkContext_t * pContext, const void * pBuffer, size_t bytes );
// Network receive.
int32_t networkRecv( NetworkContext_t * pContext, void * pBuffer, size_t bytes );
MQTTContext_t mqttContext;
TransportInterface_t transport;
MQTTFixedBuffer_t fixedBuffer;
uint8_t buffer[ 1024 ];
// Clear context.
memset( ( void * ) &mqttContext, 0x00, sizeof( MQTTContext_t ) );
// Set transport interface members.
transport.pNetworkInterface = &someNetworkInterface;
transport.send = networkSend;
transport.recv = networkRecv;
// Set buffer members.
fixedBuffer.pBuffer = buffer;
fixedBuffer.size = 1024;
status = MQTT_Init( &mqttContext, &transport, getTimeStampMs, eventCallback, &fixedBuffer );
if( status == MQTTSuccess )
{
// Do something with mqttContext. The transport and fixedBuffer structs were
// copied into the context, so the original structs do not need to stay in scope.
}
```
**New Code Snippet**:
```
// Function for obtaining a timestamp.
uint32_t getTimeStampMs();
// Callback function for receiving packets.
void eventCallback(
MQTTContext_t * pContext,
MQTTPacketInfo_t * pPacketInfo,
MQTTDeserializedInfo_t * pDeserializedInfo
);
// Network send.
int32_t networkSend( NetworkContext_t * pContext, const void * pBuffer, size_t bytes );
// Network receive.
int32_t networkRecv( NetworkContext_t * pContext, void * pBuffer, size_t bytes );
MQTTContext_t mqttContext;
TransportInterface_t transport;
MQTTFixedBuffer_t fixedBuffer;
uint8_t buffer[ 1024 ];
static MQTTPubAckInfo_t pOutgoingPublishRecords[ OUTGOING_PUBLISH_RECORD_COUNT ];
static MQTTPubAckInfo_t pIncomingPublishRecords[ INCOMING_PUBLISH_RECORD_COUNT ];
// Clear context.
memset( ( void * ) &mqttContext, 0x00, sizeof( MQTTContext_t ) );
// Set transport interface members.
transport.pNetworkInterface = &someNetworkInterface;
transport.send = networkSend;
transport.recv = networkRecv;
transport.writev = NULL;
// Set buffer members.
fixedBuffer.pBuffer = buffer;
fixedBuffer.size = 1024;
status = MQTT_Init( &mqttContext, &transport, getTimeStampMs, eventCallback, &fixedBuffer );
if( status == MQTTSuccess )
{
// Initialize MQTT context for QoS > 0. This only has to be done if
// performing QoS > 0 operations.
status = MQTT_InitStatefulQoS(pxMQTTContext,
pOutgoingPublishRecords,
OUTGOING_PUBLISH_RECORD_COUNT,
pIncomingPublishRecords,
INCOMING_PUBLISH_RECORD_COUNT);
}
if( status == MQTTSuccess )
{
// Do something with mqttContext. The transport and fixedBuffer structs were
// copied into the context, so the original structs do not need to stay in scope.
}
```

* For coreMQTT version >=v2.1.0, the `MQTT_SEND_RETRY_TIMEOUT_MS` macro configuration has been deprecated. It has been replaced with `MQTT_SEND_TIMEOUT_MS`. `MQTT_SEND_RETRY_TIMEOUT_MS` was formerly used to timeout sending an MQTT packet after the transport `send` function failed to transmit any bytes for too long. This timeout would reset each time the transport `send` function sent any bytes, leading to the actual timeout scaling with the number of bytes being sent. In other words, the maximum time for sending a packet was variable. In order to remedy this, the `MQTT_SEND_RETRY_TIMEOUT_MS` macro was replaced with `MQTT_SEND_TIMEOUT_MS`. `MQTT_SEND_TIMEOUT_MS` now sets the maximum duration that coreMQTT will attempt to send an MQTT packet, leading to more consistent timeout behavior across various APIs and packet lengths.

### Additional Changes

* The `MQTT_CancelCallback` function has been added to allow a program to prevent the event callback from being called when receiving an ACK for a sent packet. For example, if a program sends a publish with packet ID 2 and QoS > 0 using `MQTT_Publish`, the program could then call `MQTT_CancelCallback` on packet ID 2 to prevent coreMQTT from calling the event callback when it receives the `PUBACK` for packet ID 2.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ connectInfo.userNameLength = USERNAME_STRING_LENGTH;
mqttStatus = MQTT_Connect( pMqttContext, &connectInfo, NULL, CONNACK_RECV_TIMEOUT_MS, pSessionPresent );
```

## Upgrading to v2.0.0 and above

With coreMQTT versions >=v2.0.0, there are breaking changes. Please refer to the [coreMQTT version >=v2.0.0 Migration Guide](MigrationGuide.md).

## Building the Library

Expand Down

0 comments on commit 885db0c

Please sign in to comment.