Skip to content

feat(NODE-7932): add serverSelector option to target one member - #5078

Open
tap1r wants to merge 5 commits into
mongodb:mainfrom
tap1r:feat/server-selector
Open

tap1r wants to merge 5 commits into
mongodb:mainfrom
tap1r:feat/server-selector

Conversation

@tap1r

@tap1r tap1r commented Oct 14, 2026

Copy link
Copy Markdown

Summary

PyMongo's server_selector keyword is a driver-specific MongoClient option. It lets an application narrow server selection to one member of a replica set or one mongos, while the driver keeps discovering the rest of the deployment. The Node driver already has directConnection, which is different: that option forces a Single topology and skips replica-set discovery.

This adds the same hook as serverSelector on MongoClient. It is not a URI option, because the value is a function.

new MongoClient(uri, {
  serverSelector(servers) {
    const member = servers.find(server => server.address === 'db-2.example.com:27017');
    return member ? [member] : [];
  }
});

Selection order matches PyMongo:

  1. Read preference, tags, max staleness, and overload deprioritization run first.
  2. serverSelector sees that list, before localThresholdMS, so it can choose a slower eligible member.
  3. The latency window is then applied to whatever the function returns. A one-element result is used as-is.

The function is not called when no server is eligible, for a Single topology (directConnection), for a load-balanced topology, or for operations pinned to a server (getMore, killCursors). Writes still only see writable servers, so this does not send a normal write to a secondary.

Omitting the option leaves selection unchanged.

Test plan

  • Unit tests for parsing, latency-window order, primary-only input, empty eligibility, sharded selection, Single and load-balanced bypass, pinned-server bypass, and Topology#selectServer wiring
  • tsc --noEmit
  • Existing server-selection and connection-string unit tests
  • test/integration/server-selection/server_selector.test.ts on a replica set (needs the driver's auth test user; not run against the local deployment)
    EOF

PyMongo's server_selector keyword filters servers after read preference
and before the latency window. Expose the same MongoClient option so an
application can send eligible operations to one replica set member or
mongos without turning the topology into a standalone.
@tap1r
tap1r marked this pull request as ready for review October 14, 2026 22:27
@tap1r
tap1r requested a review from a team as a code owner October 14, 2026 22:27
@tadjik1 tadjik1 changed the title feat: add serverSelector option to target one member feat(NODE-7932): add serverSelector option to target one member Oct 22, 2026
@tadjik1 tadjik1 added tracked-in-jira Ticket filed in MongoDB's Jira system External Submission PR submitted from outside the team labels Oct 22, 2026
@tadjik1

tadjik1 commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

@tap1r, thank you for taking the time to create this PR! This is tracked in NODE-7932. The team is currently at capacity, so we will be reviewing it over the next few quarters as capacity allows.

@PavelSafronov

Copy link
Copy Markdown
Contributor

Hi @tap1r , thanks again for the PR and your clear write-up.

The reason that the Node Driver does not have this specific functionality is because the custom server selector is optional in the Server Selection specification. We considered it before (NODE-1491) and decided not to add this support because we did not have a concrete use case that required it.

Can you provide some more information about your use case?

Some questions about your case:

  1. What specific problem are you solving? Why do you need to target one specific member? (e.g., sending analytics queries to a dedicated node, keeping traffic off a member during maintenance, data locality.)
  2. What kind of deployment do you have? Replica set, sharded, self-managed, Atlas?
  3. How is the target member chosen? Is it a fixed host, or does your app pick one at runtime?
  4. Do the existing options work for you?
    a. Read preference with tag sets, so you can route reads to tagged members
    b. For sharded clusters, list only the mongos hosts you want in the connection string
    c. If the goal is less about a specific host and more about avoiding slow or lagging members, localThresholdMS and maxStalenessSeconds may help

If none of the approaches work, could you tell us why?

Unfortunately, without a concrete use case, we are unlikely to add this optional functionality to the Node Driver.

@tap1r

tap1r commented Oct 29, 2026

Copy link
Copy Markdown
Author

Thanks @PavelSafronov, and for digging up NODE-1491.

I'm glad you mentioned that the Server Selection spec allows a custom selector function. That's the reason I took this approach in the first place. PyMongo exposes it as server_selector, so they clearly had a use case for it too, and their design might be worth a look.

Taking your questions in turn:

  1. The problem: directed command targeting. A simple, common example is a routine DBA task like compacting a namespace in a rolling fashion. You run compact on each secondary in turn, and then on the primary after stepping it down. That needs each command to reach one specific member, deterministically.
  2. Deployment: replica sets, including on Atlas.
  3. Choosing the member: the tool picks it at runtime from the topology, one member at a time.
  4. Existing options:
    a. Tag sets would normally be the standard answer, with a distinct tag on each member. However, Atlas doesn't allow custom configuration of replica set tags.
    b. Listing mongos hosts doesn't apply, since this is about replica set members.
    c. localThresholdMS and maxStalenessSeconds only affect which eligible members get picked. Neither one can guarantee a particular member.

I really tried, and I couldn't find any combination of tags and read preferences that sends commands to a specific member deterministically.

The one workaround that does work is the internal _selectServer. It isn't a supported API, though, so it's likely to be brittle and break when the driver changes.

How would you suggest solving this?

tap1r added 3 commits November 1, 2026 21:29
Skip the user filter for the connect handshake and endSessions. Reject
servers that were not eligible, pass copies into the filter, and select
the original descriptions.
Deep-copy electionId, topologyVersion, $clusterTime, and error before
the user filter runs, so an in-place edit cannot change SDAM state.
Document preference versus a hard pin, which operations can see a
secondary, and how transactions call the filter. Export the helper
from the bundled test barrel.
@PavelSafronov

Copy link
Copy Markdown
Contributor

Thanks for the detailed reply. I tried your rolling-compact workflow on an Atlas replica set (8.0) using a direct connection per member instead of a custom selector with the following approach:

  • Use your regular MongoClient to discover the members
  • For each member, create a MongoClient for that host with directConnection=true, run the command, and close the client.

The driver sends that command only to that member, and it sends primaryPreferred so secondaries accept it.
compact succeeded on each secondary this way. On the primary, the server requires force: true (or you can trigger a failover first).

Note on Atlas: Atlas doesn’t allow database users to run replSetStepDown, so failover is done through the “Test Failover” feature in the UI or the Admin API. A custom server selector wouldn’t help with this step.

You mentioned in the original post that “directConnection… skips replica-set discovery”. In this workflow, discovery only needs to happen once to build the member list, and the regular client handles that.

Would this approach work for your use case?

@tap1r

tap1r commented Nov 3, 2026

Copy link
Copy Markdown
Author

Yes. That is essentially the implementation I use today.

Performing topology discovery on the main client, then creating a directConnection client per member, is how these commands get a hostname at all. compact is only one of them, and it is not guaranteed to be a short-running command. The primary does not replicate it; the documented procedure is to run it on each secondary in turn. Runtime grows as a function of collection size and database write load, and a single namespace can run for hours under pathological conditions. The direct clients, one or more of them, have to stay up for that entire run, each with its own copy of TLS, auth, serverApi, compression, and timeouts.

The same pattern shows up for the other member-local admin commands: validate, dbStats, collStats, fsync / fsyncLock, replSetResizeOplog, replSetMaintenance, setProfilingLevel, planCacheClear, getParameter / setParameter, and reading or killing operations on one process. None of these are cluster-wide commands. Each one has to land on a chosen mongod.

Atlas does not provide a way to do that with the existing selection options. The predefined replica-set tags (nodeType, provider, region, availability zone) select a class of nodes, and users cannot change them. localThresholdMS and maxStalenessSeconds only rank eligible members. A mongos host list does not apply. So the working approach is to leave the discovered topology and open a Single client directly to that member. That connection rewrites the default primary read preference to primaryPreferred, which is why the secondary accepts the command.

That workaround is why I asked for serverSelector. The main client has already discovered the member and already has a pool to it. The spec allows an application to make the final choice after normal selection. NODE-1491 deferred the hook for lack of a use case; this is that use case, and it is a class of commands rather than one example. replSetStepDown on Atlas is a separate limitation, and I am not asking the selector to solve it.

Whether the driver exposes the optional hook is a product decision. The heavy lifting for the implementation is already in this pull request: selection order, the operations that must skip the hook, copies that cannot change SDAM state, documentation, and tests. The direct client shows that the need is real, and it is what I have to maintain until the driver can target one member of a topology it is already monitoring.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

External Submission PR submitted from outside the team tracked-in-jira Ticket filed in MongoDB's Jira system

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants