When a client terminates a connection

Eric Wong normalperson at yhbt.net
Wed Nov 28 21:10:40 UTC 2012

Andrew Stewart <boss at airbladesoftware.com> wrote:
> Hello,
> I have run into the following situation several times in the past few months:
> (My stack is Nginx -- Unicorn -- Rails 3.0.12)
> 1. Client clicks a delete link in my webapp.
> 2. Rails starts processing the appropriate destroy action.  This updates various things in the database (but not in a single db transaction).
> 3. Client terminates the connection.
> 4. Rails stops executing the destroy action wherever it has got to, leaving the latter part of the action unexecuted and my db inconsistent.
> 5. Rails and Unicorn don't write anything to their logs; Nginx writes a 499.
> This question has been asked before[1] but regrettably I don't
> entirely understand all the solutions proposed :)
> I plan to wrap all the changes made by my destroy action in a single
> db transaction.  I'm assuming this will ensure db consistency, even
> when subject to awkward clients.  Is that correct?

Yes (assuming all your DB actions in the transaction may be
rolled-back).  I'm no expert in DBs, but I recommend you always
use transactions when data consistency is required.

> (I'll probably also move the action's work onto a background process,
> e.g. with delayed job.)

This also works, too, and is likely better if your DB actions take
a long time.

> Now apologies if I'm barking up the wrong tree here...in general is it
> possible to configure Unicorn to buffer the request and:
> - if the client terminates before the request is fully received, don't
> pass it to Rails at all;
> - else pass the complete request to Rails and let Rails execute it
> fully, even if client terminates in the meantime?

Yes.  Using the Unicorn::PrereadInput middleware (before Rails or any
other middleware/framework touches env["rack.input"]) should do
everything you want.

> The thread[1] also mentioned Nginx's proxy_ignore_client_abort.  If
> that's relevant, has anyone tried it?

I haven't, perhaps Jesse can respond?

> [1]
> http://rubyforge.org/pipermail/mongrel-unicorn/2011-August/001096.html

On a related note, Tom Burns has/had the same problem[2].  Unlike you,
he wanted to avoid processing the request in as many cases as possible.
The solution he came up with isn't 100% foolproof, either, but his
primary goal was to reduce load on an overloaded system.

I haven't heard back on results of our nasty/crazy solution, though.

If you do combine Tom's solution with yours, you still need to ensure
DB consistency in the app.

[2] http://mid.gmane.org/CAK4qKG32dGgNbzTzCb6NoH5hu_DrHMOFaFhk-6Xvy-T86++ukA@mail.gmail.com

More information about the mongrel-unicorn mailing list