

Abort closed clients to save capacity by sirupsen · Pull Request #1227 · puma/pu...
source link: https://github.com/puma/puma/pull/1227
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Have you ever wondered what happens when a client refreshes their browser really fast? According to Wireshark, Chrome (I haven't tested other browsers) will properly send an RST packet. At Shopify, we see this pattern a lot during flash sales where people are refreshing their browsers aggressively at the top of the hour pending a release.
This will propagate through your reverse proxies and finally update the state in the TCP backlog to a closing state. Your Ruby webserver, however, is less fortunate—it'll pick up the client, go through the Rack middleware stack, render the response, and write it to the client. Only then it'll find out that it's doing all of this on a one-directional socket and raise an error like Errno::EPIPE
. Below is a picture of this scenario (it uses Unicorn as a webserver as this is where I was fixing this the first time around, replace it with Puma and it's the same):
When Req 2
, which has been closed by the client, is accept(2)
ed by Puma it'll go through Puma's client handling and finally run @app.call
. This could mean we're easily spending 10s to 100s of milliseconds rendering a response to a client whose connection is closed. This steals capacity from actual, legitimate users.
In 2012 Tom Burns submitted a patch to Unicorn addressing this problem. All credit to Tom Burns and Eric Wong for figuring this out 5 years ago. Interest was renewed recently internally because this patch doesn't work well for clients that are not on the loopback device. The link explains this in detail, I won't duplicate this here—but feel free to ask questions! Eric and I collaborated on a new patch upstream that's pretty much identical to this one. It simply inspects the TCP state of the client socket it's accepting and aborts if it's in a closed or closing state. While we don't use Puma for Shopify Core, we happily use it in many other apps. I'd love to hear what you think about putting this patch in Puma as well. It's saved us from many DDoSes in the early days and saved us tremendous capacity during sneaker drops (and other sales).
The production symptom of this for us is that Nginx will emit a lot of 499 (client aborted connections), but Rails will emit 200s for these 499s. With a connection abortion patch, we can subtract the throughput of 499s from legitimate status codes.
I tested this patch with a simple script that'll start a request and close it immediately. If you run this script against a Puma server without this patch, you'll see 10 requests going through. If you run it with this patch, no requests will go through.
Recommend
-
106
logrus - Structured, pluggable logging for Go.
-
51
使用 spatie/laravel-pjax 的时候遇到总是 abort(422) 的情况,查了一圈大多说原因是渲染的页面里找不到监听的 pjax container。 可检查过后我的页面里都是有 id="pj...
-
16
关于 OpenResty 里的 ngx.on_abort ,官方文档里是这样说明的: Registers a user Lua function as the callback which gets called automatically...
-
10
We might want to use AbortController to abort multiple fetch requests in JavaScript. How might we accomplish this? A Quick Refresh: Aborting One Request Let’s quickly refresh ourselves on how to abort one fetch re...
-
8
-
10
手淘架构组招人 iOS/Android 皆可,地点杭州,有兴趣的请联系我!! iOS内存abort(Jetsam) 原理探究苹果最近开源了iOS系统上的XNU内核代码,加上最近又开始负责手淘/猫客的稳定性及性能相关的工作,所以赶紧拜读下苹果的大作。今天主要开始想...
-
4
Copy link Member flip1995 commented...
-
1
abort immediately on bad mem::zeroed/uninit #105997
-
3
1227 - 自制电路测试锂电池放电 ...
-
4
1227 - 重新学习 Swift & SwiftUI ...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK