I once wrote a foreign exchange order-passing system. The idea behind this system was that it should make orders for various foreign currencies available to a number of users worldwide in such a way as to ensure that, at any one time, only one small group of dealers actually had control of each order. At the end of each working day, the dealers would transfer their orders to one of their colleagues in another time zone. During the night, each outstanding order would make its weary way around the globe, until either it was executed, or it got back to the dealer who had it in the first place.
There were two important things that the system had to ensure: firstly, no orders could be lost, and secondly, no orders could be executed twice. Remember the discussion on the principles of conservation at the beginning of the chapter? Well, here’s our principle of data conservation again. And let me tell you that this law was no simple matter to enforce, especially given that per-seat cost considerations meant that we couldn’t consider using any off-the shelf message-oriented middleware packages. What I would have given for MSMQ…
Once a message is within MSMQ, we can trust the system to deliver it, or at least tell us that it failed to, so that we can take evasive action. So let’s say that we have to take a record from one database and send it to another one using MSMQ — how can we ensure that this is completely safe? The answer is to make the message queue transactional. Now, I don’t know about you, but I find the idea of incorporating something as asynchronous as a queue into a transaction ever so slightly counter-intuitive, so what are we really talking about here?
Let’s go back to our aircraft analogy, and imagine now that instead of us making the trip to Seattle, it’s our great aunt. She’s going to see our long-lost distant cousin, the black sheep of the family, who it turns out has made a fortune in the coffee trade. Unfortunately, she’s never been abroad before, and she’s rather worried about making the trip. Apart from being slightly nervous about flying, all sorts of things might go wrong between leaving her apartment and actually getting on the plane. She’s not even sure which terminal to go to.
Now, we’re nice, helpful people, and we couldn’t bear the thought of our poor great aunt struggling to the airport using public transport, so we offer to drive her. We pick her up at the apartment, and we take her all the way to the airport. We park the car, and carry her bags into the terminal. In fact, we don’t let her out of our sight until she is safely through passport control — as far as we can go with her. Once she is through there, she is in the hands of the airport staff, and we can be pretty certain that she will end up in Seattle. Of course, when she eventually emerges at the other side of customs, we’ll have to hope that our errant cousin deigns to turn up to greet her — he always was pretty unreliable.
What we’ve actually done here is turn the process of getting our great aunt on to the plane into a transaction. We have ensured that the two activities of “leave home” and “get on plane” both happen. If we had not intervened, “leave home” would have happened, but “get on plane” might not. Indeed, “get on wrong plane” might have happened instead. At the other end of the flight, a second transaction is poised ready to occur: the combination of “get off plane” and “go home with long-lost grandson”.
What if the second transaction fails? What if our cousin fails to turn up? If this happens, we cannot proceed with the “get off plane” part of the transaction, and we might instead have to invoke a compensating transaction back at the other end, following a return flight. This would be the combination of “get off plane” and “go back home”.
So this is where messages fit into the transaction environment. Once a message is on a queue — in our example, the plane — it’s as good as being in another database. We don’t worry about what happens when the message is received; that’s part of another transaction.
It’s time for another code example.