Search code examples
c#azureasynchronousasync-awaitamqp

DeviceMessageLockLostException is thrown from Azure Device Client when calling CompleteAsync method sometimes


I am trying to receive the message from Azure Device Client as below

public async Task<List<string>> RecieveMessage(string correlationId)
        {
            var response = new List<string>();
            InitializeDeviceClient("AMQP");
            var flag = true;
            while (flag)
            {
                Microsoft.Azure.Devices.Client.Message receivedMessage = await deviceClient.ReceiveAsync(TimeSpan.FromSeconds(120));

                if (receivedMessage == null)
                {
                    await Task.Delay(100).ConfigureAwait(false);
                    continue;
                }

                Trace.WriteLine(receivedMessage.CorrelationId.ToString());
                await this.deviceClient.CompleteAsync(receivedMessage);
                if (receivedMessage.CorrelationId != correlationId)
                {
                    continue;
                }  
               
                var content = Encoding.UTF8.GetString(receivedMessage.GetBytes());
                response.Add(content);
                flag = false;
            }
            return response;
        }

When I filter the message based on particular 'CorrelationId' then I return the response.

Here at the step when I call 'CompleteAsync' exception is seen intermittently when running from VSTS.

Microsoft.Azure.Devices.Client.Exceptions.DeviceMessageLockLostException: Exception of type 'Microsoft.Azure.Devices.Client.Exceptions.DeviceMessageLockLostException' was thrown.

Stacktrace:

Microsoft.Azure.Devices.Client.Transport.AmqpIoT.AmqpIoTOutcome.ThrowIfError()
   at Microsoft.Azure.Devices.Client.Transport.Amqp.AmqpTransportHandler.DisposeMessageAsync(String lockToken, AmqpIoTDisposeActions outcome)
   at Microsoft.Azure.Devices.Client.Transport.ErrorDelegatingHandler.<>c__DisplayClass23_0.<<ExecuteWithErrorHandlingAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.Azure.Devices.Client.Transport.ErrorDelegatingHandler.ExecuteWithErrorHandlingAsync[T](Func`1 asyncOperation)
   at Microsoft.Azure.Devices.Client.Transport.RetryDelegatingHandler.<>c__DisplayClass26_0.<<CompleteAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.Azure.Devices.Client.Transport.RetryDelegatingHandler.CompleteAsync(String lockToken, CancellationToken cancellationToken)
   at Microsoft.Azure.Devices.Client.InternalClient.CompleteAsync(String lockToken)

Solution

  • According to GH issue#117 and #111, the DeviceMessageLockLostException is part of normal operation when a C2D message's TTL expired before it was acked by the device.

    I will add the recommendation here:

    When you get a DeviceMessageLockLostException, the calling application should just trace it, ignore it and proceed. One of two things will happen:

    DeviceClient receives the same message again on ReceiveAsync(). At this point, the DeviceClient can try to complete the message again.

    DeviceClient never receives the same message again, in which case no action required anyways.