Michael Nygard tilts a little too much toward the enterprisey in his recent post on outbound email.
Some of his complaints seem to be about the JavaMail API specifically. e.g. "one connection per message" is not a limitation of SMTP. I suppose his concern is that the JavaMail API has a lot of setup and teardown cost for each message.
But regarding his concern about holding up request-handling threads and recovering mail messages when connecting to the mail transfer agent fails... he goes on to recommend using a message bus/queue like JMS or an ESB, for goodness sake, in *addition* to the SMTP server. An intermediary for the intermediary??
Rather than doing running some other kind of server, why not make sure you're running a fairly stable SMTP server? He writes...
...keep in mind that SMTP servers aren't always 100% reliable...But I thought the point of his book is that *no* system is always 100% reliable. (btw is there such a thing as "sometimes 100% reliable"? :^)
And so even with a bus/queue or an ESB in between your system and the SMTP server there are still opportunities to fail. In fact, there seem to be *more* opportunities to fail.
Another quote from Michael...
Yes, you can log an exception when your connection to the MTA fails. But that only lets the administrator know that some amount of mail failed. It doesn't say what the mail was!But if you can log the exception, certainly you can log the message that would have been sent, and then either automatically or "manually" resend it.
How would you handle this situation?
This seems like a general problem, and not specific to SMTP. For example, let's say instead of SMTP you'd like to publish a message to a collection on an Atompub HTTP service. Would you recommend a bus/queue or an ESB as a stepping stone between your system and the HTTP server?
How is this similar or dissimilar to the SMPT scenario?
4 comments:
I think you're right in that it's a more general problem - mainly one of how to handle a failure in a system when you have an important message to deliver but can't contact the service you want to deliver it to. It's actually not a simple problem to solve - you generally don't want to log the message as you suggest, mainly because:
1) simply logging the message doesn't give you a reliable, easy way to retrieve it and replay it later - you need more data than just the message
2) even if you do have #1, you can still lose the message with a local disk failure
Rather, I think his approach has a lot of parallels to the Erlang world to be honest :) To draw some comparisons, he's creating a focused process that matches a specific pattern, does the mail delivery and lets a JMS broker act as a combination supervisor and replicated mnesia table.
I don't see anywhere in Michael's post where he advocates using an ESB or any such heavy weight approach, in fact the redelivery, simple parallelism and reliability he's after can be done with an embedded JMS server. You don't need an external process or middleware. Having some sort of persistence to guarantee an extra level of reliability behind the JMS service is generally a good failsafe but not required.
To your point, yes he is adding an additional layer to the delivery, but there are benefits to that layer that aren't enterprisey, they are actually more in-line with the idea that a client of a service should fend for itself in the harsh environment of distributed systems failures. In Java anyway, JMS is an easy way to let clients try something, and if they fail, have the plumbing retry that something at a later point in time over a given number of attempts. Much like an Erlang process, if you're unhappy with the state of the system, simply bail out and the supervisor level will take care of the rest.
"simply logging the message doesn't give you a reliable, easy way to retrieve it and replay it"
Yeah, I would expect the source application already has most of the information saved somewhere reliable already. At most that system would need a simple additional "ledger" of some kind that tracks whether the email has been sent or not.
Given most of that mechanism is already in place, adding something as grand as a JMS seems overkill.
But yeah, it's not _just_ writing the message to a log file, and so I don't think (2) comes into play.
"lets a JMS broker act as a combination supervisor and replicated mnesia table"
Yeah, his JMS is the ledger and the process or thread for running through that ledger. Although we (Ed and I on Hopper) found something like ActiveMQ to be fairly lightweight, eh, it just seems too much.
Maybe I am making a mountain out of a molehill, but pretty soon you end up with all kinds of parts that mostly duplicate other parts.
"I don't see anywhere in Michael's post where he advocates using an ESB"
He called it a "message broker", under the "better still" section. Links to Mule, Tibco, etc.
The point is that you want your precious request handling thread to be available for handling requests ASAP. Submitting the mail-out request to a JMS queue means no change in synchronicity, but ensures you can handle the next request earlier in case the connection to the external system has problems.
If the transmission of the message is important and is generated within a database transaction, then you'd want to handle the case when the transmission or transaction fails. If you're sending the message in response to another event, then it gets pretty complicated to coordinate retransmission. You have to roll back the database transaction and arrange for a retry, or the initiating event to be redelivered, and handle failure cases in arranging the retry, try to avoid sending duplicate emails, etc.
It makes application code much simpler if it can use a messaging system with "Once and only once" delivery semantics (which SMTP does not have), and tie the transaction of the messaging system, the database and the source of the initiating event into a single, distributed transaction.
JMS makes this easy. The application, which has to coordinate distributed resources, can push an outgoing message onto a JMS queue. The email sender can pull messages off a single queue and try to send them by email. The failure case is simple (no distributed transactions) and the JMS message broker implements the retries for you.
So, in some situations it is easier to put a JMS broker in between your app and an SMTP broker because you're reusing the JMS broker's implementation of reliability and retry. And those are features that have nasty corner cases that are very hard to get right and very hard to test. I'd rather rely on an old system that has had bugs shaken out of it over 20 years than my own code.
It also makes the internal architecture of the application simpler. There is a single messaging scheme used within the app with clear gateways to other protocols at the edges.
Post a Comment