diff --git a/src/internal/subscriber.zig b/src/internal/subscriber.zig index 178e0e9..2b195d6 100644 --- a/src/internal/subscriber.zig +++ b/src/internal/subscriber.zig @@ -310,16 +310,21 @@ pub const Subscriber = struct { } const path = w.buffered(); - // DNS + TCP connect through pool_io (Threaded — has working netLookup). - // The resulting fd is used by the Evented io for TLS + WebSocket I/O. + // Connect on the same io that will read the socket. The old split — + // DNS + connect on pool_io, then TLS + WebSocket on self.io — existed + // because Io.Uring never implemented netLookup. Under Threaded both + // are the same backend, so it was a no-op; under any evented backend + // it hands a foreign-domain fd to the reader, which is a defect (a + // blocking fd read from a fiber). Backends that lack netLookup are + // expected to delegate it, not to have callers route around them. + // // each phase labels its catch site so operators can see which layer // is failing across the fleet via relay_subscriber_disconnect_total. - const dns_io = self.pool_io orelse self.io; const host_name = Io.net.HostName.init(self.options.hostname) catch |e| { _ = self.bc.stats.subscriber_disconnect_dns_connect.fetchAdd(1, .monotonic); return e; }; - const net_stream = host_name.connect(dns_io, 443, .{ .mode = .stream }) catch |e| { + const net_stream = host_name.connect(self.io, 443, .{ .mode = .stream }) catch |e| { _ = self.bc.stats.subscriber_disconnect_dns_connect.fetchAdd(1, .monotonic); return e; };