var self = justin();

Software Developer Teammate

A place to remind my future self of what I've learned and experienced. That means both my successes and failures.


Multiple NSB Hosts with RavenDB

At my current client, we've been using a bus in our app since day one. For the most part, the bus was a simple in process, synchronous bus that was developed in house. It served the needs of the application and was easy to use with pratically no setup. We've also been using RavenDb for certains of the application (the other parts were backed by MSSQL).

About 6 months ago we started replacing our in process bus with NServiceBus. Unforutnately, this was not just a simple switch and go. We've been feeling the pain of not using NSB from the very beginning, but I digress.

The other day (probably a Thursday, crap always seems to happen Thursdays for me), I was creating a second NSB host and spun it up to start some testing. Both the first and the second hosts were communicating with RavenDb.

Note: when testing practically any NSB stuff, I like to create a small console app that will send messages for me instead of having to go through the entire app.

I sent the message and watched the first host pick it up, spit out the debug messages and then saw the second host spit this error: A resource manager with the same identifier is already registered with the specified transaction coordinator.

It took me a while to figure out what the problem was, but I finally did and was able to fix it.

When the RavenDb client registers itself with the DTC, it gives a fixed GUID as its id: E749BAA6-6F76-4EEF-A069-40A4378954F8

When both hosts initialized the RavenDb client, the both tried to register with the DTC using that same GUID under the same transaction. This led to the DTC rejecting the registration and throwing the error.

A simple solution was to initialize the RavenDb client with a random GUID as the resource manager id, but then that would make it very difficult if I ever needed to inspect transactions and the like. I needed a way to determinstically create a GUID for each of my NSB hosts and ensure they were unique among themselves. Ripped some code from here.

static Guid DeterministicGuidBuilder(string input)
{
    //use MD5 hash to get a 16-byte hash of the string
    var provider = new MD5CryptoServiceProvider();
    byte[] inputBytes = Encoding.Default.GetBytes(input);
    byte[] hashBytes = provider.ComputeHash(inputBytes);
    //generate a guid from the hash:
    return new Guid(hashBytes);
  }

Then created this little class.

public static class ResourceManagerIdGenerator
{
    private static readonly Guid defaultId = new Guid("E749BAA6-6F76-4EEF-A069-40A4378954F8");
    public static Guid Generate(string salt)
    {
        var resourceManagerId = salt + "@" + Environment.MachineName + "_View";
        return DeterministicGuidBuilder(resourceManagerId);
    }
    public static Guid DefaultId
    {
        get { return defaultId; }
    }
    static Guid DeterministicGuidBuilder(string input)
    {
        //use MD5 hash to get a 16-byte hash of the string
        var provider = new MD5CryptoServiceProvider();
        byte[] inputBytes = Encoding.Default.GetBytes(input);
        byte[] hashBytes = provider.ComputeHash(inputBytes);
        //generate a guid from the hash:
        return new Guid(hashBytes);
    }
}

Then, when registering NSB and RavenDb, I just set the ResourceManagerId on the DocumentStore and I'm good to go!

       var documentStore = new DocumentStore { ConnectionStringName = "RavenDB" };

        const string SOME_HOST = "Some.Host";
        if (documentStore.ResourceManagerId == ResourceManagerIdGenerator.DefaultId)
        {
            documentStore.ResourceManagerId = ResourceManagerIdGenerator.Generate(SOME_HOST);
        }
comments powered by Disqus