Skip to content

feat!: the socket transport by default, and options that say when they stop - #45

Merged
Taure merged 4 commits into
mainfrom
feat/socket-transport-by-default
Aug 10, 2026
Merged

feat!: the socket transport by default, and options that say when they stop#45
Taure merged 4 commits into
mainfrom
feat/socket-transport-by-default

Conversation

@Taure

@Taure Taure commented Aug 10, 2026

Copy link
Copy Markdown
Owner

transport defaults to socket rather than inet.

Why now

The saturation bench answered the question the per-operation numbers could not. At sixteen connections it changes nothing - every pooled client plateaus around 16k q/s and the pool is the ceiling before the client is. At sixty-four it is about a fifth more queries a second for about a sixth less of the machine. The transport does everything the driver does on every machine minato runs on, Windows included, and the Windows workflow proves it.

transport => inet is one option away for a machine or a workload that disagrees.

What else had to change

minato_socket:setopts/2 set nodelay and silently dropped everything else. That was survivable while the transport was opt in. As the default it is a defect: keepalive on a connection through a firewall would have quietly become nothing. It now

  • sets nodelay, keepalive, recbuf and sndbuf,
  • accepts and ignores binary, {mode, binary}, {packet, raw}, {packet, 0}, {active, false} and buffer, which it always does or has no equivalent of,
  • refuses anything else at connect rather than dropping it.

The moduledoc already promised that refusal. The code did not do it.

Coverage

minato_socket_SUITE becomes minato_transport_SUITE and runs all fourteen cases against both transports as groups. Before this the rest of the suite covered whatever the default was and the socket suite covered the other one, so flipping the default without this would have moved which transport is tested nowhere rather than fixing it. Two new cases cover the option handling from both sides: options the driver takes are taken here, and one it cannot honour is refused.

Breaking

  • A connection that passed a socket_option outside the list above now fails at connect on the default transport instead of connecting without it. That is the intent - it was already not being applied.
  • bench/README.md per-operation table is annotated with the transport it was measured on; minato_bench takes the default, so a run made now measures socket.

Checks

fmt --check, xref, dialyzer, elp eqwalize-all, elp lint, ex_doc clean. 1398 eunit, 169 ct including both transport groups with TLS and LISTEN over each.

…y stop

`transport` defaults to `socket` rather than `inet`. It does everything the
driver does on every machine minato runs on, and the saturation bench says
where that is worth having: nothing at sixteen connections, where the pool is
the ceiling before the client is, and about a fifth more queries a second for
about a sixth less of the machine at sixty-four. `transport => inet` is the way
back.

A transport that is chosen by default has to answer for the options handed to
it. `minato_socket:setopts/2` set `nodelay` and dropped everything else on the
floor, which was survivable while it was opt in and is not now: `keepalive` on
a connection through a firewall would have quietly become nothing. It now sets
`nodelay`, `keepalive`, `recbuf` and `sndbuf`, accepts and ignores the ones it
always does or has no equivalent of, and refuses anything else at connect
rather than silently dropping it.

The socket suite becomes `minato_transport_SUITE` and runs every case against
both transports as groups. Before this the rest of the suite covered the
default and the socket suite covered the other one; leaving it alone would have
swapped which transport was tested nowhere.
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown

🟡 Code Coverage — 88.8%

1724 of 1941 lines covered.


✅ ELP Lint

No diagnostics.

Taure added 3 commits August 10, 2026 21:30
Windows found this the moment the socket transport became the default and the
listener started running over it. A peer that closes while a read is
outstanding does not answer the completion with an error there - it aborts it,
and `{'$socket', _, abort, _}` fell through `handle_message/2` to `ignore`. The
owner then waited for a message that was never coming, which is a listener that
never reconnects. `activate/1` on the same socket afterwards raised
`{invalid, state}` out of `prim_socket:nif_recv/4` rather than answering, which
is the listener crashing instead.

`abort` is now a close for both shapes of wait, and `minato_socket:activate/1`
answers `{error, closed}` for a socket with no state left to read in.

The case is in the transport suite for both transports, because the three ways
this can be said - a `select` that never comes, a completion answered with an
error, an abort - are one invariant: an owner waiting on a connection that died
has to hear about it.
`minato_listener` re-activates whenever anything about its subscribers changes:
a second subscriber to a channel already listened to, a subscriber going down,
an unsubscribe. On the driver that is `{active, once}` set twice and costs
nothing. On a socket handle it is a second read on the same handle, which a
completion system refuses outright - the Windows run raised `{invalid, state}`
out of `prim_socket:nif_recv/4` and took the listener down with it.

`activate/1` on a connection that is already waiting now answers with the
connection. One wait is what both calls asked for.
The case asserted the close arrived as a message, but arming a socket the
server has already gone from answers `{error, {socket, closed}}` instead, and
which of the two happens is a race with the server's own shutdown - OTP 29 took
the other branch and the case called it a failure. Both are the connection
saying it closed, which is the whole of what the case is for.
@Taure
Taure merged commit ec530d3 into main Aug 10, 2026
19 checks passed
@Taure
Taure deleted the feat/socket-transport-by-default branch August 10, 2026 19:50
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