I’ve spent part of the weekend playing with some code for a BizTalk 2006 Send adapter,
and while looking at the failure handling code, remember something that always struck
me a bit odd about the model.
BizTalk supports a several different ways of implementing a send adapter, depending
on the complexity of the adapter and the capabilities it provides. An adapter can
made to handle a single message at a time (but with likely multiple messages in parallel),
or handle batches of messages. An adapter can also handle messages synchronously or
asynchronously.
Synchronous adapters are not really very desirable in general terms, unless the adapter
itself doesn’t do much work (like my Null
Send Adapter, which does no work at all!). The reason for this is that a synchronous
adapter will do its message processing directly in the thread provided by the BizTalk
Messaging engine, thus blocking the thread temporarily and getting in the way of the
Messaging Engine itself. Since the messaging engine threads are usually CPU bound,
and the primary function of an adapter is to do I/O, this isn’t a very desirable scenario.
However, there’s something interesting about how the messaging engine accommodates
synchronous adapters. When BizTalk wants to send a message through an adapter, it
will call the adapter’s IBTTransmitter.TransmitMessage() implementation [1].
TransmitMessage() has a boolean return value. If the adapter returns true, it means
that this was a synchronous send operation, and the message has been sent successfully.
If an exception is raised, then the send operation will be failed. In either case,
the messaging engine will do the right thing and process the message accordingly.
If the adapter returns false, however, it means that the adapter will handle the message
asynchronously and it’s responsible for telling the messaging engine what [eventually]
happened with the message.
In practice, the last part is a lot harder than it sounds. Basically, a synchronous
adapter is pretty worry free: It simply needs to return true or throw an exception,
and the messaging engine does all the hard work. However, an asynchronous adapter
has to do all the hard work itself, which includes:
- Deleting the message from the message box when it has been successfully sent.
-
Submitting messages for retries according to their retry counts and retry intervals
as configured in the send port configuration. -
Moving messages to the secondary transport configured in the send location if the
retries have been exhausted. - Suspending messages that could not otherwise be sent.
-
All of this needs to be retried until the messaging engine acknowledges the operation
successfully!
As you can see it’s a lot of responsibilities for the adapter developer; particularly
the last part. See, if you tell the engine to delete a message, it might tell you
that it couldn’t do it (maybe there was contention on the message box); it’s your
responsibility to keep trying until it succeeds. Same story for all of the operations
mentioned above. Getting all this right can be a bit tricky, as you can imagine (though
the adapter framework in the SDK can help a bit here).
Is this unnecessary? Not at all; it is quite required particularly for batched and
transaction aware adapters, and it actually is quite flexible. It also allows adapters
with special needs to make decisions about what the right action might be. For example,
maybe your adapter must suspend a message without trying the secondary transport (ordered
delivery requirements, maybe).
The downside of this is that, if you were not interested in creating a batched adapter
(or couldn’t!), but did want to make it relatively efficient by not doing your work
on the messaging engine threads, it’s a whole lot of complexity you have to deal with.
You can’t simply tell the messaging engine "I’m done with this message"
and have the right things happen anymore; instead you need to actually handle batches
(containing a single message, but batches nonetheless) and implement the required
semantics all on your own.
Simple synchronous adapters are still useful though. It’s an easy way to get started
with your send adapter, verify that your core adapter logic works, and then move on
to support asynchronous processing and batches, if you can support those.
[1] If the adapter supports batching, it will implement IBTBatchTransmitter
instead of IBTTransmitter and the workflow will be different, but let’s forget about
that for now.

Server