Azure / azure-functions-servicebus-extension

Service Bus extension for Azure Functions
MIT License
65 stars 35 forks source link

Unable to add Message object to ServiceBus due to serialization error #185

Closed sm-cdecker closed 2 years ago

sm-cdecker commented 2 years ago

I'm trying to upgrade an app from dotnet 3.1 to 6, and it looks like Microsoft.Azure.ServiceBus.Message were dropped in favor of ServiceBusMessage. The Message class is still there but it's throwing an error when we attempt to add the object to the IAsyncCollector. Specifically it's throwing an InvalidOperationException when we attempt to serialize ExpiresAtUtc, even though this property is NEVER touched by our code. It looks like the issue is due to SystemPropertiesCollection.sequenceNumber never being set.

Unfortunately our services utilize the Name metadata property on the Message class, so if this cannot be resolved then we'll be force to migrate ALL of our services at once rather than one at a time.

Version: dotnet 6, Microsoft.Azure.WebJobs.Extensions.ServiceBus 5.3.0

sm-cdecker commented 2 years ago

Here is a simplified version of our code.

// sending micro-service
namespace MicroService1 {

    public class TimerTriggerService {

        [FunctionName(nameof(TimerTrigger))]
        public async Task TimerTrigger(
            [TimerTrigger("0 * * * * *")]
            TimerInfo myTimer, 
            [ServiceBus("service2-incomingcommands", Connection = "AzureServiceBus")]
            IAsyncCollector<Message> collector)
        {
            var addCommand = new Service2.Add();

            await collector.AddAsync(addCommand.ToMessage());
        }
    }

    public static class ServiceBusHelper {
        public static Message ToMessage(this object o) {
            var message = new Message(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(o)));
            message.UserProperties.Add("Name", o.GetType().FullName);
            return message;
        }
    }
}

// receiving micro-service
namespace MicroService2 {
    public class QueueTriggerService {

        [FunctionName(nameof(QueueTriggerHandler))]
        public async Task QueueTriggerHandler(
            [ServiceBusTrigger("service2-incomingcommands", Connection = "AzurePremiumServiceBus")]
            Message message)
        {
            switch (message.UserProperties["Name"])
            {
                case typeof(Add).FullName:
                    // doSomething
                    return;
                case typeof(Update).FullName:
                    // doSomething
                    return;
                case typeof(Delete).FullName:
                    // doSomething
                    return;
                default:
            }
        }
    }

    public class Add{}
    public class Update{}
    public class Delete{}
}

I'm trying to upgrade MicroService1, and this is where I'm experiencing the issue (specifically on the AddAsync() call). If I switch MicroService1 to ServiceBusMessage, the switch functionality in MicroService2 breaks completely, since UserProperties were moved to ApplicationProperties. And if I upgrade MicroService2 then logically I have to upgrade every other micro-service that it sends/receives messages from other micro-services, so I'd have to upgrade everything all at-once.

  1. Is there some way I can get the AddAsync call to not error out when I pass in a Message?
  2. Is there a property on ServiceBusMessage that maps to the ApplicationProperties to Message.UserProperties?
alrod commented 2 years ago

@sm-cdecker, can you reproduce the issue using Microsoft.Azure.WebJobs.Extensions.ServiceBus 4.3.0?

sm-cdecker commented 2 years ago

@alrod This works, though I swear I had tried this earlier and it was having an issue binding the input params due to a type mismatch...

Thanks for the help.

But I still have an issue with our upgrade capabilities in the future. If the metadata properties of Message and ServiceBusMessage don't get translated over to one another, organizations will be forced to upgrade all of their engines at the same time.

shreyabatra4 commented 2 years ago
  1. For 4.x, the class name is 'Message' and for 5.x the class name to be used is 'ServiceBusMessage'.
  2. The property ServiceBusMessage.ApplicationProperties is equivalent to Message.UserProperties.
sm-cdecker commented 2 years ago
  1. For 4.x, the class name is 'Message' and for 5.x the class name to be used is 'ServiceBusMessage'.
  2. The property ServiceBusMessage.ApplicationProperties is equivalent to Message.UserProperties.

This doesn't solve my issue @shreyabatra4. If I have 10+ microservices all talking to one another using metadata then I'll need to upgrade all those engines all at once. While I'm now able to use dotnet core 6, I'm effectively stuck on an older version of the servicebus library. Something needs to be put into place to translate the metadata from one type to another so that newer/older services will be able to move one at a time.