summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Wlodarek <gregory.wlodarek@mongodb.com>2021-04-28 17:32:43 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-04-29 13:52:08 +0000
commit9b437dbad5f9b570a579ee3feb92206d79033ecc (patch)
treeb9f032d457adbd04b6a7fa12a7c7b8675f89fc3e
parentb649876de8765d7a9d25d6aaec6a86b0d67ca9e2 (diff)
downloadmongo-9b437dbad5f9b570a579ee3feb92206d79033ecc.tar.gz
SERVER-56445 Upsert violates multi-timestamp constraints when inserting into a full capped collection
-rw-r--r--jstests/core/capped_upsert.js19
-rw-r--r--src/mongo/db/SConscript1
-rw-r--r--src/mongo/db/exec/upsert_stage.cpp16
3 files changed, 32 insertions, 4 deletions
diff --git a/jstests/core/capped_upsert.js b/jstests/core/capped_upsert.js
new file mode 100644
index 00000000000..38f5afe3b24
--- /dev/null
+++ b/jstests/core/capped_upsert.js
@@ -0,0 +1,19 @@
+/**
+ * Tests upserting into a capped collection with deletes needed.
+ *
+ * @tags: [
+ * requires_capped,
+ * # Capped collections cannot be sharded
+ * assumes_unsharded_collection,
+ * ]
+ */
+(function() {
+"use strict";
+
+const coll = db.capped_upsert;
+coll.drop();
+
+assert.commandWorked(db.createCollection(coll.getName(), {capped: true, size: 100000, max: 1}));
+assert.commandWorked(coll.insert({text: "a"}));
+assert.commandWorked(coll.save({_id: 123, text: "b"}));
+}());
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index b44f18ffb60..ddebf40045a 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -1300,6 +1300,7 @@ env.Library(
'update/update_driver',
],
LIBDEPS_PRIVATE=[
+ '$BUILD_DIR/mongo/db/repl/local_oplog_info',
'catalog/database_holder',
'commands/server_status_core',
'kill_sessions',
diff --git a/src/mongo/db/exec/upsert_stage.cpp b/src/mongo/db/exec/upsert_stage.cpp
index 4f7403d28d5..8e3dcc0f755 100644
--- a/src/mongo/db/exec/upsert_stage.cpp
+++ b/src/mongo/db/exec/upsert_stage.cpp
@@ -31,6 +31,7 @@
#include "mongo/db/concurrency/write_conflict_exception.h"
#include "mongo/db/curop_failpoint_helpers.h"
+#include "mongo/db/repl/local_oplog_info.h"
#include "mongo/db/s/operation_sharding_state.h"
#include "mongo/db/update/storage_validation.h"
#include "mongo/s/would_change_owning_shard_exception.h"
@@ -148,11 +149,18 @@ void UpsertStage::_performInsert(BSONObj newDocument) {
writeConflictRetry(opCtx(), "upsert", collection()->ns().ns(), [&] {
WriteUnitOfWork wunit(opCtx());
+ InsertStatement insertStmt(_params.request->getStmtIds(), newDocument);
+
+ auto replCoord = repl::ReplicationCoordinator::get(opCtx());
+ if (collection()->isCapped() &&
+ !replCoord->isOplogDisabledFor(opCtx(), collection()->ns())) {
+ auto oplogInfo = repl::LocalOplogInfo::get(opCtx());
+ auto oplogSlots = oplogInfo->getNextOpTimes(opCtx(), /*batchSize=*/1);
+ insertStmt.oplogSlot = oplogSlots.front();
+ }
+
uassertStatusOK(collection()->insertDocument(
- opCtx(),
- InsertStatement(_params.request->getStmtIds(), newDocument),
- _params.opDebug,
- _params.request->isFromMigration()));
+ opCtx(), insertStmt, _params.opDebug, _params.request->isFromMigration()));
// Technically, we should save/restore state here, but since we are going to return
// immediately after, it would just be wasted work.