Queued Component Error Handling

In classic synchronous COM, the client knew immediately about the success or failure of a method call by inspecting the returned HRESULT. A queued component client interacts with the recorder only, and the returned success code only indicates the success of recording the call. Once recorded, a queued component call can fail because of delivery problems or domain-specific errors. COM+ provides a few options for handling errors on both the client side and the server side. These options include an exception-like mechanism, auto-retries, and a few administrative tools. You can always use a response object to handle errors, as well.

Handling Poison Calls

Once a call is placed successfully in the application queue, plenty can still go wrong; perhaps the component was removed, its installation was corrupted, or the component failed while executing the call (for example, if the customer provided a bogus credit card number). In case of failure, if the call is simply returned back to the queue, COM+ could be trapped in an endless cycle of removing the call from the queue—trying to call the component, failing, placing it back in the queue, and so on. COM+ would never know when to stop retrying—perhaps the call could succeed the next time.

This scenario in distributed messaging systems is called the poison message syndrome because it can literally kill your application. COM+ addresses the poison message syndrome by keeping track of the number of retries and managing the interval between them.

Besides creating the application public queue (where the calls are placed), COM+ creates five private retry queues and one dead queue when you enable queuing for a COM+ application (seeFigure 8-7). The dead queue is the final resting place for a message for which all delivery attempts have failed—it is a suspected poison message.

When a call is posted to a queued component, it is placed in the application public queue and a player tries to invoke it.

If the invocation fails, the message is put into Queue 0. COM+ tries to process the message again three times from queue 0, with a one-minute interval between retries. If the call still fails, the message continues to move up the retry queues, where COM+ retries three times from each queue, with ever-increasing intervals between the retries. The higher the number of the retry queue, the longer the interval between retries (Q_1 is 2 minutes, Q_2 is 4, Q_3 is 8, and Q_4 is 16). After the last attempt from the last retry queue fails, COM+ puts the message in the dead queue, from which there are no more retries, and the message is deemed poisonous.

COM+ application private retry queues and dead letter queue

Figure 8-7.  COM+ application private retry queues and dead letter queue

The dead queue can accumulate an infinite number of messages. It is up to your application administrator to manage the dead queue. One simple course of action available to the administrator is to simply purge the queue of all the messages it contains. Another option is to put the message back in the application or retry queues, if the administrator believes that the call will succeed this time.

Your application administrator can also delete one or more of the retry queues and by doing so control the number and length of the retries; COM+ continues to move a message that continuously fails up the remaining retry queues. If all retry queues are deleted, a message that fails will be moved directly to the dead queue.

Queued Component Exception Classes

Sometimes it may not be possible for the call to succeed due to domain-specific constraints. For example, a customer may attempt to withdraw money from an account that has insufficient funds, or the customer account may close when the message is in the queue. Or, plain security settings may be a problem—the user who issued the queued call simply does not have the right credentials to carry out the call.

If the situation is brought to your product administrator’s attention (on the client and the server side) he or she might be able to do something about it. COM+ lets you associate an exception class with your queued component. In case of repeated failure, COM+ creates the exception class object and notifies it about the failure.

You associate an exception class with your queued component on the Advanced tab of your component’s properties page by specifying the prog-ID of the exception calls (see Figure 8-8). If a queued call cannot reach your component, COM+ instantiates the exception class and lets it handle the failure.

Specifying an exception class for a queued component

Figure 8-8.  Specifying an exception class for a queued component

A queued component exception class is a COM+ component that implements all the queued interfaces supported by your component and a special interface called IPlaybackControl . COM+ uses the exception class object if the call could not be delivered to the queued component, or if the call persistently failed.

IPlaybackControl has only two methods and is defined as:

interface IPlaybackControl : IUnknown 
{
   HRESULT  FinalClientRetry(  );
   HRESULT  FinalServerRetry(  );
};

The terms client and server are defined loosely in the interface. It really refers to which side of the queued call the error occurred on. Both the client and the server administrators can install the exception class, although each will be more interested in what happened on their side.

Client-side exception handling

Delivering a message to the queued component queue is never guaranteed. If all attempts to deliver the message to the queued component queue fail, COM+ places the call on the client side in a public queue called the Xact Dead Letter queue. The Xact Dead Letter queue is shared by all clients on the same machine.

The dead letter queue has a listener associated with it called the Dead Letter Queue Monitor (DLQM)—a COM+ server application installed on every Windows 2000 machine. You can start the DLQM application manually or programmatically by calling into the COM+ Catalog. When the DLQM app is running, and it detects the message in the queue, it retrieves the target component from the message and checks for an exception class.

If the component has an exception class associated with it, the DLQM instantiates the exception class and queries it for IPlaybackControl. Since this is a client-side failure, the DLQM calls IPlaybackControl::FinalClientRetry( ) on the exception class, letting it know that client-side failure of delivery is the reason it is invoked.

Next, the DLQM plays back all method calls from the message to the exception class. Recall that the exception class is required to implement the same set of queued interfaces as the component it is associated with.

If the exception class returns a failure status from any one of the method calls, the message is returned to the Xact Dead Letter queue. The DLQM deletes the message from the Xact Dead Letter Queue only if the exception class returns S_OK on all calls.

This error-handling schema allows the exception class to implement an alternative behavior for messages that cannot be sent to the server. For example, the exception class could generate a compensating transaction. Another course of action would pass in a queued notification object as a method parameter. The exception class would call the notification object, letting it know which calls failed. The notification object can in turn send an email to the customers asking them to resubmit the order, or it can take some other domain-specific error handling action.

Because all COM+ knows about the exception class is its ID, you can even provide deployment-specific exception classes and have a per-customer error handling policy.

Server-side exception handling

Successful delivery of the message to the server side does not mean that the call will succeed—it could still fail for domain-specific reasons, including invalid method parameters, corrupt installation, and missing components.

As explained before, the message moves up through the retry queues in case of repetitive invocation failures. When the final retry on the last retry queue fails, COM+ retrieves the target component from the message and checks for an exception class. Similar to its handling of the failure on the sending client side, if the component has an exception class associated with it, COM+ instantiates the exception class, queries for IPlaybackControl, and calls IPlaybackControl::FinalServerRetry( ). It does this to let the exception class know that the failure took place on the server side and that the message is about to be placed in the dead queue.

COM+ then plays back all method calls from the message to the exception class. The exception class can do whatever it deems fit to handle the error, from sending an email to the application administrator to alerting the user. If the exception class returns S_OK from all method calls, COM+ considers the message delivered. If the exception class returned failure on any of the queued calls, COM+ puts the message in the dead letter queue.

The MessageMover class

Regardless of where the error took place (sending or receiving side), your system or application administrator has to deal with it. Application administrators usually do not develop software for a living and know nothing about COM+, queued components, MSMQ retry queues, etc. It is up to you, the enterprise application developer, to provide your application administrator with tools to manage your product. You should deliver your main product with an application-oriented administration utility to manage retries of asynchronous calls and dead calls (on the server and client side).

The application administration utility should use, in its user interface, terminology from the domain at hand (such as shipment details) rather than queue names. Internally, it will probably interact with exception classes and notification objects. Your helper utility will probably need to move messages between retry queues, the dead queue, and the application queue.

For example, suppose a queued call destined for a customer accounts management component failed because the specified account number was invalid. The administration utility may prompt the administrator to enter the correct account number for the customer and then put the message back in the application queue, this time with the correct account number. To enable you to move messages between queues easily, COM+ provides you with the IMessageMover interface and a standard implementation of it. The standard implementation is available for the C++ developer by calling CoCreateInstance( ) using CLSID_MessageMover and for the Visual Basic developer by calling CreateObject( ) using the prog-ID QC.MessageMover .

The interface IMessageMover is defined as:

interface IMessageMover : IDispatch 
{
   [id(1),propget] HRESULT SourcePath([out,retval]BSTR* pbstrPath);
   [id(1),propput] HRESULT SourcePath([in] BSTR bstrPath);
   [id(2),propget] HRESULT DestPath([out,retval] BSTR* pbstrPath);
   [id(2),propput] HRESULT DestPath([in]BSTR bstrPath);
   [id(3),propget] HRESULT CommitBatchSize([out,retval]long* plSize);
   [id(3),propput] HRESULT CommitBatchSize([in]long lSize);
   [id(4)] HRESULT MoveMessages([out, retval]long* plMessagesMoved);
};

As you can see, IMessageMover is a simple interface. You can set the source and destination queues and call MoveMessages( ) , as shown in Example 8-2, in Visual Basic 6.0.

Example 8-2. Using the IMessageMover interface to move messages from the last retry queue to the application queue

Dim MessageMover  As Object
Dim MessagesMoved As Long

Set MessageMover = CreateObject("QC.MessageMover")

'move all the messages from the last retry queue to the application queue
MessageMover.SourcePath = ".PRIVATE$MyApp_4"
MessageMover.DestPath   = ".PUBLIC$MyApp"

MessagesMoved = MessageMover.MoveMessages

IMessageMover does not provide you with a way to move fewer than the total number of messages on the queue, but it does save you the agony of interacting directly with the MSMQ APIs.

See the MSDN Library for more information about using the IMessageMover interface.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset