All Products
Search
Document Center

ApsaraDB for MongoDB:Opcounters and Repl Opcounters metrics

Last Updated:Mar 28, 2026

ApsaraDB for MongoDB exposes two operation counter metrics — Opcounters and Repl Opcounters — that help you understand database workload and diagnose performance anomalies. For the instance architectures that support these metrics, see Monitoring items and metrics.

MetricWhere to find itWhat to use it for
OpcountersMonitoring Data module (QPS metric) and Performance moduleIdentify which operation type is driving high load on the primary node
Repl OpcountersMonitoring Data moduleVerify replication health and identify unexpected write amplification on secondary nodes

Opcounters

Opcounters counts insert, query, update, delete, getMore, and command operations performed on a mongod or mongos since the last startup. The count includes both client-initiated and internal operations.

The metric appears in two places in the ApsaraDB for MongoDB console:

Tracked operations

OperationUnitDescription
insertCount/secondInsert operations per second
queryCount/secondQuery operations per second
updateCount/secondUpdate operations per second
deleteCount/secondDelete operations per second
getmoreCount/secondgetMore operations per second. This value can be high even when query count is low, because secondary nodes send getMore operations against local.oplog.rs during replication.
commandCount/secondCommand operations per second. Counts all commands except insert, update, delete, query, and getmore.
Multi-document operations — such as updateMany affecting four documents — count as a single operation in Opcounters. If you need document-level granularity, see the FAQ below.

Internal operations

Beyond client traffic, Opcounters also records internal operations. The table below explains what triggers each counter in the absence of client activity.

OperationInternal behavior
insert(1) During chunk migration, the primary node acting as the destination shard records these insert operations. (2) Replica set nodes flush in-memory sessions to config.system.sessions every 5 minutes, which generates insert operations. To change the flush interval, adjust the logicalSessionRefreshMillis parameter.
query(1) When Mirrored Reads is enabled, query operations are mirrored to secondary nodes. The feature is enabled by default for instances running MongoDB versions later than 4.4. For details, see Mirrored Reads. (2) Setting fullDocument: "updateLookup" on a Change Stream triggers additional query operations.
updateSession flushes (same mechanism as insert) also generate update operations.
deleteTTL index deletions are not recorded. Orphaned document deletions after chunk migration are not recorded.
getmorePrimary/secondary synchronization generates getMore operations against local.oplog.rs. This is the primary reason getmore values can be high even when client query count is low.
commandRecorded commands include: isMaster and hello (internal health checks); replSetUpdatePosition (replication sync); and monitoring commands such as serverStatus, listCollections, collStats, and replSetGetStatus. For the full command reference, see Commands.

Repl Opcounters

Repl Opcounters counts database replication operations by type since mongod was last started. It is available in the Monitoring Data module of the ApsaraDB for MongoDB console. For more information, see Monitoring information.

Repl Opcounters-cn.png

Focus on secondary node values when monitoring Repl Opcounters. The metric captures:

  • Client query operations routed to secondary nodes via readPreference

  • All write operations applied to the local database by primary/secondary replication

Use Repl Opcounters to verify replication health and identify unexpected write amplification on secondary nodes.

Tracked operations

OperationUnitDescription
insertCount/secondReplicated insert operations applied per second on secondary nodes
queryCount/secondQuery operations routed to secondary nodes per second
updateCount/secondReplicated update operations applied per second on secondary nodes
deleteCount/secondReplicated delete operations applied per second on secondary nodes
getmoreCount/secondgetMore operations on secondary nodes per second
commandCount/secondReplicated command operations per second

Additional operations tracked vs. Opcounters

Repl Opcounters records several operations that Opcounters does not:

  • Insert and update operations triggered by session flushes

  • Delete operations caused by TTL indexes

  • Orphaned document deletions (typically with a delay after chunk migration)

  • Writes to system collections, such as retryable writes to config.transactions. For details, see Retryable Writes.

ApsaraDB for MongoDB serializes data in different ways during primary/secondary replication. Therefore, the value of the Repl Opcounters metric for a secondary node in an instance is different from that for the primary node in the instance.

FAQ

Why is the Repl Opcounters value on a secondary node much higher than the Opcounters value on the primary node?

Multi-document operations are recorded as a single operation in Opcounters on the primary node, but replication applies each document change individually. A single updateMany that modifies four documents increments opcounters.update by 1 on the primary, but increments opcountersRepl.update by 4 on the secondary.

Example — run updateMany that modifies four documents:

  1. Before the update, check the counters:

    > db.serverStatus().opcounters.update
    NumberLong(13)
    
    > db.serverStatus().opcountersRepl.update
    NumberLong(11)
  2. Run the batch update:

    > db.coll.updateMany({x:2},{$set:{x:3}})
    { "acknowledged" : true, "matchedCount" : 4, "modifiedCount" : 4 }
  3. Check the counters again:

    > db.serverStatus().opcounters.update
    NumberLong(14)   // +1 (one operation)
    
    > db.serverStatus().opcountersRepl.update
    NumberLong(15)   // +4 (four documents replicated individually)

Why does Repl Opcounters show many insert operations when I only run update operations?

This happens when {upsert: true} is used and the target document does not exist. MongoDB converts the failed update into an insert, records it as an insert in the oplog, and replicates it to secondary nodes as an insert — so opcountersRepl.insert increments instead of opcountersRepl.update.

Example — run updateOne with {upsert: true} on a non-existent document:

  1. Before the update:

    > db.serverStatus().opcounters
    { "insert": NumberLong(1516), "update": NumberLong(33), ... }
    
    > db.serverStatus().opcountersRepl
    { "insert": NumberLong(1539), "update": NumberLong(24), ... }
  2. Run the upsert:

    > db.coll.updateOne({x:"a"}, {$set:{x:"b"}}, {upsert:true})
    { "acknowledged": true, "matchedCount": 0, "modifiedCount": 0, "upsertedId": ObjectId("64bf72b829907f52b4b363ea") }
  3. After the update — the primary records an update (+1), the secondary records an insert (+1):

    > db.serverStatus().opcounters
    { "insert": NumberLong(1516), "update": NumberLong(34), ... }
    
    > db.serverStatus().opcountersRepl
    { "insert": NumberLong(1540), "update": NumberLong(24), ... }

Why is the update count on the primary much higher than the replicated update count on secondary nodes?

No-op updates — where the new value matches the existing value — are recorded in Opcounters on the primary but are not written to the oplog and therefore not replicated.

Example — run updateMany that matches no documents:

  1. Before the update: primary update = 34, secondary update = 24.

  2. Run an update with no matching documents:

    > db.coll.updateMany({x:"ab"},{$set:{x:"cd"}})
    { "acknowledged": true, "matchedCount": 0, "modifiedCount": 0 }
  3. After the update: primary update = 35 (+1), secondary update stays at 24.

    > db.serverStatus().opcounters
    { "update": NumberLong(35), ... }   // +1
    
    > db.serverStatus().opcountersRepl
    { "update": NumberLong(24), ... }   // unchanged

Why do I see operations in Opcounters when no client traffic is active?

ApsaraDB for MongoDB generates background operations continuously, even with no client activity:

  • Internal maintenance: replica set heartbeats, primary/secondary synchronization, and session flushes

  • Management operations: detection, monitoring, and health checks from the control plane

Why do Opcounters values differ from counts derived from the oplog op field?

In the oplog, the op field uses these values:

kCommand: "c"
kInsert:  "i"
kUpdate:  "u"
kDelete:  "d"
kNoop:    "n"

All operations inside a transaction are recorded with op: "c". The individual insert, update, and delete operations are stored in o.applyOps, not as top-level oplog entries. Aggregating by op alone misses transactional writes.

To count a specific operation type within transactions, connect to the instance using the mongo shell and run:

use local
db.oplog.rs.aggregate([
  {$match: {
    "op": "c",
    "ts": {"$gte": Timestamp(1733849400, 0)},
    "o.applyOps": {$exists: true},
    "o.applyOps.0.op": "u"
  }},
  {$count: "count"}
])

Parameters:

ParameterDescription
Timestamp(1733849400, 0)Lower boundary for the ts field. Replace with your target query time as a UNIX timestamp or a value read from local.oplog.rs.
"o.applyOps.0.op": "u"The operation type of the first entry in the transaction oplog. Replace "u" with "i" for insert or "d" for delete.
{$count: "count"}Returns the number of matching oplog entries. Replace with other aggregate operators for further analysis.

Example — two insertOne operations inside a transaction increment opcounters.insert from 4 to 6:

  1. Before the transaction:

    > db.serverStatus().opcounters
    { "insert": NumberLong(4), "query": NumberLong(6723), "update": NumberLong(110489), ... }
  2. Run the transaction:

    session = db.getMongo().startSession({ readPreference: { mode: "primary" } });
    coll1 = session.getDatabase("mydb1").foo;
    coll2 = session.getDatabase("mydb2").bar;
    session.startTransaction({ readConcern: { level: "local" }, writeConcern: { w: "majority" } });
    try {
      coll1.insertOne({ abc: 1 });
      coll2.insertOne({ xyz: 999 });
    } catch (error) {
      session.abortTransaction();
      throw error;
    }
    session.commitTransaction();
    session.endSession();
  3. After the transaction — insert count is 6 (+2):

    > db.serverStatus().opcounters
    { "insert": NumberLong(6), ... }

To view transaction metrics directly in the console, see the Transaction operations metric in the Monitoring Data module. For more information, see Monitoring items and metrics.