Stop premature email sending with NServiceBus

We use NServiceBus quite a lot to manage integration points where the other side isn’t transactional and we need to “shore up the process” of communicating with external services. One integration point we often don’t think about in terms of an integration point is the sending of an email.

Often times, you’ll see some sort of process that needs to notify someone that the job is complete via email. It’ll look something like:

public void SomeServiceCall()
{
    DoSomeWorkHere();
    CreateSomeObjects();

    SaveToADatabase();

    var message = new MailMessage();
    var client = new SmtpClient();
    client.Send(message);
}

This is all fine and dandy, but the problem comes when something like this is executed inside a transaction. We expect that if the SomeServiceCall method is wrapped in a transaction, that the database save should roll back. But what about that email? It often won’t fail until the transaction commits, which is AFTER the email is sent:

image

In the picture above, we can see that the transaction only involves the database, but we can’t un-send our email.

What we need to do here is decouple the actual sending of an email with the message to indicate an email needs to be sent. Right now, we’re using a synchronous, non-transactional mechanism of sending an email. Instead, we just need to wrap that interaction point with an NServiceBus message:

public void SomeServiceCall()
{
    DoSomeWorkHere();
    CreateSomeObjects();

    SaveToADatabase();

    _bus.Send(new SendEmailMessage());
}

We’re using the NServiceBus bus to send a message that we want to send an email. The nice thing about this picture is that the sending of that NServiceBus message now participates in the transaction:

image

In the case of failure, that message never makes it over to my NServiceBus host. If it does succeed:

image

Then my message is delivered to the SMTP NServiceBus host, which will then interact with the SMTP server.

By decoupling the action of sending an email with the intention through an NServiceBus message, I can make sure that the email isn’t sent unless I successfully complete the transaction.

Another nice benefit here is that I was originally synchronously sending an email. With this architecture, if the SMTP server goes down, it doesn’t affect the ability of My Service to stay up. Instead, the durable messaging of MSMQ and NServiceBus reliability mechanisms ensures that I don’t lose the messages of sending those emails.

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in NServiceBus. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Elias Rangel

    This is the kind of stuff that is hard to understand when one comes from non-event programming.  It’s one thing to publish the event, and another to act on the event.  
    Thank you for the great example,  Jimmy

  • Evgeny T

    Use PickupDirectoryFromIIS, so 100% mail messaging is fail-safe + no need for async.
    You can move Send(..) out of transaction and just send.
    NServiceBus is overkill for the case.

    • Anonymous

      I don’t have control over that – everything is executed in an infrastructure-level unit of work (which is how it should be – transactions aren’t something I should have to manage on a daily basis).

      How do I make sure that something doesn’t happen between the transaction commit and sending the message? I want to make sure that the entire workflow succeeds/fails.

      • Sean Kearon

        Quite! AFAIK you’d have to code the handling between the commit and send yourself.  

        “NServiceBus is overkill for the case.” – Assuming you have MSMQ available an NSB email endpoint is a relatively  simple option.   NSB’s deployment story so sweet, it’s a breeze!

  • Jason Hooten

    Do you use a combination synchronous and asynchronous Command objects?

    Like if you were to create a user, which also needed an email sent off when created – would your controller make two Command objects, synchronous one for creating the user and an async for passing through your system to send off the email?

    Or would there just be one command which would call async and/or sync methods on services/domian models?

    • Anonymous

      Mmmmm….typically, command to create a user. Then a UserCreated event results in the sending of an email, which could be done just in that handler.

      Whether or not the original command is sync or not is a little bit orthogonal, it could be either and still not really affect the design of the rest.

  • Adamschroder

    We write all emails to a table, and have a service that sends emails every x seconds. This allows the writing of the record to also remain part of the original transaction. It also allows us to retry as many times as we want and even let the user know that the email failed to send due to a specific reason. 
    Great example non-the-less. These real world scenarios are so much better than just theory.

    • Anonymous

      We used to do this too – and our email table was called “EmailQueue”. I kinda prefer now using a queue for a queue and a database for transactional data. But a database table does provide a history of sorts for what’s happened though.

  • http://josephvano.wordpress.com/ Joey Vano

    I do this exact thing.

    Good post.

    Even better to publish the message as the exact event that happened vs  ”SendEmailMessage”. In the context of an expensive job finishing, “IFinishedJob”  and on the handler side “JobFinishedEmailMessageHandler”.

    • Anonymous

      You know, I’ve gone back and forth on this soooo much. On the one hand, I like having specific, targeted handlers for specific, targeted events. On the other hand,  I didn’t like re-deploying my email service for every new person that wanted to send an email.

      Can’t say I’ve settled on which way I’m leaning though – both have their merits!

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #987

  • Daniel

    I’m not sure how this code 
    var message = new MailMessage();
    var client = new SmtpClient();
    client.Send(message);is so different than this_bus.Send(new SendEmailMessage());I mean, if the db transction fails and an exception is throw before sending the email, neither version of “send email” is executed. It’s still not clear how the NServiceBus is part of the transaction.

    • Anonymous

      But that’s the thing – the transaction is committed AFTER the message is sent. Imagine this is in a web request – we would have a transaction wrapping the entire controller action call. The transaction isn’t in the SaveToADatabase method, it’s wrapping this entire service method call.

      • Mike

        I have a few questions that might help me undestand what’s going on:

        1 – Does that mean that a subscriber to the event “SendEmailMessage” check that the db transaction was successful? Sounds kind of weird to me

        2 – or is there some kind of event caching in NServiceBus so that the event are only published after a specific method on the bus is called? (I am not familiar with NServiceBus, sorry). In that case, who would call the method?

        Reading your code, when I see _bus.Send(new SendEmailMessage()), I am expecting any subscriber to the “SendEmailMessage” to be immediately notified. The email would also be sent, even if the transaction failed.

        • Anonymous

          A Send isn’t Pub-Sub, the other endpoint isn’t subscribed to messages. The My Service is sending a message to the other endpoint, so that the My Service service already knows that the email sender exists. As opposed to pub-sub, where the email sender knows about the My Service so that it can subscribe.

  • Finoutlook

    If you’re already using an ESB this is a good idea. If not there’s not much to this that having a de-coupled SendMail() method which checks on the success of the service call couldn’t do.

    The advantage of all the messages persisting if the mail server goes down is something that many people handle by using a database.

    • Anonymous

      How would it be decoupled? Having another process check periodically on the success of things and send out emails?

      You can build a queue using a database and scheduled cron jobs, but why? An ESB is easier and you don’t have some process waking up constantly to ping a database.

      • Finoutlook

        No more than the messaging system is waking up constantly to ping a message queue. You don’t have to set up your DB to work that way.

        • Anonymous

          How would you set it up? Usually I see folks doing cron jobs. The messaging system works via events, so it’s not like the process is constantly waking up.

          I only say this cause a system I worked on was moved from a bunch of cron jobs to messaging, and we saw CPU drop quite a bit. But if there are other ways besides cron jobs, I’d love to hear about them.

          • Finoutlook

            For example you could use Service Broker in SQLServer http://msdn.microsoft.com/en-us/library/ms345108(v=sql.90).aspx

          • Anonymous

            Do you have an example not using SSSB? In terms of simplicity, I’m not sure how SSSB is easier than message queuing. Not to mention, MSMQ can be installed on absolutely any Windows OS, but IT admins are much more restrictive about installing DB components on just any machine.

  • ToOsIK

    Hmm, I like your concept, and I understand your approach, but that sounds like a very round-about way to get solution for a very simple problem.

    Why cant you just send the email when you have finished committing the transaction? (the one that wraps your method above)

    I mean it sounds like the email is relevant to the full transaction, not this method, why is this method sending the email? it just looks like the code is not structured properly!

    Very good article tho, clear and informative, just not for this specific scenario you indicated.

    Also, I would use a database table to maintain that, only because I would be able to slice and dice through the data, and infer interesting patterns based on aggregations, like: 

    >when most emails are sent
    >which email profiles are most used
    >what time of the day we have the most load, etc.

    • Anonymous

      I want the sending of an email to succeed if the saving succeeds. Having the message inside the transaction is the only way to really guarantee that this happens. Otherwise, having the message outside the scope is just something else I’ll have to manage in terms of failures and retries. With messaging and NServiceBus, I get that for free.

      Transaction scopes are not something I should have to explicitly manage. They should be managed for me in an infrastructure layer.

      As far as keeping track of statistics, that’s really orthogonal to the act of sending them. If I don’t want to mix the concerns of sending emails versus keeping statistics on emails sent, I’d just publish an event when the email is actually sent so that another tailor-made component can do whatever it needs to do to record whatever information is needed.

  • Pingback: Distributed Weekly 130 — Scott Banwart's Blog

  • Zedd

    This works as long as the service bus participates in the transaction. I’m not sure this is always the case.