Set SO_LINGER to {true, 0} before the linger loop - #1715
Conversation
|
I am unable to make the test fail without the source changes, waiting on CI to see if it fails there. |
|
Wait nevermind, CI has the source change so it won't fail there. Can you tell me how you ran the test to confirm that it failed without the source code changes? If the test doesn't fail before the source code change then we should just not have it. It's OK if the test is too difficult to create and the change is obvious. |
Cowboy already runs an application-level linger loop after shutdown(write) to drain the peer side. However, if the connection process is killed while in or after that loop the socket can stay open and the OS port leaks. See erlang/otp#9529. Setting SO_LINGER to {true, 0} once the FIN has been sent forces the OS to send RST and release the socket whenever the BEAM closes the port, including on abrupt process exit.
23d73f4 to
1089e6f
Compare
|
You're right — I can't give you a dependable pre-fix failure recipe, so I've removed the test and kept just the What was happening: the case set So I've dropped |
Implements the linger fix discussed in #1672 and the related erlang/otp#9529.
When the connection process is killed during the application-level linger loop with outbound data still unacknowledged by the peer, the underlying socket can stay open and the OS port leaks. Setting
SO_LINGERto{true, 0}right aftershutdown(write)makes the OS send RST and release the connection immediately whenever the BEAM closes the port, instead of waiting on the half-closed state.Applied in both
cowboy_http:terminate_linger/1and the mirrored block incowboy_http2:terminate_linger/1.Note on testing
An earlier revision included an end-to-end CT case to reproduce the leak, but as @essen pointed out it does not reliably fail without the fix, so it has been removed per his suggestion.
Why it couldn't be made deterministic: the case set
{sndbuf, 2097152}so the 1 MB body would buffer in the kernel and the process would reachterminate_lingerwith the data still unacknowledged. On Linux, though, an explicitSO_SNDBUFis capped bynet.core.wmem_max(212992 by default) and setting it explicitly also disables send-buffer autotuning, so the real buffer stays well under 1 MB. The body send then blocks insidecowboy_req:reply, leaving the process parked in the send rather than in the linger loop — so killing it behaves the same with or without the fix. The fix itself is straightforward, so this PR keeps just thesetoptschange.Verified locally (Linux, OTP 28):
make ct-http— 35/35 okmake ct-http2— 18/18 ok