Databases 21 min read

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.

NetEase Game Operations Platform
NetEase Game Operations Platform
NetEase Game Operations Platform
Deep Dive into mongos Connection Model and Adaptive Thread Pools in MongoDB

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 thread

To 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 destroyed

Adaptive thread‑pool mode is disabled by default and must be enabled via configuration:

# yaml
net:
  serviceExecutor: adaptive
# ini
serviceExecutor = adaptive

In 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.6

Each PoolForHost holds a stack of idle connections and a counter of checked‑out connections:

std::stack
_pool; // idle connections
int _checkedOut; // connections currently in use

Parameters 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 thread

Connection‑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.

mongodbDatabase Operationsconnection poolingadaptive thread poolmongosTaskExecutor
NetEase Game Operations Platform
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.