src/mongo/db/router_role/README_router_role_api.md
Any code that needs to route operations to the appropiate shard is said to be operating in the Router Role. This contrasts with Shard Role operations, which access data collections directly.
Operations performed in the Router Role must retrieve the routing information for either the target collection or the DBPrimary and dispatch the request to the appropriate shards. If any shard returns an error due to stale routing information on the router node, the operation must refresh the routing data from the config server and retry the entire request.
router_role.h provides the CollectionRouter and DBPrimaryRouter classes. These should be used to route commands either to the shards that own data for a collection or to the DBPrimary shard of a database respectively.
Here are two usage examples:
sharding::router::CollectionRouter router(opCtx, nss);
return router.routeWithRoutingContext(
"<Comment to identify this process>"sd,
[&](OperationContext* opCtx, RoutingContext& routingCtx) {
...
// Dispatch a collection request using `routingCtx`
...
}
);
sharding::router::DBPrimaryRouter router(opCtx, nss.dbName());
return router.route(
"<Comment to identify this process>"sd,
[&](OperationContext* opCtx, const CachedDatabaseInfo& dbInfo) {
...
// Dispatch a DBPrimary request using `dbInfo`
...
}
);
You can also find below two real usage examples for each case:
config.system.sessions collection to on all the shards owning data for that collection.These classes handle the following processes internally:
CachedDatabaseInfo object.RoutingContext gets validated (here you'll find a more clear understanding of what's checked under a RoutingContext validation).When using CollectionRouter or DBPrimaryRouter, keep the following in mind:
CollectionRouter::routeWithRoutingContext() or DBPrimaryRouter::route() must use the provided RoutingContext or CachedDatabaseInfo objects to dispatch a shard-versioned command to the shards. The recommended approach is to use the Scatter-Gather API.For more details on routing internals, see the Versioning Protocols architecture guide.
The MultiCollectionRouter extends the functionality of the CollectionRouter by enabling routing to multiple collections within a single router role loop. This is particularly useful in scenarios where a code block may encounter stale routing errors from more than one collection.
A common use case is an aggregation pipeline that includes multiple $lookup stages. These stages query different foreign collections within the same execution context. If any of these collections has stale routing information, the entire operation must be retried.
std::vector<NamespaceString> nssList{nss1,nss2};
sharding::router::MultiCollectionRouter multiCollectionRouter(
opCtx->getServiceContext(),
nssList
);
multiCollectionrouter.route(
"<Comment to identify this process>"sd,
[&](OperationContext* opCtx,
const stdx::unordered_map<NamespaceString, CollectionRoutingInfo>& criMap) {
...
// Dispatch commands using the `criMap`
...
}
);
You can also find a real usage example for the MultiCollectionRouter here.
This section describes the utility APIs that support router-side operations in MongoDB's sharded cluster architecture. These utilities work in conjunction with the Router Role API to provide standardized methods for shard versioning, command distribution, and query targeting.
While the Router Role API manages the high-level workflow (routing context lifecycle, retry logic, and validation), these utilities handle the implementation details of targeting shards, attaching version metadata, and dispatching commands.
The scatter-gather family of functions provides high-level abstractions for dispatching versioned commands to multiple shards and aggregating their responses.
Executes versioned commands against shards determined by query targeting logic. If the query is empty, the command runs on all shards that own chunks for the collection.
std::vector<AsyncRequestsSender::Response> scatterGatherVersionedTargetByRoutingTable(
OperationContext* opCtx,
RoutingContext& routingCtx,
const NamespaceString& nss,
const BSONObj& cmdObj,
const ReadPreferenceSetting& readPref,
Shard::RetryPolicy retryPolicy,
const BSONObj& query,
const BSONObj& collation
// ... additional parameters
);
Workflow:
gatherResponses()Here is an example of how it works together with the Router Role API:
#include "src/mongo/db/router_role/router_role.h"
#include "src/mongo/db/router_role/cluster_commands_helpers.h" // Contains utility APIs
// Complete router operation using all API layers
StatusWith<BSONObj> executeShardedQuery(
OperationContext* opCtx,
const NamespaceString& nss,
const BSONObj& query) {
// ROUTER ROLE API: Set up routing workflow
sharding::router::CollectionRouter router(opCtx, nss);
return router.routeWithRoutingContext(
"Complete sharded query example",
[&](OperationContext* opCtx, RoutingContext& routingCtx) {
// SCATTER-GATHER API: Automated targeting and dispatch
auto responses = scatterGatherVersionedTargetByRoutingTable(
opCtx,
routingCtx, // From Router Role API
nss,
BSON("find" << nss.coll()),
ReadPreferenceSetting(ReadPreference::PrimaryPreferred),
Shard::RetryPolicy::kIdempotent,
query,
BSONObj()
);
// Internally, scatter-gather uses:
// - QUERY TARGETING API to determine shards
// - SHARD VERSIONING API to attach versions
// Process results
return mergeShardResponses(responses);
}
);
// Router Role API handles stale routing errors and retries
}
Executes versioned commands against an explicitly specified set of shards, bypassing query analysis.
std::vector<AsyncRequestsSender::Response> scatterGatherVersionedTargetToShards(
OperationContext* opCtx,
RoutingContext& routingCtx,
const DatabaseName& dbName,
const NamespaceString& nss,
const BSONObj& cmdObj,
const ReadPreferenceSetting& readPref,
Shard::RetryPolicy retryPolicy,
const std::set<ShardId>& targetShards
);
When to use:
An example of usage:
#include "src/mongo/db/router_role/router_role.h"
#include "src/mongo/db/router_role/cluster_commands_helpers.h" // Contains utility APIs
// Complete router operation using all API layers
StatusWith<BSONObj> executeShardedQuery(
OperationContext* opCtx,
const NamespaceString& nss,
const BSONObj& query) {
// ROUTER ROLE API: Set up routing workflow
sharding::router::CollectionRouter router(opCtx, nss);
return router.routeWithRoutingContext(
"Complete targeted sharded query example",
[&](OperationContext* opCtx, RoutingContext& routingCtx) {
// Custom targeting logic beyond standard chunk-based routing
targetedShardsSet = computeShardsToTargetForSpecialCase(routingCtx);
// SCATTER-GATHER API: Explicitly target computed shard set
auto response = scatterGatherVersionedTargetToShards(
opCtx,
routingCtx, // From Router Role API
DatabaseName::kAdmin, // Custom database name
targetedShardsSet,
BSON("find" << nss.coll()),
ReadPreferenceSetting(ReadPreference::PrimaryPreferred),
Shard::RetryPolicy::kIdempotent,
query,
BSONObj()
).front();
return response;
}
);
// Router Role API handles stale routing errors and retries
}
Most router-side operations should use the high-level scatter-gather functions. Direct use of lower-level APIs like buildVersionedRequests/gatherResponses is only permitted in exceptional cases:
All router-side operations that target sharded collections must include versioning metadata to ensure routing consistency and detect stale metadata. Use the standardized appendShardVersion functions to attach version information:
// Append shard version to an existing command object
BSONObj appendShardVersion(BSONObj cmdObj, ShardVersion version);
// Append shard version to a BSONObjBuilder
void appendShardVersion(BSONObjBuilder& cmd, ShardVersion version);
Usage example:
BSONObj cmd = BSON("find" << "myCollection");
auto versionedCmd = appendShardVersion(std::move(cmd), routingCtx.getShardVersion(shardId));
Important guidelines:
The Router Role is composed of three complementary API layers:
ROUTER ROLE API
provides RoutingContext to
SCATTER-GATHER API (Command Distribution)
uses
SHARD VERSIONING API