Azure Function - Queue Trigger - confusion over library being used (C#) - azure

I've created a simple Azure function which is triggered on a service bus queue.
I'd like to inject a the full message payload into the trigger. Now there are 2 libraries available for working with Azure Service Bus - the older https://www.nuget.org/packages/Microsoft.Azure.ServiceBus/ and the newer https://www.nuget.org/packages/Azure.Messaging.ServiceBus/
If I inject in the message payload using Microsoft.Azure.ServiceBus.Message works, however using the newer Azure.Messaging.ServiceBus.ServiceBusMessage gives a run-time error.
Ideally would like to use the newer library - is there any config change required to make this work?
The error is
Executed 'IncomingMessageProcessingFunction' (Failed, Id=01b3f0c1-fe4d-4d0f-89db-7814a4a0ddbe, Duration=2756ms)
[2021-09-13T10:56:47.944Z] System.Private.CoreLib: Exception while executing function: IncomingMessageProcessingFunction. Microsoft.Azure.WebJobs.Host: Exception binding parameter 'myQueueItem'. Microsoft.Azure.WebJobs.ServiceBus: Binding parameters to complex objects (such as 'ServiceBusMessage') uses Json.NET serialization or XML object serialization.
[2021-09-13T10:56:47.946Z] 1. If ContentType is 'application/json' deserialize as JSON
[2021-09-13T10:56:47.948Z] 2. If ContentType is not 'application/json' attempt to deserialize using Message.GetBody, which will handle cases like XML object serialization
[2021-09-13T10:56:47.949Z] 3. If this deserialization fails, do a final attempt at JSON deserialization to catch cases where the content type might be incorrect
[2021-09-13T10:56:47.951Z] The JSON parser failed: Error converting value 123 to type 'Azure.Messaging.ServiceBus.ServiceBusMessage'. Path '', line 1, position 3.
Works
[FunctionName("IncomingMessageProcessingFunction")]
public static void Run([ServiceBusTrigger("incomingmessageprocessingqueue", Connection = "SERVICEBUS")]Microsoft.Azure.ServiceBus.Message myQueueItem, ILogger log)
{
log.LogInformation($"C# ServiceBus queue trigger function processed message: {myQueueItem.Body}");

Related

OData PATCH request results in IllegalArgumentException

Since last week we started using SDK version 3.34.1 (and also tested this with 3.35.0). When we send a PATCH request to a SAP service we get a HTTP 204 No-Content response back from our SAP service (SAP Gateway). When the SDK tries to read that response, it tries to parse the response body which is empty. This leads to the following exception:
2020-12-17 16:13:51.767 ERROR 106363 --- [ut.sap.cases]-0] .s.c.s.d.o.c.r.ODataRequestResultGeneric :
Failed to buffer HTTP response. Unable to buffer HTTP entity.
java.lang.IllegalArgumentException: Wrapped entity may not be null
at org.apache.http.util.Args.notNull(Args.java:54)
at org.apache.http.entity.HttpEntityWrapper.<init>(HttpEntityWrapper.java:59)
at org.apache.http.entity.BufferedHttpEntity.<init>(BufferedHttpEntity.java:59)
at com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric.lambda$getHttpResponse$4f00ca4e$1(ODataRequestResultGeneric.java:180)
at io.vavr.control.Try.of(Try.java:75)
at com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestResultGeneric.getHttpResponse(ODataRequestResultGeneric.java:180)
at com.sap.cloud.sdk.datamodel.odata.client.request.ODataHealthyResponseValidator.requireHealthyResponse(ODataHealthyResponseValidator.java:44)
at io.vavr.control.Try.andThenTry(Try.java:250)
at com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestGeneric.tryExecute(ODataRequestGeneric.java:194)
at com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestGeneric.tryExecuteWithCsrfToken(ODataRequestGeneric.java:225)
at com.sap.cloud.sdk.datamodel.odata.client.request.ODataRequestUpdate.execute(ODataRequestUpdate.java:136)
at com.sap.cloud.sdk.datamodel.odata.helper.FluentHelperUpdate.executeRequest(FluentHelperUpdate.java:372)
at com.alliander.gvrn.pmd.adapter.out.sap.cases.SapCasesClient.updateCase(SapCasesClient.java:103)
at com.alliander.gvrn.pmd.adapter.out.sap.cases.SapCasesClient.persistOn(SapCasesClient.java:81)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at ....
We use generated typed OData V2 client, which are generated by providing our EDMX files a per the SDK documentation.
Below a code snippet of the function that's calling the service. The matrixCase is a autogenerated object. The OData PATCH is properly handled by the SAP service.
private void updateCase(final ExternalId caseId, final PMDFlow pmdFlow, String jwtToken) {
final HttpDestination sapMatrix = httpDestinationProvider.providePrincipalPropagationDestination(jwtToken);
// "Create matrixCase object with key
MatrixCase matrixCase = MatrixCase.builder()
.psReference(caseId.getValue())
.build();
// Set PmdAppControl explicitly, otherwise the generated client doesn't know which fields are updated.
matrixCase.setPMDAppControl(pmdFlow.getSapNotation());
try {
casesService
.updateMatrixCase(matrixCase)
.executeRequest(sapMatrix);
} catch (ODataException e) {
OdataLogger.logODataException(e);
throw new SapClientException(e);
}
}
We've updated to SDK 3.34.1 due to other issues, however before we used 3.32.0 and I don't remember having this issue in version 3.32.0
Any ideas?
Danny
Yes, your observation is correct that 204 represents a valid answer and is not worth logging an error. Hence, the Cloud SDK team will adjust the log entry level to be less alarming.
Regards,
Tanvi

Cannot bind parameter 'lockToken' in Azure function app in .net framework?

I was using .net core Azure function in an app, with no problem faced, with the below code.
But when I create an Azure function app using .net framework it is throwing below error.
I tried all possible google solutions. the error is clearly because of nuget and library as same code works fine for v2 .net core function app.
[FunctionName("Function1")]
public static async System.Threading.Tasks.Task RunAsync([ServiceBusTrigger("topic", "sub", Connection = "ConnectionStringSettingName")]Message mySbMsg, string lockToken, MessageReceiver messageReceiver, ILogger log)
{
log.LogInformation($"C# ServiceBus topic trigger function processed message: {mySbMsg}");
}
I even tried this as well:
[FunctionName("Function1")]
public static async System.Threading.Tasks.Task RunAsync([ServiceBusTrigger("topic", "sub", Connection = "ConnectionStringSettingName")]Message mySbMsg, Guid lockToken, MessageReceiver messageReceiver, ILogger log)
{
log.LogInformation($"C# ServiceBus topic trigger function processed message: {mySbMsg}");
}
If I took it as guid or string or vice versa, it gives the error:
Error indexing method 'Function1.RunAsync'.
Microsoft.Azure.WebJobs.Host: Cannot bind parameter 'lockToken' to
type Guid. Make sure the parameter Type is supported by the binding.
If you're using binding extensions (e.g. ServiceBus, Timers, etc.)
make sure you've called the registration method for the extension(s)
in your startup code (e.g. config.UseServiceBus(), config.UseTimers(),
etc.).
Vice versa error:
RunAsync: Microsoft.Azure.WebJobs.Host: Error indexing method
'Function1.RunAsync'. Microsoft.Azure.WebJobs.Host: Cannot bind
parameter 'lockToken' to type Guid. Make sure the parameter Type is
supported by the binding. If you're using binding extensions (e.g.
ServiceBus, Timers, etc.) make sure you've called the registration
method for the extension(s) in your startup code (e.g.
config.UseServiceBus(), config.UseTimers(), etc.).
The locktoken parameter that you are sending in is a string.
The error message is that it cannot bind the locktoken to a Guid.
Therefore, it is probably expecting a string that is a valid Guid, and you are sending in something else.

Automatic retry to CosmosDb output binding

I'm using an Azure function that sends an array of around 200 documents to a CosmosDB via the Output Binding. That function gets triggered about 1000 at the same time by queue messages.
In some cases I get the "Request rate is large" error and the function execution fails. The documentation says when this error occurs, I can retry the execution in some milliseconds, but I suspect the azure function runtime is doing that for me. I couldn't find any documentation explicitly saying that when the output binding throws that exception it will retry automatically (like with the .NET Linq library).
Can someone point me out to see if this is the case?
The Output binding uses SDK 1.13.2 which already has the retry mechanism in place.
Assuming you are using Azure Functions v1, if you are using the IAsyncCollection the Function will do an UpsertDocumentAsync for each AddAsync, if you are using a single document output, then the UpsertDocumentAsync should be happening once.
In any case, the SDK retries by default 9 times on a throttled result, after that, the exception is bubbled and you Function will error; the document should go back to the queue for retrying as per the QueueTrigger design and after a couple of iterations, it goes to the deadletter queue..
If you want more granular control of the flow, you could obtain the DocumentClient and do the UpsertDocumentAsync yourself with a try/catch, if it fails more than 9 times, you can opt to send to another Queue or retry another set of times. Something like:
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;
[FunctionName("CosmosDBSample")]
public static async Task<HttpResponseMessage> Run(
[QueueTrigger("my-queue")] MyPOCOClass myMessage,
[DocumentDB("test", "test", ConnectionStringSetting = "CosmosDB"] DocumentClient client,
TraceWriter log)
{
try
{
await client.UpsertDocumentAsync(myMessage);
}
catch(DocumentClientException ex)
{
// retry / queue somewhere else?
log.Warning($"DocumentClientException {ex.Message} in document {myMessage.Id}.");
}
}

ServiceBusTrigger with enqueueTimeUtc argument fails when triggered via HTTP endpoint

I'm developing a Service Bus Trigger in Azure Functions v1 locally with Visual Studio 2017. I want to test the example from the official docs without having to put a message in the service bus. So I trigger it via Postman at endpoint POST http://localhost:7071/admin/functions/ServiceBusQueueTriggerCSharp with body { "input": "foo" }.
This fails with a script host error: Exception while executing function: ServiceBusQueueTriggerCSharp. Microsoft.Azure.WebJobs.Host: One or more errors occurred. Exception binding parameter 'deliveryCount'. Microsoft.Azure.WebJobs.Host: Binding data does not contain expected value 'deliveryCount'.
I tried removing the deliveryCount argument, but then it fails at enqueueTimeUtc. Removing that too works. Is there a way to keep these arguments and test the Function locally?
I understand that these two arguments wouldn't make much sense when triggered via HTTP, but they could be given default values. messageId has a non-zero value.
Example for reference:
[FunctionName("ServiceBusQueueTriggerCSharp")]
public static void Run(
[ServiceBusTrigger("myqueue", AccessRights.Manage, Connection = "ServiceBusConnection")]
string myQueueItem,
Int32 deliveryCount, // this fails
DateTime enqueuedTimeUtc, // this fails too
string messageId,
TraceWriter log)
{
log.Info($"C# ServiceBus queue trigger function processed message: {myQueueItem}");
log.Info($"EnqueuedTimeUtc={enqueuedTimeUtc}");
log.Info($"DeliveryCount={deliveryCount}");
log.Info($"MessageId={messageId}");
}
As of right now, if you want to be able to work with these additional metadata properties, you'll need to use a real service bus message.
In theory, the admin endpoint could be smart enough to allow you to pass additional binding data (such as deliveryCount in this case) as query parameters. I filed the following feature request to track:
https://github.com/Azure/azure-functions-host/issues/2955

How to abandon or deadletter messages on ServiceBus BrokeredMessage level on AzureFunction V2?

I am encountering one major road block issue when trying to use ServiceBusTrigger in azureFunction. I am trying to abandon, or deadletter, a service bus message in V2 ServiceBusTrigger, How can I do so? I've tried the following solution, but I didn't get anywhere.
Here is the codeSample I used:
public async static Task Run(Message myQueueItem, TraceWriter log, ExecutionContext context)
{
log.Info($"C# ServiceBus queue trigger function processed message delivery count: {myQueueItem.SystemProperties.DeliveryCount}");
QueueClient queueClient = new QueueClient("[connectionstring]","[queueName]");
////await queueClient.DeadLetterAsync(myQueueItem.SystemProperties.LockToken);
await queueClient.AbandonAsync(myQueueItem.SystemProperties.LockToken);
}
Solution 1: I tried to substitute Message myQueueItem for BrokeredMessage like in V1, I then can call myQueueItem.Abandon, or deadletter, on the message lever. However It came back with exception:
Microsoft.Azure.WebJobs.Host: Exception binding parameter 'myQueueItem'. System.Private.DataContractSerialization: There was an error deserializing the object of type Microsoft.ServiceBus.Messaging.BrokeredMessage. The input source is not correctly formatted. System.Private.DataContractSerialization: The input source is not correctly formatted."
At least I can go one step further. to
solution 2. Solution 2: is to use:
QueueClient queueClient = new QueueClient("[connectionstring]","[queueName]");
////await queueClient.DeadLetterAsync(myQueueItem.SystemProperties.LockToken);
await queueClient.AbandonAsync(myQueueItem.SystemProperties.LockToken);
I can use the lock provided in the Message Object, however, when I try to send it with queueClient, It said the message gone from the queue. or no longer available.
Can anybody let me know if i am on the right track? If I am not, please kindly guide me in the right track.
Service Bus messages are automatically completed or abandoned by Azure Functions runtime based on the success/failure of the function call, docs:
The Functions runtime receives a message in PeekLock mode. It calls Complete on the message if the function finishes successfully, or calls Abandon if the function fails.
So, the suggested way to Abandon your message is to throw an exception from function invocation.

Resources