Data Access and Transactions |
Complex transactions may have several smaller subtransactions, each with differing levels of importance. Determining which operations are required to complete the transaction, and which may be eligible for deferral, is a crucial first step in designing a robust system with Message Queuing. You should only consider using messages for operations that are not essential to the overall stability of the system.
Message Queuing operations always commit, and return, control to your application quickly. When used to process part of your transaction, Message Queuing saves a “message” to an on-disk queue, instead of performing a database operation. The message should contain enough information to describe the operation that would have been performed on the remote system, had it been connected.
Some time later (or at a time you choose), Message Queuing establishes a connection with Message Queuing that is running on the remote system and then transmits the message. As soon as the remote system has successfully received it, the message is dequeued and processed as part of a new database transaction. If all goes well, a confirmation message is sent back to the Message Queuing system that initiated the transaction. This message path is shown in the following figure:
Since Message Queuing messages are system-specific, you are responsible for collecting the information your system will need to complete the transaction. Message Queuing merely guarantees that your message will be delivered to the appropriate queue on the remote system. The following example demonstrates how you might add support for Message Queuing to a COM component within a system that processes orders for a vendor’s products:
Sub OrderProduct(
OrderID As Int,
ProductID As Int,
Quantity As Int)
Dim objCtx As ObjectContext
Dim query As New MSMQQuery
Dim msg As New MSMQMessage
Dim infoSend As New MSMQQueueInfo
Dim infoResp As MSMQQueueInfo
Dim queue As MSMQQueue
On Error Goto ErrHandler
Set objCtx = GetObjectContext()
'Open destination queue.
infoSend.PathName = "Contractor\ProductOrderQueue"
Set queue = infoSend.Open(MQ_SEND_ACCESS,MQ_DENY_NONE)
'Construct application specific message.
msg.Label = "Product Order"
msg.Body = Str(OrderID) & ";" & Str(ProductID) & ";" & Str(Quantity)
msg.PrivLevel = MQMSG_PRIV_LEVEL_BODY
'Lookup response queue.
Set infoResp = query.LookupQueue(Label:="ContractorResponse")
infoResp.Reset
'Set application specific response queue.
Set msg.ResponseQueueInfo = infoResp.Next
'Send message to remote queue.
msg.Send queue, MQ_MTS_TRANSACTION
queue.Close
objCtx.SetComplete
Exit Sub
ErrHandler:
objCtx.SetAbort
End Sub
A Message Queuing transaction merely sends a message, so the application must assume that the work can be performed. In this case, no effort has been made to determine if the contractor can supply the requested number of products. This inability to determine, in advance, if the transaction will be honored is a problem known as “deferred integrity.” A good example of deferred integrity in the real world might be that of a bank handling the case of insufficient funds for a check that has already been cashed.
Once the decision has been made to allow portions of a transaction to take place asynchronously, a business policy must be implemented to determine what happens if the transaction cannot be satisfied. In certain cases, transaction failures may require an application-level rollback of prior-committed transactions. Or, they may require correspondence with the customer in order to decide the best course of action.