NServiceBus, Semantic Versioning and events

Something that caught us quite off guard when migrating from the 2.6 version of NServiceBus to the 3.x versions was around how NServiceBus treats assembly versions for publishing messages.

When a subscriber expresses intent for a subscription of a message type, it does so automatically via a subscription message sent to the publisher. This message contains metadata information about which message types the subscriber intends to subscribe to, including full assembly version. It’s then stored in some persistent store, RavenDB, SQL Server, etc.

{
  "MessageType": "Publisher.Messages.MyEvent, Version=1.0.0.0",
  "Clients": [
    {
      "Queue": "subscriber",
      "Machine": "jimmybogard-pc"
    }
  ]
}

Note that the assembly version is included in the subscription store. So here’s our picture right now:

image

At some point in the future, Publisher bumps its version (for whatever reason) and its message assembly’s version is bumped as well. However, the subscriber hasn’t upgraded its message assembly (likely because nothing changed in the messages), and our picture is now this:

image

What we’re finding is that the Publisher reads the subscription store and notices that it’s only publishing 2.0 of some event. But Subscriber is only subscribed to 1.0, so therefore, can’t fulfill the subscription. The Subscriber will receive no event message, nor will it receive any notification that its subscription was broken. A bit of a silent failure.

I’ve tried a variety of scenarios to make sure that the Subscriber gets its published message, regardless of the version. It seems the only reliable way to ensure that Subscriber gets its subscribed message is to upgrade the Subscriber’s messages assembly to the latest version, regardless if anything has actually changed in the type of the message:

image

Once this common version is the same everything seems to work. I’ve tried “fooling” NServiceBus by fiddling with the subscription store’s version, but that had mixed results, and I couldn’t get it to reliably work.

This only applies to major version bumps – v1.1 messages will be published to v1.0 subscribers. But v2.0 will not be delivered to v1.5, v1.3 etc. It’s only the major version that’s looked at when determining who is subscribed.

This is a breaking change from the NServiceBus versions prior to 3.0 (2.6 and earlier). Previously, assembly version was not considered for dispatching events.

Fixing your versions

In many environments, Semantic Versioning is not practiced for internal components. In fact, version numbers have a very wide variety of uses. I like to embed date information, “2013.1.31.1349” in my versions, because I’m pushing out so many builds and there is no one that really cares about semantic versioning. Other teams use years and quarters to signify releases. The versions are more release numbers than an indication that anything is broken.

Until it becomes configurable, be very careful about your version numbers in your message assemblies. To avoid silent production breakages, either:

  • Keep the major version constant. Use assembly file version or something else for descriptive version numbers
  • Always upgrade subscribers to major versions to avoid breakage

A bit of a hassle, but necessary if you don’t want to accidentally break consumers by bumping your assembly versions.

Happy messaging!

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in NServiceBus. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://twitter.com/thefringeninja João P. Bragança

    Argh! Sounds like a pain. Another way to deal with this is to put your message assembly in a nuget package. Then your CI process can always grab the latest version and use assembly binding redirects. If your message classes are always binary backwards compatible then you should be all good. We do something similar.

    • http://twitter.com/PhatBoyG Chris Patterson

      This actually doesn’t solve the problem, as the issue is how NSB maps subscriptions to messages (which stores the version separately in the subscription store).

  • Edwin G. Castro

    I once read that interfaces belong to the consumer of the interface. I can’t remember exactly where I read that but I’ve seen this mistake happen over and over again in distributed scenarios. I work with WCF services and generally speaking I see service implementations defining the contract they implement. So whenever the service implementation changes it forces the contract to change as well. The end result is that the client/consumer also must change even if they wanted to continue to use the old contract. I’ve seen this happen even when the new implementation supports multiple contracts. The same is happening here. The contract is the message. The message truly belongs to the consumer/subscriber. The publisher can specify which version of a message it happens to support. Therefore, if it is impractical to change all of your consumers to subscribe to a newer version of a message then somebody needs to continue to produce that specific version of the message (even if it is a different implementation of the publisher). Contracts/messages/interfaces really should be defined independently of both the subscriber/client that consume them and the publisher/service that implement/generate them. That is the only way to truly keep components loosely decoupled regardless of the mechanism used to communicate (service bus, SOAP, REST, RPC, Remoting, etc.).

    • jbogard

      Consumer/subscriber are two different concepts – I wouldn’t mix the two. Send/Receive is much different than publish/subscribe.

      In Send/Receive, the receiver owns the contract.

      In Pub/Sub, the publisher owns the contract.

      Think of it this way – the IRS – the receiver – owns the tax forms. The magazine publisher owns the magazine. It’s both sources/sinks, but the roles they play are very different.

      • Edwin G. Castro

        Using the perspective that the publisher always owns the contract (which is a valid perspective) then you must accept the fact that all subscribers must upgrade whenever the publisher decides to change. When physical assemblies are involved, then change is dictated by both physical structure (the classes themselves) and AssemblyVersion (two classes with the same structure in assemblies with different versions *are* different classes). In the end I don’t think looking at contracts as either Send/Receive versus Publish/Subscribe is very interesting (as you mentioned they are both sources/sinks). What is interesting is your dedication to changing behavior without impacting the other end. If you want all clients to continue to subscribe and react to a particular message when you change behavior, then you must keep the AssemblyName constant (which include AssemblyVersion) because the true full name of the message includes the full AssemblyName. Changing the AssemblyVersion of your messages should require all your subscribers to upgrade. I personally don’t find that strange or odd.

        • jbogard

          Type != message, assembly != version. The assumption is that the type IS the message. It’s not. The type is the type, the message is the message. The type is merely a representation of the message, nothing more. If the message looks *exactly the same*, then it is the same message.

          In any case, using the assembly version to version messages is a pretty lousy approach. It allows no wiggle room to support existing clients. If clients must upgrade all their DLLs, I’ve increased, not decreased coupling.

  • Pingback: Scott Banwart's Blog › Distributed Weekly 192

  • Udi Dahan

    The short story here is that you can’t just change a message schema and expect everything to continue to “just work”.

    You can change anything else you want about your publishers and subscribers without anything breaking.The schema is the only thing shared between publishers and subscribers so it should be versioned carefully – including changing its version number.

    • http://twitter.com/bonder Bruce Onder

      The way one of my developers “solved” this at a previous company was classic: just pass strings around. LOL

    • Edwin G. Castro

      Well put. This is precisely what I was trying to convey but expressed much better than I was able to. This principle applies to all contracts/interfaces regardless of the mechanism used to communicate between parties.

    • jbogard

      So one thing that might throw a wrench into all this is the assumption that the assembly version belongs in the message schema. Assembly versions can be used for a lot of things (identifying releases, dates, etc.) that have little or nothing to do with actual shape of the message.

      The behavior we saw was that the year changed from 2012 -> 2013, which we include as our major version (not even set by us, but by the build/deploy group), and all of our subscriptions broke, silently.

      Typically, I’d handle versioning much differently. I either add information, or I create new messages. If the message is so different that it would break clients (i.e. version number), it’s a new message.

    • http://mhinze.com Matt Hinze

      How does this resolve with the spirit of this guidance? http://support.nservicebus.com/customer/portal/articles/894151-versioning-sample

      At any rate, I think tying the .NET type metadata to the message schema is unacceptable platform coupling.

      Naively, I expect what’s defined in MessageEndpointMappings to be stored as subscription data.

      I think we need to be verrrrry careful with SemVer. I agree with conventional wisdom that it is well thought out and broadly applicable, but should not be shellacked upon every versioning problem.

      Finally, the lack of information about this coupling – message schema to .NET type metadata – can lead to confusion .. especially considering the publisher’s behavior when in cases where this suddenly doesn’t match.

  • Adrian Hofman

    I can’t help but think that this is a symptom of using a .NET type as the source of truth of message schema (as opposed to say, an XSD). A .NET type carries so much baggage terms of implementation details, for example, the versioning problem identified here.

    NServiceBus has built in support for exposing message schema as an XSD; this approach decouples message schema from .NET implementation concerns. I wonder why this wasn’t the recommended setup?

    • jbogard

      These days my messages are all JSON, so I’m not sure what to do in that case.