Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Two Phase Commit - How to use my queue effectively?

What I am about to describe is essentially a two phase commit problem between two disparate systems and I am looking for advice on how to handle it. In our web application, we offload some expensive/3rd-party operations, such as sending emails, to out-of-band background worker processes (we call it our job infrastructure.)

To send an email, for instance, we create both an email object and email job in our database. We then have to wait for our job monitor to pickup the email job and send it. The job monitor essentially works by polling the database every few seconds when it is idle.

This, however, adds a delay into email sending and adds what I view as undue load on the database with the polling. It would be much nicer if we could immediately put the email job on the queue as soon as the email is created.

However, this currently fails for two reasons. First, the queue is often much faster than the web request. The email is picked up for processing before the web request has committed its database transaction, so it can't properly generate the email. Second, if the web request fails, it rolls back its database transaction which means the the email should not be sent. However, if it's already been put on the queue, then it's no longer in the request's control.

Is there a good strategy for creating a two-phase commit between the queue and the database? For reference, we are using RabbitMQ and MySQL with InnoDB tables. One idea I head was to stick the email jobs on the queue after the database transaction has been committed, but that leaves the possibility that the email never gets queued. I'll still have to create a polling process that watches for emails that should have been sent and weren't.

like image 334
dave mankoff Avatar asked Mar 02 '12 17:03

dave mankoff


People also ask

What is the problem that two-phase commit is trying to solve?

The crux of the problem is that a transaction can commit its updates on one database system, but a second database system can fail before the transaction commits there too. In this case, when the failed system recovers, it must be able to commit the transaction.

How does 2 phase commit work?

The coordinator implements the commit handling in two phases. It first sends the prepare request to each of the participants. Once it receives a successful response from all the participants, the coordinator marks the transaction as prepared to complete. Then it sends the commit request to all the participants.

What is two-phase commit in MQ?

Two-phase commit - for transactions that update resources owned by more than one resource manager. This is the standard distributed syncpoint protocol. It involves more logging and message flows than a single-phase commit.

What happens when a participant fails in Phase 2 of the 2 phase commit protocol?

The two-phase commit protocol is designed to handle system and media failures in such a way that data integrity is preserved across all the participating database servers. The two-phase commit protocol performs an automatic recovery if a failure occurs.


1 Answers

I realise this is a couple years late :) but I wanted to add some thoughts on this for others that run into this question.

You could send the message delayed to increase the chance the DB is ready when the jobs gets it. I've never used RabbitMQ but I found this example of using a rabbitMQ queue as a delayed queue How to create a delayed queue in RabbitMQ?. Your email-job would still need to deal with any uncommented records since delaying isn't a deterministic method of dealing with distributed processing.

That said, it feel like there's room to improve your design. Generally with service architectures its a bad idea to share db tables between services. It leads to distributed transaction and scaling issues. I would try to insure that the RabbitMQ message contains all the data it needs to process the email independently of any other service. Or if it needs more data, it should request that data through a ServiceBus request instead of a DB query.

like image 119
eSniff Avatar answered Oct 16 '22 07:10

eSniff