v1.2.0
Post-1.1.0 work split across three tracks: a client-side socket-backend
opt-in, a round of hot-path micro-optimisations on the send and
receive paths, and a migration fix for the default gen_udp client.
Added
- Opt-in
socket_backend => socketfor client connections. Routes
the client throughquic_socket:open_for_send/2so it picks up the
OTP socket NIF on Linux with GSO available per-message via cmsg,
instead of thegen_udpport driver. +18% download throughput on
arm64 Linux docker (10 MB bench); upload is neutral. (#88, #91) - Client migration (
quic:migrate/1) now works on the opt-in socket
backend. Rebind closes the old OTP socket, stops its dedicated
receiver process, opens a fresh one, and threads the new handle
through the connection state. (#90) quic_socket:start_client_receiver/2/stop_client_receiver/1:
dedicated receiver process for the socket-backend client path
(the OTP socket NIF has no{active, N}mode). (#88)quic_socket:set_socket/2swaps the underlying socket handle
inside a#socket_state{}while preserving batching configuration.
Used by the migration rebind path. (#93)- Instrumentation counters
ack_sentandretransmitson
quic_connection:get_stats/1and the throughput bench output
(Phase 0a). (#77, #78)
Fixed
quic:migrate/1on the default gen_udp client no longer drops
post-migrate traffic. Rebinding previously left
#state.socket_statepointing at the just-closed old socket; every
send went through the dead handle and was silently dropped. Also
flushes any pending batch to the old socket before rebind so
pre-migrate packets reach the server under their original CID.
(#93)quic_dist: simultaneous-connect deadlock in the accept path.
Two nodes dialling each other within a tight window wedged both
net_kernel:connect_node/1calls indefinitely. The old accept
path ran the dist worker through a nine-hop handoff
(register_pending / controller rendezvous in acceptor_loop) before
reachingdist_util:mark_pending, so net_kernel's tie-breaker
arbitration never ran in time. Collapsed to the TCP-dist shape:
accept_connection/5runsset_supervisor+start_timer+
handshake_other_startedinline. Docker 5-node regression now
passes 5/5. (#106)quic_dist: batch-yield path ininput_handler_loopcould lose
or reorder buffered dist bytes when the mailbox had backlog.
Yield now threads the buffer remnant through the normal return
channel instead of piggybacking on the self-message. (#104)quic_dist_user_stream_SUITE/accept_user_streams/2doc:
refreshed to match the auto-assign / direct
{quic_dist_stream, _, {data, _, _}}delivery shape. (#105)docker/dist: 3+ node cluster mesh formation. Each node now dials
only higher-named peers and boots with-connect_all false, so
globaldoes not re-introduce cross-dials behind the explicit
test topology. (#95, #106)- h3: preserve WebTransport and unknown SETTINGS identifiers in the
peer settings map so extension-stream hooks can read them. (#96) quic_socket: client migrate path opens the new socket before
closing the old one, avoiding a window where the client has no
valid send handle. (#97)quic_socket:client_recv_loopexits cleanly on unexpected
socket errors instead of spinning. (#98)quic_socket: clear the pending batch buffer on flush error so
stale frames do not get retried on the next flush. (#99)quic:connect/4: reject thesocket+{socket_backend, socket}
option combination with a clear error instead of silently
overriding one. (#100)- Client connection: treat receiver-process exit as a fatal error
and close the connection, matching server behaviour. (#101) - Server: build a per-connection sender even when
server_send_batchingisfalseso the direct-send path uses the
samequic_socketshape as the batched path. (#102, #103)
Performance
- Fuse per-packet cwnd + pacing check into
quic_cc:send_check/3
(one BIF call and one record match instead of the previous four).
(#79) - Hoist per-chunk lookups (
stream_urgency,max_stream_data_per_packet,
pre-computed stream-frame header prefix) out of the chunked send
loop. (#80, #85) - ACK 1-RTT packets immediately on reorder (RFC 9002 §6.2) while
keeping the decimation window for in-order traffic. (#81) - Fast-path single-stream-frame in
contains_ack_eliciting_frames/1
on the bulk-upload hot path. (#82) - Thread the updated
socket_stateback fromdo_socket_sendvia
the return value, dropping the process-dictionary roundtrip. (#83) - Replace the
crypto:exor/2NIF call with inline Erlang XOR for
the 1-4 byte header-protection mask. (#84) - Inline the
?QLOG_ENABLEDcheck at packet/frame event call
sites so the event-map is never built when qlog is off. (#86) - Coalesce the
monotonic_timesamples on the receive hot path
(one BIF call per received datagram instead of three). (#87) - Flush the pending stream-data batch before emitting an ACK-only
packet so it does not break GSO uniformity on the opt-in socket
backend. +6.4% upload throughput on arm64 Linux docker. (#92) - Re-enable GSO on the opt-in socket-backend client: drop the
socket-levelUDP_SEGMENTsetsockopt and rely on per-message cmsg
viaflush_gso/1. (#91)