(Who’s on First?)
Another question I have been asked more times than I can remember is “why doesn’t Service Broker have message priority?” On one hand the answer is “of course Service Broker has message priority”. Every message has a priority column and messages are received in priority order by dialog. What Service Broker doesn’t have is a way to set message priority. In this entry, I’ll discuss priority in relationship to reliable messaging and general and Service Broker in particular. In a future entry, I’ll write a little sample code to illustrate a couple ways to make priority work.
The first thing to realize with Service Broker priority is that messages can’t have priority – only dialogs can. Because dialogs guarantee message ordering, it makes no sense to prioritize messages within a dialog. Message 10 can’t be processed until messages 0-9 have been processed no matter what their priority. Since priority only applies to dialogs, there are two logical places to set priority.
The first place to set priority is in the BEGIN DIALOG statement or in the CONTRACT. At first glance, BEGIN DIALOG seems like the best place to do it because then each dialog can have its own priority no matter what contract is used. You could have high priority orders and low priority orders in an order entry system for example. This is acceptable as long as you own both ends of the dialog and have complete control over who get what priority. If you don’t have control because for example you are providing a Service that an number of different applications will use, this is much less workable. Who would voluntarily set their call to a low priority? In most cases this would result in everybody running at priority 0 which is in fact the way Service Broker works today.
The second place to set priority is in the CONTRACT. Since every dialog is associated with a contract, you could configure high and low priority contracts. This has the advantage that the Service owner would get to decide which priority to assign to a contract. This would mean having several contracts for the same set of messages with different priorities which could be a maintenance hassle. The owner can also decide who can run at which priority through security permissions but if an initiator has permissions for multiple priorities (a not uncommon requirement) the service owner still must trust the initiator to do the right thing. There are ways to control the initiator – charging more for higher priority messages for example – but in general setting priorities is difficult to do in a fair manner. I’m sure this is part of the reason why not many messaging systems support message priority.
Once you have a way to set priorities, the next thing to consider is what do you do with the priority. The normal answer is you should receive messages in priority order. Does that mean that as long as there are priority one messages in the queue you never receive priority two messages? That’s the normal interpretation but it means that low priority messages will never be processed if there are enough high priority messages available.
If you want to ensure that high priority messages always have priority, what happens if all the queue readers are processing low priority messages when a high priority message arrives? If another reader is started will it have enough resources to process the message while all the low priority messages are being processed? Should a low priority message be rolled back to make room for the high priority message or should the high priority message wait for one of the low priority messages to finish?
Another possibility is that both high and low priority messages are processed but more resources are dedicated to the high priority messages. For example, you might have 10 queue readers for high priority messages and one reader for low priority messages. This ensures that low priority messages are processed but at a lower rate than high priority messages.
Given all this, let’s look at a few ways to implement priorities in Service Broker. One possibility is to use different dialogs with the same Service on the same queue. You could then use a WHERE clause in the RECEIVE statement to receive from the high priority dialogs until there are no messages available and then receive lower priority messages. While this seems like an attractive alternative because there is only one queue and one service, the implementation can be quite complex. There has to be a way to identify which dialogs have high priority and the list of high priority dialogs has to be stored in the database in case the target database is restarted. This technique will also only work if there are relatively few high priority dialogs because the RECEIVE statement can only specify one dialog to receive from at a time. Ideally a single high priority dialog will be easiest to handle but unless high priority messages are fairly rare, you run the risk of high priority messages lined up behind other high priority messages.
Another approach is to define high and low priority services on different queues. This is generally simpler to set up and program. It also has the advantage of different service which can be set up with different permissions to control who has access to high priority processing. There’s also quite a bit of flexibility in how messages are handled. A single message loop that receives from the high priority queue until it’s empty and then receives a message from the low priority queue gives very high priority to the high priority queue. If you want to be sure that there’s always something to process a high priority message you could have one copy of the queue reader that only listens to the high priority queue to pick up high priority messages that arrive while a low priority message is being processed. The multi-queue approach also allows you to configure different queue readers for each queue and dedicate more readers to the higher priority queues. This ensures that lower priority messages get processed in parallel with high priority messages.
Well, that’s a quick summary of the issues with implementing priority processing of messages. While I used Service Broker for the examples, most of this applies to any reliable messaging system. When I get some code written I’ll post some sample code on implementing the priority processing options I discussed. Let me know if you have any feedback or some other alternative implementations.