Azure / azure-functions-dotnet-worker

Azure Functions out-of-process .NET language worker
MIT License
413 stars 178 forks source link

Service Bus Output Binding is creating multiple messages from a JSON array when returning a string #2387

Open blockingHD opened 7 months ago

blockingHD commented 7 months ago

Service bus output binding creating multiple messages when returning a string which is a JSON array. This has been noted since updating to .Net 8 and using the dotnet-isolated function runtime. This behaviour has changed either between .Net upgrades or moving to isolated functions.

Repro steps

Run the following function:

[Function("Test Func")]
[ServiceBusOutput("queuName", Connection = "ServiceBusConnectionString")]
public static async Task<string> RunAsync(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequestData req)
{
    return await req.ReadAsStringAsync();
}

Call with this JSON payload:

[
  {
    "Test1": "test1"
  },
  {
    "Test2": "test2"
  }
]

Expected behavior

A message is created with the body:

[
  {
    "Test1": "test1"
  },
  {
    "Test2": "test2"
  }
]

Actual behavior

Two messages are created:

1)

{
  "Test1": "test1"
}

2)

{
  "Test2": "test2"
}

Related information

Provide any related information

blockingHD commented 7 months ago

Note that this documentation: https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-service-bus-output?tabs=python-v2%2Cisolated-process%2Cnodejs-v4%2Cextensionv5&pivots=programming-language-csharp states that to place a single message string, byte[], JSON serializable types should be used and for multiple messages T[] where T is one of the single message types should be used instead.

liliankasem commented 5 months ago

I believe this is expected, the docs say to use string only if it's "simple":

"The message as a string. Use when the message is simple text."

A JSON payload would not be considered a simple string in this case.

You should be able to use byte[] or POCO for this instead. Alternatively, you can escape the json string and it would end up as one message e.g. "[\n {\n \"Test1\": \"test1\"\n },\n {\n \"Test2\": \"test2\"\n }\n]"

blockingHD commented 5 months ago

The issue is the difference in behaviour between the in process and isolated models. When sending this via the in process model it is sent as a whole string whereas the isolated model sends it as separate messages.

Unfortunately the docs are ambiguous as simple string should be any string in my mind. Mainly because if I wanted multiple messages I would send an array of some description. Not helped by the same wording being present between the two models.

I have a continued case open with Microsoft and they have isolated the issue to the service bus extension SDK. It seems the engineering team are working out a way to make this work. Whether that be via configuration or a change to the SDK. The only workaround they have come back with is using the service bus SDK directly.

liliankasem commented 5 months ago

Thanks for the context. If the SDK team was able to identify the issue and is working on a fix for it, there isn't an action item for the worker. But we can keep this issue open to validate the fix worked once it has been released.