Azure functions with service bus: Unable to "complete" and access properties of a brokered message - azure

This is my working code in a console app:
Writer (working code):
static void Main(string[] args)
{
Console.WriteLine("Starting..");
string connectionString = CloudConfigurationManager.GetSetting("Microsoft.ServiceBus.ConnectionString");
QueueClient Client = QueueClient.CreateFromConnectionString(connectionString, "xxx");
// Create message, passing a string message for the body.
BrokeredMessage message = new BrokeredMessage("");
// Set some addtional custom app-specific properties.
message.Properties["UserCode"] = "HELLOOO22353";
message.Properties["UserId"] = "4511";
try
{
// Send message to the queue.
Client.Send(message);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.WriteLine("Complete..");
//Console.ReadKey();
}
Reader: (working code)
static void Main(string[] args)
{
Console.WriteLine("Starting");
string connectionString = CloudConfigurationManager.GetSetting("Microsoft.ServiceBus.ConnectionString");
QueueClient Client = QueueClient.CreateFromConnectionString(connectionString, "xxx");
OnMessageOptions options = new OnMessageOptions();
options.AutoComplete = false;
options.AutoRenewTimeout = TimeSpan.FromMinutes(1);
Client.OnMessage((message) =>
{
try
{
string sMessage = " UserCode: " + message.Properties["UserCode"];
Console.WriteLine("Found new User - " + sMessage);
// Remove message from queue.
message.Complete();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
// Indicates a problem, unlock message in queue.
message.Abandon();
}
}, options);
Console.ReadKey();
}
Now, this is the code I have in Azure functions which isn't working:
public static void Run(BrokeredMessage myMessage, TraceWriter log)
{
log.Info($"C# ServiceBus queue trigger function processed message: {myMessage}");
log.Info("1111");
log.Info(myMessage.MessageId); // this works
myMessage.Complete(); // fails: Microsoft.ServiceBus: Operation is not valid due to the current state of the object.
log.Info(myMessage.Properties["UserCode"].ToString()); // fails: myMessage.Properties is empty for some reason
}
What I'm failing to understand is why the Reader console app is able to read and complete message correctly but Azure function one (which is essentially based on the same idea) isn't. Both the codes are using the same version of Windows.ServiceBus package.

You do not need to complete the message yourself when using the ServiceBus trigger with Azure Functions. The ServiceBus trigger automatically uses PeekLock mode and will handle automatically completing, abandoning and renewing the message lock for you. You can also take control the finer details of this behavior via host.json settings for your function.

Related

Application Insights + Azure Service Bus - Capture message from the bus in dependency tracking

I am looking into how the app insights work with different types of dependencies. I have a question on using the AppInsights ASP Net Core sdk with Messaging Service Bus sdk.
How can I capture messages to service bus while sending or receiving using this sdk in dependency? I understand that this is not something we would like to log all the time and I will make it configurable.
Thanks
You can create your own sender and implement the desired logic inside the SendMessageAsync method:
public class TelemetryEnabledServiceBusSender : ServiceBusSender
{
private readonly TelemetryClient telemetryClient;
internal TelemetryEnabledServiceBusSender(ServiceBusClient client, string topic, TelemetryClient telemetryClient) : base(client, topic)
{
this.telemetryClient = telemetryClient;
}
public override async Task SendMessageAsync(ServiceBusMessage message, CancellationToken cancellationToken = default)
{
telemetryClient.TrackTrace(message.Body.ToString());
await base.SendMessageAsync(message, cancellationToken);
}
}
use it like this:
var serviceBusSender = new TelemetryEnabledServiceBusSender(serviceBusClient, serviceBusData.Topic, telemetryClient);
await serviceBusSender.SendMessageAsync(message);
Logging processed messages is even simpler and can be done using the ServiceBusProcessor
serviceBusProcessor.ProcessMessageAsync += ProcessMessageAsync;
...
private async Task ProcessMessageAsync(ProcessMessageEventArgs arg)
{
telemetryClient.TrackTrace(arg.Message.Body.ToString());
...
}
Adding my approach as an answer here as it is too long to comment.
var telemetry = new DependencyTelemetry(
"Azure Service Bus",
_serviceBusClient.FullyQualifiedNamespace,
"ServiceBusReceiver.Receive",
string.Empty
);
using var operation =
_telemetryClient.StartOperation(telemetry);
try
{
var receivedMessage = await _serviceBusReceiver.ReceiveMessageAsync();
if (receivedMessage?.Body is not null)
{
message = receivedMessage.Body.ToString();
telemetry.Data = message;
// do something
}
telemetry.Success = true;
//Consider set telemetry duration
}
catch (Exception e)
{
// log exception
_telemetryClient.TrackException(e);
telemetry.Success = false;
throw;
}
finally
{
_telemetryClient.TrackTrace("Done");
_telemetryClient.StopOperation(operation);
}
Thanks to #Peter Bons

Azure Service Bus | Enable Session | Session does not receive

I have a requirement to process the same set of messages together and for this, I was trying Azure Service Bus Sessions Enabled feature. To test this, I created a very simple application, a message is submitted successfully in a queue, however, while trying to receive the message in "ReceiveSessionMessage" function, a message session is not returned and the program exits after this line.
I am not able to figure out the exact root cause, any help would be much appreciated. Thanks
[var messageSession = await
sessionClient.AcceptMessageSessionAsync();]
Program
using Microsoft.Azure.ServiceBus;
using System;
using System.Text;
using System.Threading.Tasks;
namespace TestSendReceiveMessagesAzure
{
class Program
{
static string connectionString = "";
static string queueName = "demosessionqueue";
static void Main(string[] args)
{
Console.WriteLine("Test Service Bus Session! enable feature");
SendMessage();
Console.WriteLine("Message Pushed");
ReceiveSessionMessage();
}
private static void SendMessage()
{
QueueClient queueClilent = new QueueClient(connectionString, queueName, ReceiveMode.PeekLock);
string msgJson = "{PizzaType:Veggie,SessionID:SessionId0101}";
Message message = new Message(Encoding.UTF8.GetBytes(msgJson))
{
SessionId = "SessionId0101"
};
Console.WriteLine(msgJson);
queueClilent.SendAsync(message).Wait();
}
private static async Task ReceiveSessionMessage()
{
var sessionClient = new SessionClient(connectionString, queueName, ReceiveMode.PeekLock);
Console.WriteLine("Accepting a message session...");
try
{
var messageSession = await sessionClient.AcceptMessageSessionAsync();
Console.WriteLine($"Message.SessionID={messageSession.SessionId}");
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
}
}
}
Console Output
The issue is with the declaration of
static void Main(string[] args) , and the calling method "ReceiveSessionMessage()" in it. The correct way of calling this function from the Program.cs was
static async Task Main(string[] args)
{
Console.WriteLine("Message Session Handler..");
await MessageSessionReceiver();
}
"ReceiveSessionMessage" function was an async function and the calling function did not have the await keyword mentioned due to which the program exited. After changing the syntax to add await, it worked.

Returned Azure service bus queue sequence number different in my consumer than what was returned in the producer and shown in the Azure portal?

When I create a scheduled service bus message, both in Azure Portal and in my app using the Service bus producer code (below) and I receive a sequence number. I save it in my db.
Problem - When my Service bus consumer code is triggered by the dequeue of the scheduled message the sequence number is different than the one that was initially given to me by both the service bus producer code and through the Azure portal.
Shown here, where '13' is the sequnce number shown in Azure Portal screen.
Here is the code that receives the scheduled message and you can see the sequence number is different!
Here is my consumer code (don't think it matters)
private async Task MessageHandler(ProcessMessageEventArgs args)
{
string body = args.Message.Body.ToString();
JObject jsonObject = JObject.Parse(body);
var eventStatus = (string)jsonObject["EventStatus"];
await args.CompleteMessageAsync(args.Message);
// fetch row here by sequence number
// edit some data from entity, then save
int result = await dbContext.SaveChangesAsync();
}
Here is my producer code
public async Task<long> SendMessage(string messageBody, DateTimeOffset scheduledEnqueueTimeUtc)
{
await using (ServiceBusClient client = new ServiceBusClient(_config["ServiceBus:Connection"]))
{
ServiceBusSender sender = client.CreateSender(_config["ServiceBus:Queue"]);
ServiceBusMessage message = new ServiceBusMessage(messageBody);
var sequenceNumber = await sender.ScheduleMessageAsync(message, scheduledEnqueueTimeUtc);
return sequenceNumber;
}
}
From the documentation:
The SequenceNumber for a scheduled message is only valid while the message is in this state. As the message transitions to the active state, the message is appended to the queue as if had been enqueued at the current instant, which includes assigning a new SequenceNumber.
This is the code on my side:
using System;
using System.Threading;
using System.Threading.Tasks;
using Azure.Messaging.ServiceBus;
namespace ConsoleApp3
{
class Program
{
static string connectionString = "xxxxxx";
static string queueName = "myqueue";
static ServiceBusClient client;
static ServiceBusProcessor processor;
static async Task Main(string[] args)
{
client = new ServiceBusClient(connectionString);
processor = client.CreateProcessor(queueName, new ServiceBusProcessorOptions());
try
{
processor.ProcessMessageAsync += MessageHandler;
processor.ProcessErrorAsync += ErrorHandler;
await processor.StartProcessingAsync();
Console.WriteLine("Wait for a minute and then press any key to end the processing");
Console.ReadKey();
Console.WriteLine("\nStopping the receiver...");
await processor.StopProcessingAsync();
Console.WriteLine("Stopped receiving messages");
}
finally
{
await processor.DisposeAsync();
await client.DisposeAsync();
}
}
static async Task MessageHandler(ProcessMessageEventArgs args)
{
string body = args.Message.Body.ToString();
Console.WriteLine($"Received: {body}");
Console.WriteLine($"ID: {args.Message.MessageId}");
await args.CompleteMessageAsync(args.Message);
}
static Task ErrorHandler(ProcessErrorEventArgs args)
{
Console.WriteLine(args.Exception.ToString());
return Task.CompletedTask;
}
}
}
And it seems no problem on my side:
Message Id changed should be the message be thrown back by some reasons.

Azure Service Bus - must add fake message before send the real one - why?

I'm facing a strange issue, and I ran out of the possible causes. The scenario is
Fetch incoming message from queue
Process it and then add new message to another queue
but the thing is, if I finish the long running task for the incoming message, and then try to add new message to another queue, I don't receive it. If I just add a face message to that another queue, then I am able to receive the real message after the long-running operation is finished. But why ? I don't want to put any fake messages to the queue, but without that my scenario doesn't work. Any ideas ?
public class WorkerRole : RoleEntryPoint
{
// QueueClient is thread-safe. Recommended that you cache
// rather than recreating it on every request
Microsoft.ServiceBus.Messaging.QueueClient Client;
ManualResetEvent CompletedEvent = new ManualResetEvent(false);
public override void Run()
{
MyResult result = null;
var queueClient = new Microsoft.Azure.ServiceBus.QueueClient("QueueConnectionString", "QueueName");
Client.OnMessage(async (receivedMessage) =>
{
try
{
using (Stream stream = receivedMessage.GetBody<Stream>())
{
using (StreamReader reader = new StreamReader(stream))
{
string json = reader.ReadToEnd();
OCRQueueItem_Incoming item = JsonConvert.DeserializeObject<IncomingClass>(json);
var someClass = new OCRManager();
var message = new Message(Encoding.UTF8.GetBytes("test 1"));
await queueClient.SendAsync(message);
result = new SomeManager().RunLongRunningTask(item); //it runs for 1-2min
}
}
}
catch (Exception ex) { }
finally
{
var json = JsonConvert.SerializeObject(result);
var message = new Message(Encoding.UTF8.GetBytes(json));
await queueClient.SendAsync(message);
}
});
CompletedEvent.WaitOne();
}
public override bool OnStart()
{
ServicePointManager.DefaultConnectionLimit = 12;
string connectionString = CloudConfigurationManager.GetSetting("Queue.ConnectionString");
Client = Microsoft.ServiceBus.Messaging.QueueClient.Create(connectionString);
return base.OnStart();
}
public override void OnStop()
{
Client.Close();
CompletedEvent.Set();
base.OnStop();
}
}

Azure Functions: Servicebustrigger -> BrokeredMessage been disposed

I been writting alot of Webjobs with similar functionallity where similar code works just fine. But with Azure Functions instead I get error sometimes.
[FunctionName("Alert")]
public static async void Alert([ServiceBusTrigger(Topic.Alert, Subscription.Sql, AccessRights.Listen, Connection = "servicebusconnectionstring")] BrokeredMessage message, TraceWriter log)
{
using (var stream = message.GetBody<Stream>())
{
using (var memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
var json = Encoding.UTF8.GetString(memoryStream.ToArray());
try
{
throw new Exception("Test-Exception");
}
catch (Exception e)
{
EventLogger.LoggException("Function.Sql.Alert", e, new Dictionary<string, string>() { { "Messsage", json } });
if (message.DeliveryCount >= 5)
{
EventLogger.LoggEvent("DeadLetterQueue", new Dictionary<string, string>() { { "Function", "Function.Sql.Alert" }, { "Messsage", json } });
await QueueService.SendAsync(Queue.Deadletter, JsonConvert.DeserializeObject<CloudAlert>(json));
await message.CompleteAsync();
}
else
await message.AbandonAsync();
}
}
}
await message.CompleteAsync();
}
The issue is when I call message.AbandonAsync() or message.CompleteAsync it sometimes throw
System.ObjectDisposedException: BrokeredMessage has been disposed.
If I don't call message.CompleteAsync() at the end the message is still marked as completed. I could live with that, but I want to be able to Abandon the message as well and that function dosn't always work either.
Someone done something similar and have a solution? Using .NET Standard 2.0 and following NuGet package for ServiceBus: Microsoft.Azure.ServiceBus v2.0.0
You should not Complete or Abandon messages manually. Azure Functions runtime will do it for you based on success of failure (exception) of your function execution.
So, throw an exception if you want to Abandon. It will be auto-retried again up until Max Delivery Count limit.

Resources