summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRishab Joshi <rishab.joshi@mongodb.com>2022-05-29 07:50:23 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-05-29 08:19:12 +0000
commitb42b9f28708408fc8fbb03c7b0530148dc9d57dc (patch)
tree4c8968f598be3fe85864dbb137aed2cbec163fbb
parentb154f8ea0332b8829f2c7f46a8ea84c2b86f3a45 (diff)
downloadmongo-b42b9f28708408fc8fbb03c7b0530148dc9d57dc.tar.gz
SERVER-66123 Introduce logic to write to the change collection in the primary.
-rw-r--r--jstests/replsets/write_change_stream_change_collection.js104
-rw-r--r--src/mongo/db/catalog/collection_impl.cpp7
-rw-r--r--src/mongo/db/catalog/validate_state.cpp4
-rw-r--r--src/mongo/db/change_stream_change_collection_manager.cpp100
-rw-r--r--src/mongo/db/change_stream_change_collection_manager.h23
-rw-r--r--src/mongo/db/namespace_string.cpp9
-rw-r--r--src/mongo/db/namespace_string.h7
-rw-r--r--src/mongo/db/repl/SConscript1
-rw-r--r--src/mongo/db/repl/collection_cloner.cpp6
-rw-r--r--src/mongo/db/repl/oplog.cpp7
-rw-r--r--src/mongo/db/repl/replication_coordinator_external_state_impl.cpp7
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp3
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_record_store.h4
13 files changed, 238 insertions, 44 deletions
diff --git a/jstests/replsets/write_change_stream_change_collection.js b/jstests/replsets/write_change_stream_change_collection.js
new file mode 100644
index 00000000000..32fce968ecc
--- /dev/null
+++ b/jstests/replsets/write_change_stream_change_collection.js
@@ -0,0 +1,104 @@
+// Tests that entries are written to the change collection for collection create, drop and document
+// modification operations.
+// @tags: [
+// featureFlagServerlessChangeStreams,
+// multiversion_incompatible,
+// featureFlagMongoStore,
+// ]
+(function() {
+"use strict";
+
+const replSetTest = new ReplSetTest({nodes: 1});
+replSetTest.startSet({setParameter: "multitenancySupport=true"});
+replSetTest.initiate();
+
+const primary = replSetTest.getPrimary();
+const oplogColl = primary.getDB("local").oplog.rs;
+const changeColl = primary.getDB("config").system.change_collection;
+const testDb = primary.getDB("test");
+
+// Verifies that the oplog and change collection entries are the same for the specified start and
+// end duration of the oplog timestamp.
+function verifyChangeCollectionEntries(startOplogTimestamp, endOplogTimestamp) {
+ // Fetch all oplog and change collection entries for the duration: [startOplogTimestamp,
+ // endOplogTimestamp].
+ const oplogEntries =
+ oplogColl.find({$and: [{ts: {$gte: startOplogTimestamp}}, {ts: {$lte: endOplogTimestamp}}]})
+ .toArray();
+ const changeCollectionEntries =
+ changeColl
+ .find({$and: [{_id: {$gte: startOplogTimestamp}}, {_id: {$lte: endOplogTimestamp}}]})
+ .toArray();
+
+ assert.eq(
+ oplogEntries.length,
+ changeCollectionEntries.length,
+ "Number of entries in the oplog and the change collection is not the same. Oplog has total " +
+ oplogEntries.length + " entries , change collection has total " +
+ changeCollectionEntries.length + " entries");
+
+ for (let idx = 0; idx < oplogEntries.length; idx++) {
+ const oplogEntry = oplogEntries[idx];
+ const changeCollectionEntry = changeCollectionEntries[idx];
+
+ // Remove the '_id' field from the change collection as oplog does not have it.
+ assert(changeCollectionEntry.hasOwnProperty("_id"));
+ assert.eq(timestampCmp(changeCollectionEntry._id, oplogEntry.ts),
+ 0,
+ "Change collection '_id' field: " + tojson(changeCollectionEntry._id) +
+ " is not same as the oplog 'ts' field: " + tojson(oplogEntry.ts));
+ delete changeCollectionEntry["_id"];
+
+ // Verify that the oplog and change collecton entry (after removing the '_id') field are
+ // the same.
+ assert.eq(
+ oplogEntry,
+ changeCollectionEntry,
+ "Oplog and change collection entries are not same. Oplog entry: " + tojson(oplogEntry) +
+ ", change collection entry: " + tojson(changeCollectionEntry));
+ }
+}
+
+// Performs writes on the specified collection.
+function performWrites(coll) {
+ const docIds = [1, 2, 3, 4, 5];
+ docIds.forEach(docId => assert.commandWorked(coll.insert({_id: docId})));
+ docIds.forEach(
+ docId => assert.commandWorked(coll.update({_id: docId}, {$set: {annotate: "updated"}})));
+}
+
+// Test the change collection entries with the oplog by performing some basic writes.
+(function testBasicWritesInChangeCollection() {
+ const startOplogTimestamp = oplogColl.find().toArray().at(-1).ts;
+ assert(startOplogTimestamp != undefined);
+
+ performWrites(testDb.stock);
+ assert(testDb.stock.drop());
+
+ const endOplogTimestamp = oplogColl.find().toArray().at(-1).ts;
+ assert(endOplogTimestamp !== undefined);
+ assert(timestampCmp(endOplogTimestamp, startOplogTimestamp) > 0);
+
+ verifyChangeCollectionEntries(startOplogTimestamp, endOplogTimestamp);
+})();
+
+// Test the change collection entries with the oplog by performing writes in a transaction.
+(function testWritesinChangeCollectionWithTrasactions() {
+ const startOplogTimestamp = oplogColl.find().toArray().at(-1).ts;
+ assert(startOplogTimestamp != undefined);
+
+ const session = testDb.getMongo().startSession();
+ const sessionDb = session.getDatabase(testDb.getName());
+ session.startTransaction();
+ performWrites(sessionDb.test);
+ session.commitTransaction_forTesting();
+
+ const endOplogTimestamp = oplogColl.find().toArray().at(-1).ts;
+ assert(endOplogTimestamp != undefined);
+ assert(timestampCmp(endOplogTimestamp, startOplogTimestamp) > 0);
+
+ verifyChangeCollectionEntries(startOplogTimestamp, endOplogTimestamp);
+})();
+
+replSetTest.stopSet();
+}());
diff --git a/src/mongo/db/catalog/collection_impl.cpp b/src/mongo/db/catalog/collection_impl.cpp
index a920e11d85c..ecb86d77238 100644
--- a/src/mongo/db/catalog/collection_impl.cpp
+++ b/src/mongo/db/catalog/collection_impl.cpp
@@ -1046,8 +1046,11 @@ Status CollectionImpl::_insertDocuments(OperationContext* opCtx,
}
}
- opCtx->getServiceContext()->getOpObserver()->onInserts(
- opCtx, ns(), uuid(), begin, end, fromMigrate);
+ // TODO SERVER-66813 fix issue with batch deletion.
+ if (!ns().isChangeCollection()) {
+ opCtx->getServiceContext()->getOpObserver()->onInserts(
+ opCtx, ns(), uuid(), begin, end, fromMigrate);
+ }
_cappedDeleteAsNeeded(opCtx, records.begin()->id);
diff --git a/src/mongo/db/catalog/validate_state.cpp b/src/mongo/db/catalog/validate_state.cpp
index 88d66867751..09b597752cf 100644
--- a/src/mongo/db/catalog/validate_state.cpp
+++ b/src/mongo/db/catalog/validate_state.cpp
@@ -107,11 +107,13 @@ ValidateState::ValidateState(OperationContext* opCtx,
bool ValidateState::shouldEnforceFastCount() const {
if (_mode == ValidateMode::kForegroundFullEnforceFastCount) {
- if (_nss.isOplog()) {
+ if (_nss.isOplog() || _nss.isChangeCollection()) {
// Oplog writers only take a global IX lock, so the oplog can still be written to even
// during full validation despite its collection X lock. This can cause validate to
// incorrectly report an incorrect fast count on the oplog when run in enforceFastCount
// mode.
+ // The oplog entries are also written to the change collections and are prone to fast
+ // count failures.
return false;
} else if (_nss == NamespaceString::kIndexBuildEntryNamespace) {
// Do not enforce fast count on the 'config.system.indexBuilds' collection. This is an
diff --git a/src/mongo/db/change_stream_change_collection_manager.cpp b/src/mongo/db/change_stream_change_collection_manager.cpp
index e27c5af2af2..3311c7be092 100644
--- a/src/mongo/db/change_stream_change_collection_manager.cpp
+++ b/src/mongo/db/change_stream_change_collection_manager.cpp
@@ -37,13 +37,22 @@
#include "mongo/db/catalog/coll_mod.h"
#include "mongo/db/catalog/create_collection.h"
#include "mongo/db/catalog/drop_collection.h"
+#include "mongo/db/catalog_raii.h"
+#include "mongo/db/multitenancy_gen.h"
#include "mongo/db/namespace_string.h"
+#include "mongo/db/repl/oplog.h"
#include "mongo/logv2/log.h"
namespace mongo {
namespace {
const auto getChangeCollectionManager =
ServiceContext::declareDecoration<boost::optional<ChangeStreamChangeCollectionManager>>();
+
+// TODO: SERVER-65950 create or update the change collection for a particular tenant.
+NamespaceString getTenantChangeCollectionNamespace(boost::optional<TenantId> tenantId) {
+ return NamespaceString{NamespaceString::kConfigDb, NamespaceString::kChangeCollectionName};
+}
+
} // namespace
ChangeStreamChangeCollectionManager& ChangeStreamChangeCollectionManager::get(
@@ -60,21 +69,24 @@ void ChangeStreamChangeCollectionManager::create(ServiceContext* service) {
getChangeCollectionManager(service).emplace(service);
}
+bool ChangeStreamChangeCollectionManager::isChangeCollectionEnabled() {
+ return feature_flags::gFeatureFlagServerlessChangeStreams.isEnabled(
+ serverGlobalParams.featureCompatibility) &&
+ gMultitenancySupport;
+}
+
Status ChangeStreamChangeCollectionManager::createChangeCollection(
OperationContext* opCtx, boost::optional<TenantId> tenantId) {
- // TODO: SERVER-65950 create or update the change collection for a particular tenant.
- const NamespaceString nss{NamespaceString::kConfigDb,
- NamespaceString::kChangeStreamChangeCollection};
-
// Make the change collection clustered by '_id'. The '_id' field will have the same value as
// the 'ts' field of the oplog.
CollectionOptions changeCollectionOptions;
changeCollectionOptions.clusteredIndex.emplace(clustered_util::makeDefaultClusteredIdIndex());
changeCollectionOptions.capped = true;
- auto status = createCollection(opCtx, nss, changeCollectionOptions, BSONObj());
+ auto status = createCollection(
+ opCtx, getTenantChangeCollectionNamespace(tenantId), changeCollectionOptions, BSONObj());
if (status.code() == ErrorCodes::NamespaceExists) {
- return Status(ErrorCodes::Error::OK, "");
+ return Status::OK();
}
return status;
@@ -82,21 +94,75 @@ Status ChangeStreamChangeCollectionManager::createChangeCollection(
Status ChangeStreamChangeCollectionManager::dropChangeCollection(
OperationContext* opCtx, boost::optional<TenantId> tenantId) {
- // TODO: SERVER-65950 remove the change collection for a particular tenant.
- const NamespaceString nss{NamespaceString::kConfigDb,
- NamespaceString::kChangeStreamChangeCollection};
DropReply dropReply;
- return dropCollection(
- opCtx, nss, &dropReply, DropCollectionSystemCollectionMode::kAllowSystemCollectionDrops);
+ return dropCollection(opCtx,
+ getTenantChangeCollectionNamespace(tenantId),
+ &dropReply,
+ DropCollectionSystemCollectionMode::kAllowSystemCollectionDrops);
}
-Status ChangeStreamChangeCollectionManager::insertDocumentsToChangeCollection(
+void ChangeStreamChangeCollectionManager::insertDocumentsToChangeCollection(
OperationContext* opCtx,
- boost::optional<TenantId> tenantId,
- std::vector<Record>* records,
- const std::vector<Timestamp>& timestamps) {
- // TODO SERVER-65210 add code to insert to the change collection in the primaries.
- return Status(ErrorCodes::OK, "");
+ const std::vector<Record>& oplogRecords,
+ const std::vector<Timestamp>& oplogTimestamps) {
+ invariant(oplogRecords.size() == oplogTimestamps.size());
+
+ // This method must be called within a 'WriteUnitOfWork'. The caller must be responsible for
+ // commiting the unit of work.
+ invariant(opCtx->lockState()->inAWriteUnitOfWork());
+
+ // Maps statements that should be inserted to the change collection for each tenant.
+ stdx::unordered_map<TenantId, std::vector<InsertStatement>, TenantId::Hasher>
+ tenantToInsertStatements;
+
+ for (size_t idx = 0; idx < oplogRecords.size(); idx++) {
+ auto& record = oplogRecords[idx];
+ auto& ts = oplogTimestamps[idx];
+
+ // Create a mutable document and update the '_id' field with the oplog entry timestamp. The
+ // '_id' field will be use to order the change collection documents.
+ Document oplogDoc(record.data.toBson());
+ MutableDocument changeCollDoc(oplogDoc);
+ changeCollDoc["_id"] = Value(ts);
+
+ // Create an insert statement that should be written at the timestamp 'ts' for a particular
+ // tenant.
+ auto readyChangeCollDoc = changeCollDoc.freeze();
+ tenantToInsertStatements[TenantId::kSystemTenantId].push_back(
+ InsertStatement{readyChangeCollDoc.toBson(), ts, repl::OpTime::kUninitializedTerm});
+ }
+
+ for (auto&& [tenantId, insertStatements] : tenantToInsertStatements) {
+ // TODO SERVER-66715 avoid taking 'AutoGetCollection' and remove
+ // 'AllowLockAcquisitionOnTimestampedUnitOfWork'.
+ AllowLockAcquisitionOnTimestampedUnitOfWork allowLockAcquisition(opCtx->lockState());
+ AutoGetCollection tenantChangeCollection(
+ opCtx, getTenantChangeCollectionNamespace(tenantId), LockMode::MODE_IX);
+
+ // The change collection does not exist for a particular tenant because either the change
+ // collection is not enabled or is in the process of enablement. Ignore this insert for now.
+ // TODO: SERVER-65950 move this check before inserting to the map
+ // 'tenantToInsertStatements'.
+ if (!tenantChangeCollection) {
+ continue;
+ }
+
+ // Writes to the change collection should not be replicated.
+ repl::UnreplicatedWritesBlock unReplBlock(opCtx);
+
+ Status status = tenantChangeCollection->insertDocuments(opCtx,
+ insertStatements.begin(),
+ insertStatements.end(),
+ nullptr /* opDebug */,
+ false /* fromMigrate */);
+ if (!status.isOK()) {
+ LOGV2_FATAL(6612300,
+ "Write to change collection: {ns} failed: {error}",
+ "Write to change collection failed",
+ "ns"_attr = tenantChangeCollection->ns().toString(),
+ "error"_attr = status.toString());
+ }
+ }
}
} // namespace mongo
diff --git a/src/mongo/db/change_stream_change_collection_manager.h b/src/mongo/db/change_stream_change_collection_manager.h
index b4ad0e25c50..8ecc48b9a5c 100644
--- a/src/mongo/db/change_stream_change_collection_manager.h
+++ b/src/mongo/db/change_stream_change_collection_manager.h
@@ -60,6 +60,12 @@ public:
static ChangeStreamChangeCollectionManager& get(OperationContext* opCtx);
/**
+ * Returns true if change collections are enabled for recording oplog entries, false
+ * otherwise.
+ */
+ static bool isChangeCollectionEnabled();
+
+ /**
* Creates a change collection for the specified tenant, if it doesn't exist. Returns Status::OK
* if the change collection already exists.
*
@@ -75,16 +81,21 @@ public:
Status dropChangeCollection(OperationContext* opCtx, boost::optional<TenantId> tenantId);
/**
- * Inserts documents to the change collection for the specified tenant. The parameter 'records'
- * is a vector of oplog records and the parameter 'timestamps' is a vector for respective
+ * Inserts documents to change collections. The parameter 'oplogRecords'
+ * is a vector of oplog records and the parameter 'oplogTimestamps' is a vector for respective
* timestamp for each oplog record.
*
+ * The method fetches the tenant-id from the oplog entry, performs necessary modification to the
+ * document and then write to the tenant's change collection at the specified oplog timestamp.
+ *
+ * Failure in insertion to any change collection will result in a fatal exception and will bring
+ * down the node.
+ *
* TODO: SERVER-65950 make tenantId field mandatory.
*/
- Status insertDocumentsToChangeCollection(OperationContext* opCtx,
- boost::optional<TenantId> tenantId,
- std::vector<Record>* records,
- const std::vector<Timestamp>& timestamps);
+ void insertDocumentsToChangeCollection(OperationContext* opCtx,
+ const std::vector<Record>& oplogRecords,
+ const std::vector<Timestamp>& oplogTimestamps);
};
} // namespace mongo
diff --git a/src/mongo/db/namespace_string.cpp b/src/mongo/db/namespace_string.cpp
index 525dd9c6724..d16370da310 100644
--- a/src/mongo/db/namespace_string.cpp
+++ b/src/mongo/db/namespace_string.cpp
@@ -243,7 +243,7 @@ bool NamespaceString::isLegalClientSystemNS(
return true;
}
- if (isChangeStreamChangeCollection()) {
+ if (isChangeCollection()) {
return true;
}
@@ -404,8 +404,8 @@ bool NamespaceString::isChangeStreamPreImagesCollection() const {
return ns() == kChangeStreamPreImagesNamespace.ns();
}
-bool NamespaceString::isChangeStreamChangeCollection() const {
- return db() == kConfigDb && coll() == kChangeStreamChangeCollection;
+bool NamespaceString::isChangeCollection() const {
+ return db() == kConfigDb && coll() == kChangeCollectionName;
}
bool NamespaceString::isConfigImagesCollection() const {
@@ -432,8 +432,7 @@ NamespaceString NamespaceString::getTimeseriesViewNamespace() const {
}
bool NamespaceString::isImplicitlyReplicated() const {
- if (isChangeStreamPreImagesCollection() || isConfigImagesCollection() || isChangeCollection() ||
- isChangeStreamChangeCollection()) {
+ if (isChangeStreamPreImagesCollection() || isConfigImagesCollection() || isChangeCollection()) {
// Implicitly replicated namespaces are replicated, although they only replicate a subset of
// writes.
invariant(isReplicated());
diff --git a/src/mongo/db/namespace_string.h b/src/mongo/db/namespace_string.h
index 687fb431bb1..dd6842974f0 100644
--- a/src/mongo/db/namespace_string.h
+++ b/src/mongo/db/namespace_string.h
@@ -77,7 +77,7 @@ public:
static constexpr StringData kSystemDotViewsCollectionName = "system.views"_sd;
// Name for the change stream change collection.
- static constexpr StringData kChangeStreamChangeCollection = "system.change_collection"_sd;
+ static constexpr StringData kChangeCollectionName = "system.change_collection"_sd;
// Names of privilege document collections
static constexpr StringData kSystemUsers = "system.users"_sd;
@@ -384,9 +384,6 @@ public:
bool isSystemDotProfile() const {
return coll() == "system.profile";
}
- bool isChangeCollection() const {
- return (db() == kConfigDb) && coll().startsWith("changes.");
- }
bool isSystemDotViews() const {
return coll() == kSystemDotViewsCollectionName;
}
@@ -461,7 +458,7 @@ public:
/**
* Returns whether the specified namespace is config.system.changeCollection.
*/
- bool isChangeStreamChangeCollection() const;
+ bool isChangeCollection() const;
/**
* Returns whether the specified namespace is config.image_collection.
diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript
index 3379a0d34d1..e9bcecfbdbf 100644
--- a/src/mongo/db/repl/SConscript
+++ b/src/mongo/db/repl/SConscript
@@ -73,6 +73,7 @@ env.Library(
'$BUILD_DIR/mongo/db/catalog/index_build_oplog_entry',
'$BUILD_DIR/mongo/db/catalog/local_oplog_info',
'$BUILD_DIR/mongo/db/catalog/multi_index_block',
+ '$BUILD_DIR/mongo/db/change_stream_change_collection_manager',
'$BUILD_DIR/mongo/db/commands/feature_compatibility_parsers',
'$BUILD_DIR/mongo/db/concurrency/exception_util',
'$BUILD_DIR/mongo/db/db_raii',
diff --git a/src/mongo/db/repl/collection_cloner.cpp b/src/mongo/db/repl/collection_cloner.cpp
index 8800297cb4d..bde00eef906 100644
--- a/src/mongo/db/repl/collection_cloner.cpp
+++ b/src/mongo/db/repl/collection_cloner.cpp
@@ -108,9 +108,9 @@ CollectionCloner::CollectionCloner(const NamespaceString& sourceNss,
}
BaseCloner::ClonerStages CollectionCloner::getStages() {
- if (_sourceNss.isChangeStreamPreImagesCollection()) {
- // Only the change stream pre-images collection needs to be created - its documents should
- // not be copied.
+ if (_sourceNss.isChangeStreamPreImagesCollection() || _sourceNss.isChangeCollection()) {
+ // Only the change stream pre-images collection and change collection needs to be created -
+ // its documents should not be copied.
return {&_listIndexesStage,
&_createCollectionStage,
&_setupIndexBuildersForUnfinishedIndexesStage};
diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp
index 7f521591c0c..0908b06213a 100644
--- a/src/mongo/db/repl/oplog.cpp
+++ b/src/mongo/db/repl/oplog.cpp
@@ -58,6 +58,7 @@
#include "mongo/db/catalog/local_oplog_info.h"
#include "mongo/db/catalog/multi_index_block.h"
#include "mongo/db/catalog/rename_collection.h"
+#include "mongo/db/change_stream_change_collection_manager.h"
#include "mongo/db/client.h"
#include "mongo/db/coll_mod_gen.h"
#include "mongo/db/commands.h"
@@ -387,6 +388,12 @@ void _logOpsInner(OperationContext* opCtx,
"error"_attr = result.toString());
}
+ // Insert the oplog records to the respective tenants change collections.
+ if (ChangeStreamChangeCollectionManager::isChangeCollectionEnabled()) {
+ ChangeStreamChangeCollectionManager::get(opCtx).insertDocumentsToChangeCollection(
+ opCtx, *records, timestamps);
+ }
+
// Set replCoord last optime only after we're sure the WUOW didn't abort and roll back.
opCtx->recoveryUnit()->onCommit(
[opCtx, replCoord, finalOpTime, wallTime](boost::optional<Timestamp> commitTime) {
diff --git a/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp b/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp
index 7ffa018a666..2266dfcfa38 100644
--- a/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp
+++ b/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp
@@ -560,10 +560,9 @@ OpTime ReplicationCoordinatorExternalStateImpl::onTransitionToPrimary(OperationC
// TODO: SERVER-65948 move the change collection creation logic from here to the PM-2502 hooks.
// The change collection will be created when the change stream is enabled.
- if (::mongo::feature_flags::gFeatureFlagServerlessChangeStreams.isEnabled(
- serverGlobalParams.featureCompatibility)) {
- auto& changeCollectionManager = ChangeStreamChangeCollectionManager::get(opCtx);
- auto status = changeCollectionManager.createChangeCollection(opCtx, boost::none);
+ if (ChangeStreamChangeCollectionManager::isChangeCollectionEnabled()) {
+ auto status = ChangeStreamChangeCollectionManager::get(opCtx).createChangeCollection(
+ opCtx, boost::none);
if (!status.isOK()) {
fassert(6520900, status);
}
diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp
index 337ee7e76b2..126f2631211 100644
--- a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp
+++ b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp
@@ -894,6 +894,7 @@ WiredTigerRecordStore::WiredTigerRecordStore(WiredTigerKVEngine* kvEngine,
_isEphemeral(params.isEphemeral),
_isLogged(params.isLogged),
_isOplog(params.nss.isOplog()),
+ _isChangeCollection(params.nss.isChangeCollection()),
_forceUpdateWithFullDocument(params.forceUpdateWithFullDocument),
_oplogMaxSize(params.oplogMaxSize),
_cappedCallback(params.cappedCallback),
@@ -1416,7 +1417,7 @@ Status WiredTigerRecordStore::_insertRecords(OperationContext* opCtx,
// Increment metrics for each insert separately, as opposed to outside of the loop. The API
// requires that each record be accounted for separately.
- if (!_isOplog) {
+ if (!_isOplog && !_isChangeCollection) {
auto& metricsCollector = ResourceConsumption::MetricsCollector::get(opCtx);
auto keyLength = computeRecordIdSize(record.id);
diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.h b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.h
index ff210ba87a2..3bb07432f71 100644
--- a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.h
+++ b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.h
@@ -340,6 +340,10 @@ private:
const bool _isLogged;
// True if the namespace of this record store starts with "local.oplog.", and false otherwise.
const bool _isOplog;
+ // True if the namespace of this record store starts with "config.system.change_collection", and
+ // false otherwise.
+ const bool _isChangeCollection;
+
// TODO (SERVER-57482): Remove special handling of skipping "wiredtiger_calc_modify()".
// True if force to update with the full document, and false otherwise.
const bool _forceUpdateWithFullDocument;