HerokuLex NevaIncident / postmortem
Latency Incident Report: Socket Bind Before Connect
Summary
An August 11, 2016 incident caused increased routing latency in Heroku’s EU Common Runtime, even though CPU, packet, memory, request, and socket metrics remained normal. The routing layer received EADDRINUSE while connecting to dynos, leading engineers to investigate Linux TCP port allocation, TIME_WAIT, and the Erlang VM’s system calls with strace. They found that the VM called bind() with 0.0.0.0:0 before connect(), forcing Linux to allocate an ephemeral source port without knowing the destination; this made the ephemeral port range an effective per-instance traffic limit and could fail before 65,536 connections. Testing reproduced the failures and showed that setting SO_REUSEADDR before bind(), exposed in Erlang as {reuseaddr, true}, removed the bind errors; the corresponding Vegur patch was deployed a few days later.
Context
On August 11, 2016, Heroku saw increased routing latency in the EU region of the Common Runtime. Standard indicators, including CPU, packet rates, memory, request rates, and socket usage, were normal or better than in the US. The routing layer was also receiving the unexpected EADDRINUSE error while connecting to dynos, prompting an investigation into Linux networking and the Erlang VM.
Approach / What changed
Engineers traced the Erlang VM’s socket calls with strace and found that it called bind() with 0.0.0.0:0 before connect(). They reproduced the resulting port-allocation failures with a C program, then added SO_REUSEADDR before bind() and confirmed the behavior. The Erlang fix used {reuseaddr, true}, and the change was applied to the Vegur HTTP proxy library.
Takeaways
Calling bind() before connect() makes Linux choose an ephemeral source port without destination information, imposing a much tighter effective limit than the TCP 4-tuple model suggests.
Linux can return EADDRINUSE during ephemeral-port allocation before 65,536 connections because it randomly probes a limited sequence of candidate ports rather than scanning every port.
Setting SO_REUSEADDR before bind() resolved the reproduced failures, and the Erlang equivalent is the {reuseaddr, true} socket option.
Source and freshness
- Source
- heroku.com
- Published
- Added to Yomu
Summaries are generated from the original by a language model. Check the original before relying on it.
Keep reading
Related reading
The Good Stuff
Paid picks we use ourselves. Links carry a referral code — a subscription through one earns us a commission at no extra cost to you.
The best engineering reads, once a week
One useful email. Unsubscribe anytime.