Skip to content

Switch from gen_tcp to the socket NIF API - #9

Merged
lpgauth merged 1 commit into
masterfrom
socket-backend
Aug 23, 2026
Merged

lpgauth merged 1 commit into
masterfrom
socket-backend

Conversation

@lpgauth

@lpgauth lpgauth commented Aug 15, 2026 •

Copy link
Copy Markdown
Owner

Summary

Ports the acceptor and connection loop from gen_tcp to the socket NIF API.

  • The raw SO_REUSEPORT setsockopts are replaced with the portable {socket, reuseport} option.
  • The listener's send_timeout/send_timeout_close behaviour is emulated with socket:send/3 and close-on-error.
  • Accepted sockets run with {otp, select_read}: each successful recv re-arms the read select inside the same NIF call, so the steady state is one $socket message plus one recv per request instead of recv/EAGAIN/select/recv.

Benchmarks (M2 Pro, 64 connections, empty 200 handler):

  • keep-alive: 107.5k -> 115.1k req/s (+7%)
  • connection-per-request: 23.3k -> 21.8k req/s (-6%) on stock OTP. socket:close finalizes on a dirty scheduler; with the fix pending upstream this becomes 24.3k (+4%).

{otp, select_read} was added in OTP 27.3, so the CI matrix drops 25 and 26. Dialyzer is skipped on 27 because its socket specs predate select_read (compile/xref/eunit still run there).

Use socket:accept/recv/send/close throughout the acceptor and
connection loop. The raw SO_REUSEPORT options are replaced with the
portable {socket, reuseport} option and the listener's send_timeout
behaviour is emulated with socket:send/3 plus close-on-error.

Accepted sockets have {otp, select_read} enabled: every successful
recv re-arms the read select inside the same NIF call, so the steady
state is one $socket message plus one recv per request instead of
recv/EAGAIN/select/recv. Keep-alive throughput improves ~7%
(107.5k -> 115.1k req/s, 64 connections, M2 Pro). Connection-per-
request drops ~6% on stock OTP because socket:close finalizes on a
dirty scheduler; a fix is pending upstream.

select_read requires OTP 27.3, so the CI matrix drops 25 and 26.
Dialyzer is skipped on 27 since its socket specs predate select_read.
@lpgauth
lpgauth merged commit 7302d3c into master Aug 23, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant