+++ b/jstests/replsets/stable_timestamp_oplog_hole.js
|
@@ -0,0 +1,68 @@
|
+/**
|
+ * Test that stable timestamp can advance after a oplog hole is released via an abort.
|
+ *
|
+ * @tags: [requires_persistence, uses_transactions]
|
+ */
|
+
|
+(function() {
|
+"use strict";
|
+load("jstests/libs/check_log.js");
|
+load("jstests/core/txns/libs/prepare_helpers.js");
|
+
|
+const replTest = new ReplSetTest({nodes: 1});
|
+replTest.startSet();
|
+replTest.initiate();
|
+
|
+let primary = replTest.getPrimary();
|
+
|
+const dbName = "test";
|
+const collName = "stable_timestamp_oplog_hole";
|
+let testDB = primary.getDB(dbName);
|
+const testColl = testDB.getCollection(collName);
|
+
|
+assert.commandWorked(testColl.insert({_id: 1}));
|
+
|
+assert.commandWorked(testDB.adminCommand(
|
+ {configureFailPoint: 'hangAndFailUnpreparedCommitAfterReservingOplogSlot', mode: 'alwaysOn'}));
|
+
|
+// Run a transaction in a parallel shell.
|
+function transactionFn() {
|
+ const name = 'stable_timestamp_oplog_hole';
|
+ const dbName = 'test';
|
+ const collName = name;
|
+ const session = db.getMongo().startSession();
|
+ const sessionDB = session.getDatabase(dbName);
|
+
|
+ session.startTransaction();
|
+ sessionDB[collName].update({_id: 1}, {_id: 1, a: 1});
|
+ assert.commandFailedWithCode(session.commitTransaction_forTesting(), 51260);
|
+}
|
+const joinTransaction = startParallelShell(transactionFn, replTest.ports[0]);
|
+
|
+jsTestLog("Waiting to hang unprepared commit after reserving oplog slot.");
|
+checkLog.contains(primary, "hangAndFailUnpreparedCommitAfterReservingOplogSlot fail point enabled");
|
+
|
+// Run a write with {w: "majority"} in a parallel shell.
|
+function majorityWriteFn() {
|
+ const dbName = 'test';
|
+ const collName = 'stable_timestamp_oplog_hole';
|
+ const testDB = db.getMongo().getDB(dbName);
|
+ const testColl = testDB.getCollection(collName);
|
+
|
+ assert.commandWorked(testColl.insert({_id: 2}, {writeConcern: {w: "majority", wtimeout: 20000}}));
|
+}
|
+const joinMajorityWrite = startParallelShell(majorityWriteFn, replTest.ports[0]);
|
+
|
+jsTestLog("Waiting for majority write to advance lastApplied.");
|
+sleep(5 * 1000);
|
+assert.commandWorked(testDB.adminCommand(
|
+ {configureFailPoint: 'hangAndFailUnpreparedCommitAfterReservingOplogSlot', mode: 'off'}));
|
+
|
+jsTestLog("Joining the transaction.");
|
+joinTransaction();
|
+
|
+jsTestLog("Joining the majority write.");
|
+joinMajorityWrite();
|
+
|
+replTest.stopSet();
|
+}());
|
diff --git a/src/mongo/db/op_observer_impl.cpp b/src/mongo/db/op_observer_impl.cpp
|
index 73d31fd799..e4270bd907 100644
|
--- a/src/mongo/db/op_observer_impl.cpp
|
+++ b/src/mongo/db/op_observer_impl.cpp
|
@@ -27,6 +27,8 @@
|
* it in the license file.
|
*/
|
|
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kReplication
|
+
|
#include "mongo/platform/basic.h"
|
|
#include "mongo/db/op_observer_impl.h"
|
@@ -62,6 +64,7 @@
|
#include "mongo/scripting/engine.h"
|
#include "mongo/util/assert_util.h"
|
#include "mongo/util/fail_point.h"
|
+#include "mongo/util/log.h"
|
|
namespace mongo {
|
using repl::MutableOplogEntry;
|
@@ -70,6 +73,7 @@ using repl::OplogEntry;
|
namespace {
|
|
MONGO_FAIL_POINT_DEFINE(failCollectionUpdates);
|
+MONGO_FAIL_POINT_DEFINE(hangAndFailUnpreparedCommitAfterReservingOplogSlot);
|
|
const auto documentKeyDecoration = OperationContext::declareDecoration<BSONObj>();
|
|
@@ -1009,6 +1013,14 @@ void OpObserverImpl::onUnpreparedTransactionCommit(
|
auto oplogSlots = repl::getNextOpTimes(opCtx, statements.size());
|
invariant(oplogSlots.size() == statements.size());
|
|
+ if (MONGO_unlikely(hangAndFailUnpreparedCommitAfterReservingOplogSlot.shouldFail())) {
|
+ log() << "hangAndFailUnpreparedCommitAfterReservingOplogSlot fail point enabled.";
|
+ hangAndFailUnpreparedCommitAfterReservingOplogSlot.pauseWhileSet(opCtx);
|
+ uasserted(51260,
|
+ str::stream()
|
+ << "hangAndFailUnpreparedCommitAfterReservingOplogSlot fail point enabled");
|
+ }
|
+
|
// Log in-progress entries for the transaction along with the implicit commit.
|
int numOplogEntries = logOplogEntriesForTransaction(opCtx, statements, oplogSlots, false);
|
commitOpTime = oplogSlots[numOplogEntries - 1];
|