-
Type:
Bug
-
Resolution: Unresolved
-
Priority:
Unknown
-
None
-
Affects Version/s: 7.6.0
-
Component/s: Networking
-
None
-
None
-
None
-
None
-
None
-
None
-
None
Question: is it intended that the Node driver's autoSelectFamily default of true
makes Node's process-level opt-out unreachable for MongoDB sockets?
Expected
Node exposes a process-wide default for Happy Eyeballs, settable three documented ways:
- --no-network-family-autoselection
- NODE_OPTIONS="--no-network-family-autoselection"
- net.setDefaultAutoSelectFamily(false)
We expected a MongoClient constructed without an explicit autoSelectFamily option to
inherit that process default, as other socket-level behavior does.
Actual
All three are silently ignored for driver sockets. The driver passes
autoSelectFamily: true explicitly on every net.createConnection() call, and a
per-socket option overrides the process default.
node process default autoSelectFamily : false value driver passes to the socket : true autoSelectFamilyAttemptTimeout passed : undefined
Identical output for all three forms above.
Minimal reproduction
No running mongod required – the socket call is intercepted before connecting.
// repro.js const net = require('net'); net.createConnection = function (opts) { console.log('node process default autoSelectFamily :', net.getDefaultAutoSelectFamily()); console.log('value driver passes to the socket :', opts.autoSelectFamily); console.log('autoSelectFamilyAttemptTimeout passed :', opts.autoSelectFamilyAttemptTimeout); process.exit(0); }; const { MongoClient } = require('mongodb'); new MongoClient('mongodb://127.0.0.1:27017/?directConnection=true') .connect() .catch(() => {});
node --no-network-family-autoselection repro.js
Where it comes from
lib/connection_string.js:565 declares a hard default:
autoSelectFamily: {
type: 'boolean',
default: true
}
lib/cmap/connect.js:226 copies any non-null legal socket option onto the connect call:
for (const name of exports.LEGAL_TCP_SOCKET_OPTIONS) { if (options[name] != null) { result[name] = options[name]; } }
Because the default is true, the value is never null, so it is always copied, so the
per-socket value always wins over the process default.
The part that looks unintended
The sibling option behaves the opposite way. autoSelectFamilyAttemptTimeout is declared
with no default, stays undefined, is therefore not copied, and the process-level
--network-family-autoselection-attempt-timeout does take effect.
| Option | Driver default | Passed to socket | Process-level setting honored |
|---|---|---|---|
| autoSelectFamily | true | always | no |
| autoSelectFamilyAttemptTimeout | none | never | yes |
So of two adjacent options controlling the same Node feature, one honors process-level
configuration and one cannot. The TypeScript declaration also types it as optional
(autoSelectFamily?: boolean, mongodb.d.ts:8518), implying "unset" is a meaningful
state that the runtime never actually permits.
We also noticed TODO(NODE-6449) in
lib/client-side-encryption/auto_encrypter.js:183 regarding autoSelectFamily not being
threaded through client options, which suggests adjacent plumbing has already been flagged.
Why it mattered for us
Istio in ambient mode auto-allocates both an IPv4 and an IPv6 synthetic address for every
ServiceEntry, regardless of whether the cluster is dual-stack. In our IPv4-only staging
cluster this made DNS return two addresses where it previously returned one, which armed
Happy Eyeballs and dropped the effective connect deadline from connectTimeoutMS (30s) to
the 250ms per-attempt budget. CPU-bound services routinely exceed 250ms of event loop lag,
so connects began failing as MongoNetworkError: connect ETIMEDOUT wrapping an
AggregateError, cascading into repeated MongoPoolClearedError (one pod reached
connectionGeneration: 295).
--no-network-family-autoselection is the standard mitigation and is what we reached for
first. It deployed cleanly, changed nothing, and gave no indication it had not applied –
the only signal was that internalConnectMultiple frames were still present in stack
traces. Setting autoSelectFamily=false&family=4 on the connection string resolved it.
Suggested resolution
Removing default: true from the option descriptor would make the process-level setting
reachable while preserving current behavior for everyone who has not opted out: with the
option unset it simply would not be passed, and Node's own default (true on Node >= 20)
would apply. That appears backward compatible.
If the hard default is deliberate – i.e. the driver intends to own this setting regardless
of process configuration – that would be worth stating in the autoSelectFamily docs,
since the current documentation does not indicate that Node's own flags will not apply.
- is related to
-
NODE-6366 Remove unnecessary Node.js option type patching
-
- Backlog
-