An Azure service that enables bidirectional communication between internet of things (IoT) devices and applications.
We might face out-of-sequence issue could be due to redelivery and message acknowledgment as you mentioned in description above.
"When IoT Hub puts a message in a device message queue, it sets the message state to Enqueued. When a device thread takes a message from the queue, IoT Hub locks the message by setting the message state to Invisible. This state prevents other threads on the device from processing the same message. When a device thread successfully completes the processing of a message, it notifies IoT Hub and then IoT Hub sets the message state to Completed.
A device application that successfully receives and processes a message is said to Complete the message. However, if necessary a device can also:
- Reject the message, which causes IoT Hub to set it to the Dead lettered state. Devices that connect over the Message Queuing Telemetry Transport (MQTT) protocol can't reject cloud-to-device messages.
- Abandon the message, which causes IoT Hub to put the message back in the queue, with the message state set to Enqueued. Devices that connect over the MQTT protocol can't abandon cloud-to-device messages.
"
Reference -
Below are recommendation to remediate out of sequence issues.
- Enable retry logic and adjust the TTL, retry setting and other properties for successful completion of messages
- Using a sequence number in messages through code might arrange the sequence of received message.
Sample code (using sequence number) for reference.
import asyncio
from azure.iot.device.aio import IoTHubDeviceClient
from azure.iot.device import Message
# Environment variable IOTHUB_DEVICE_CONNECTION_STRING must be set
# e.g., export IOTHUB_DEVICE_CONNECTION_STRING="HostName=...;DeviceId=...;SharedAccessKey=..."
# Keeps track of last sequence processed
last_processed_sequence = -1
# Buffer for out of order C2D messages
pending_messages = {}
async def process_in_order_messages(client):
global last_processed_sequence, pending_messages
while True:
# If the next expected message is buffered, process it
next_seq = last_processed_sequence + 1
if next_seq in pending_messages:
msg = pending_messages.pop(next_seq)
# Business logic for this message
print(f"[In order] Seq={next_seq}, Body={msg.data.decode('utf-8')}")
# Complete the message so IoT Hub removes it
await client.complete(msg)
print(f"Completed Seq={next_seq}")
last_processed_sequence = next_seq
else:
# Nothing to process now – break and wait for new messages
break
async def main():
global pending_messages
# Create the client
client = IoTHubDeviceClient.create_from_connection_string(
connection_string=None # Uses environment variable
)
await client.connect()
print("Connected to IoT Hub, waiting for C2D messages...")
try:
async for msg in client.receive_messages(): # async receive loop
# Extract sequence number property if present
seq_prop = msg.custom_properties.get("sequenceNumber")
seq = int(seq_prop) if seq_prop is not None else None
print(f"Received C2D: Seq={seq}, Body={msg.data.decode('utf-8')}")
if seq is None:
# No sequence property: just complete immediately
await client.complete(msg)
print("Completed unsequenced C2D message")
else:
# Buffer and order
pending_messages[seq] = msg
# Try to process any in‑order messages
await process_in_order_messages(client)
except Exception as e:
print(f"Error receiving messages: {e}")
finally:
await client.disconnect()
if __name__ == "__main__":
asyncio.run(main())
Hope it helps address the out of sequence issue.
Thank you.