Deep Dive into mongos Connection Model and Adaptive Thread Pools in MongoDB
This article explains how MongoDB's mongos router manages client‑to‑mongos and mongos‑to‑mongod connections, compares the default per‑connection‑per‑thread model with the adaptive thread‑pool introduced in version 3.6, details the internal traditional and ASIO connection pools, relevant configuration parameters, and provides practical guidance on interpreting connection‑pool statistics.
Li Pengchong, a senior operations engineer at NetEase Games and MongoDB/MySQL enthusiast, focuses on developing and operating a MongoDB SaaS platform.
In NetEase's internal MongoDB sharded cluster, a common SRE question is the relationship between client‑to‑mongos connections and mongos‑to‑mongod connections—whether it is a 1:1 mapping.
The article first clarifies that, regardless of the underlying I/O model (ASIO or legacy), mongos uses a "per‑connection‑per‑thread" model by default, allocating 1 MiB of stack memory for each thread handling a client connection.
static const size_t STACK_SIZE = 1024 * 1024; // 1 MiB per threadTo reduce memory consumption when many connections arrive, MongoDB 3.6 introduced an adaptive thread pool . A configurable number of worker threads (adaptiveServiceExecutorReservedThreads) are pre‑created, and additional workers are spawned only when needed. Workers that stay idle below a configurable threshold (adaptiveServiceExecutorIdlePctThreshold) are automatically destroyed.
adaptiveServiceExecutorReservedThreads : number of pre‑created worker threads (default = CPU cores / 2)
adaptiveServiceExecutorIdlePctThreshold : percentage of idle time below which a worker is destroyedAdaptive thread‑pool mode is disabled by default and must be enabled via configuration:
# yaml
net:
serviceExecutor: adaptive
# ini
serviceExecutor = adaptiveIn adaptive mode, each worker can handle multiple connections, and the exact client‑to‑worker ratio is dynamic, based on runtime load. Statistics can be inspected with:
mongos> db.serverStatus().network.serviceExecutorTaskStats
{
"executor" : "adaptive",
"totalQueued" : NumberLong(25),
"totalExecuted" : NumberLong(25),
"threadsInUse" : 1,
"threadsRunning" : 8,
"threadsPending" : 0,
"threadCreationCauses" : {
"belowReserveMinimum" : NumberLong(8),
"starvation" : NumberLong(0),
"stuckThreadsDetected" : NumberLong(0),
"replacingCrashedThreads" : NumberLong(0)
},
"metricsByTask" : {
"processMessage" : { "totalQueued" : NumberLong(12), "totalExecuted" : NumberLong(12), "totalTimeExecutingMicros" : NumberLong(11114), "totalTimeQueuedMicros" : NumberLong(5) },
"sourceMessage" : { "totalQueued" : NumberLong(12), "totalExecuted" : NumberLong(12), "totalTimeExecutingMicros" : NumberLong(78), "totalTimeQueuedMicros" : NumberLong(132) },
"exhaustMessage" : { "totalQueued" : NumberLong(0), "totalExecuted" : NumberLong(0), "totalTimeExecutingMicros" : NumberLong(0), "totalTimeQueuedMicros" : NumberLong(0) },
"startSession" : { "totalQueued" : NumberLong(1), "totalExecuted" : NumberLong(1), "totalTimeExecutingMicros" : NumberLong(19), "totalTimeQueuedMicros" : NumberLong(42) }
}
}mongos maintains two kinds of connection pools:
Traditional pool (DBConnectionPool) – used for many legacy operations.
ASIO pool (ConnectionPool) – used by the TaskExecutor infrastructure.
Key structures of the traditional pool:
typedef std::map
PoolMap; // servername -> pool
PoolMap _pools;
int _maxPoolSize;
int _maxInUse; // added in 3.6Each PoolForHost holds a stack of idle connections and a counter of checked‑out connections:
std::stack
_pool; // idle connections
int _checkedOut; // connections currently in useParameters controlling the traditional pool include connPoolMaxShardedConnsPerHost , connPoolMaxConnsPerHost (soft limits on idle connections) and connPoolMaxShardedInUseConnsPerHost , connPoolMaxInUseConnsPerHost (hard limits on total connections).
The ASIO pool is built around an unordered_map<HostAndPort, std::unique_ptr<SpecificPool>> where each SpecificPool maintains four sub‑pools (ready, processing, checked‑out, dropped) and respects options such as minConnections , maxConnections , and maxConnecting to bound creation speed.
stdx::unordered_map
> _pools;
struct Options {
size_t minConnections = kDefaultMinConns;
size_t maxConnections = kDefaultMaxConns;
size_t maxConnecting = kDefaultMaxConnecting; // limits concurrent connection creation
Milliseconds refreshTimeout = kDefaultRefreshTimeout;
Milliseconds refreshRequirement = kDefaultRefreshRequirement;
Milliseconds hostTimeout = kDefaultHostTimeout;
};
const Options _options;TaskExecutor is the scheduler that executes remote commands. Each TaskExecutor contains a single thread (default 8 MiB stack) and an ASIO connection pool. The TaskExecutorPool manages a fixed executor for ConfigServer communication and a configurable number of anonymous executors for ShardServer communication (parameter taskExecutorPoolSize ).
NetworkInterfaceThreadPool : schedules logic
NetworkInterfaceASIO : holds ASIO ConnectionPool and a single threadConnection‑pool statistics can be retrieved with connPoolStats and shardConnPoolStats , which report counts for inUse , available , created , and refreshing connections per host. The total mongos‑to‑mongod connections for a host equal the sum of the two command outputs.
mongos to host A connections = db.runCommand({shardConnPoolStats:1}).hosts[A] + db.runCommand({connPoolStats:1}).hosts[A]Because of replica‑set routing and delayed cleanup of failed connections, the reported numbers may differ from actual socket counts, especially in versions prior to 3.6.
Key take‑aways :
mongos defaults to a per‑connection‑per‑thread model; version 3.6 adds an optional adaptive thread pool.
Each client‑connection thread consumes ~1 MiB stack; each ASIO worker thread consumes ~8 MiB.
Traditional pools (shardConnectionPool, globalConnPool) are gradually deprecated in favor of the ASIO pool for CRUD operations from 3.6 onward.
Connection‑pool parameters must be tuned to avoid excessive memory usage or connection limits.
The client‑to‑mongos vs. mongos‑to‑mongod relationship is not a fixed 1:1 ratio; it depends on version, workload, and pool configuration.
NetEase Game Operations Platform
The NetEase Game Automated Operations Platform delivers stable services for thousands of NetEase titles, focusing on efficient ops workflows, intelligent monitoring, and virtualization.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.