summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/catalog/apply_ops.cpp2
-rw-r--r--src/mongo/db/repl/master_slave.cpp4
-rw-r--r--src/mongo/db/repl/multiapplier.h2
-rw-r--r--src/mongo/db/repl/oplog.cpp16
-rw-r--r--src/mongo/db/repl/oplog.h8
-rw-r--r--src/mongo/db/repl/sync_tail.cpp39
-rw-r--r--src/mongo/db/repl/sync_tail.h16
-rw-r--r--src/mongo/db/repl/sync_tail_test.cpp115
8 files changed, 120 insertions, 82 deletions
diff --git a/src/mongo/db/catalog/apply_ops.cpp b/src/mongo/db/catalog/apply_ops.cpp
index 5b783fff7f4..0072d2ef0b0 100644
--- a/src/mongo/db/catalog/apply_ops.cpp
+++ b/src/mongo/db/catalog/apply_ops.cpp
@@ -158,7 +158,7 @@ Status _applyOps(OperationContext* txn,
MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN {
if (*opType == 'c') {
- status = repl::applyCommand_inlock(txn, opObj);
+ status = repl::applyCommand_inlock(txn, opObj, true);
} else {
OldClientContext ctx(txn, ns);
diff --git a/src/mongo/db/repl/master_slave.cpp b/src/mongo/db/repl/master_slave.cpp
index 353e81f2e60..c78dbba2c5d 100644
--- a/src/mongo/db/repl/master_slave.cpp
+++ b/src/mongo/db/repl/master_slave.cpp
@@ -640,14 +640,14 @@ bool ReplSource::handleDuplicateDbName(OperationContext* txn,
void ReplSource::applyCommand(OperationContext* txn, const BSONObj& op) {
try {
- Status status = applyCommand_inlock(txn, op);
+ Status status = applyCommand_inlock(txn, op, true);
if (!status.isOK()) {
SyncTail sync(nullptr, SyncTail::MultiSyncApplyFunc());
sync.setHostname(hostName);
if (sync.shouldRetry(txn, op)) {
uassert(28639,
"Failure retrying initial sync update",
- applyCommand_inlock(txn, op).isOK());
+ applyCommand_inlock(txn, op, true).isOK());
}
}
} catch (UserException& e) {
diff --git a/src/mongo/db/repl/multiapplier.h b/src/mongo/db/repl/multiapplier.h
index 66e9d150b60..3b994a00abd 100644
--- a/src/mongo/db/repl/multiapplier.h
+++ b/src/mongo/db/repl/multiapplier.h
@@ -67,7 +67,7 @@ public:
/**
* Type of function to to apply a single operation. In production, this function
- * would have the same outcome as calling SyncTail::syncApply() ('convertUpdatesToUpserts'
+ * would have the same outcome as calling SyncTail::syncApply() ('inSteadyStateReplication'
* value will be embedded in the function implementation).
*/
using ApplyOperationFn = stdx::function<Status(OperationPtrs*)>;
diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp
index bac3bc31e95..e2985ef6c91 100644
--- a/src/mongo/db/repl/oplog.cpp
+++ b/src/mongo/db/repl/oplog.cpp
@@ -654,7 +654,7 @@ std::map<std::string, ApplyOpMetadata> opsMap = {
Status applyOperation_inlock(OperationContext* txn,
Database* db,
const BSONObj& op,
- bool convertUpdateToUpsert,
+ bool inSteadyStateReplication,
IncrementOpsAppliedStatsFn incrementOpsAppliedStats) {
LOG(3) << "applying op: " << redact(op);
@@ -870,7 +870,7 @@ Status applyOperation_inlock(OperationContext* txn,
opCounters->gotUpdate();
BSONObj updateCriteria = o2;
- const bool upsert = valueB || convertUpdateToUpsert;
+ const bool upsert = valueB || inSteadyStateReplication;
uassert(ErrorCodes::NoSuchKey,
str::stream() << "Failed to apply update due to missing _id: " << op.toString(),
@@ -965,7 +965,9 @@ Status applyOperation_inlock(OperationContext* txn,
return Status::OK();
}
-Status applyCommand_inlock(OperationContext* txn, const BSONObj& op) {
+Status applyCommand_inlock(OperationContext* txn,
+ const BSONObj& op,
+ bool inSteadyStateReplication) {
const char* names[] = {"o", "ns", "op"};
BSONElement fields[3];
op.getFields(3, names, fields);
@@ -998,6 +1000,14 @@ Status applyCommand_inlock(OperationContext* txn, const BSONObj& op) {
}
}
+ // Applying renameCollection during initial sync might lead to data corruption, so we restart
+ // the initial sync.
+ if (!inSteadyStateReplication && o.firstElementFieldName() == std::string("renameCollection")) {
+ return Status(ErrorCodes::OplogOperationUnsupported,
+ str::stream() << "Applying renameCollection not supported in initial sync: "
+ << redact(op));
+ }
+
// Applying commands in repl is done under Global W-lock, so it is safe to not
// perform the current DB checks after reacquiring the lock.
invariant(txn->lockState()->isW());
diff --git a/src/mongo/db/repl/oplog.h b/src/mongo/db/repl/oplog.h
index 6266cf6babb..b2078e93d28 100644
--- a/src/mongo/db/repl/oplog.h
+++ b/src/mongo/db/repl/oplog.h
@@ -106,22 +106,24 @@ using IncrementOpsAppliedStatsFn = stdx::function<void()>;
/**
* Take a non-command op and apply it locally
* Used for applying from an oplog
- * @param convertUpdateToUpsert convert some updates to upserts for idempotency reasons
+ * @param inSteadyStateReplication convert some updates to upserts for idempotency reasons
* @param incrementOpsAppliedStats is called whenever an op is applied.
* Returns failure status if the op was an update that could not be applied.
*/
Status applyOperation_inlock(OperationContext* txn,
Database* db,
const BSONObj& op,
- bool convertUpdateToUpsert = false,
+ bool inSteadyStateReplication = false,
IncrementOpsAppliedStatsFn incrementOpsAppliedStats = {});
/**
* Take a command op and apply it locally
* Used for applying from an oplog
+ * inSteadyStateReplication indicates whether we are in steady state replication, rather than
+ * initial sync.
* Returns failure status if the op that could not be applied.
*/
-Status applyCommand_inlock(OperationContext* txn, const BSONObj& op);
+Status applyCommand_inlock(OperationContext* txn, const BSONObj& op, bool inSteadyStateReplication);
/**
* Initializes the global Timestamp with the value from the timestamp of the last oplog entry.
diff --git a/src/mongo/db/repl/sync_tail.cpp b/src/mongo/db/repl/sync_tail.cpp
index 81ffc65518b..560584c415c 100644
--- a/src/mongo/db/repl/sync_tail.cpp
+++ b/src/mongo/db/repl/sync_tail.cpp
@@ -284,7 +284,7 @@ bool SyncTail::peek(OperationContext* txn, BSONObj* op) {
// static
Status SyncTail::syncApply(OperationContext* txn,
const BSONObj& op,
- bool convertUpdateToUpsert,
+ bool inSteadyStateReplication,
ApplyOperationInLockFn applyOperationInLock,
ApplyCommandInLockFn applyCommandInLock,
IncrementOpsAppliedStatsFn incrementOpsAppliedStats) {
@@ -316,7 +316,7 @@ Status SyncTail::syncApply(OperationContext* txn,
Lock::GlobalWrite globalWriteLock(txn->lockState());
// special case apply for commands to avoid implicit database creation
- Status status = applyCommandInLock(txn, op);
+ Status status = applyCommandInLock(txn, op, inSteadyStateReplication);
incrementOpsAppliedStats();
return status;
}
@@ -330,7 +330,7 @@ Status SyncTail::syncApply(OperationContext* txn,
DisableDocumentValidation validationDisabler(txn);
Status status =
- applyOperationInLock(txn, db, op, convertUpdateToUpsert, incrementOpsAppliedStats);
+ applyOperationInLock(txn, db, op, inSteadyStateReplication, incrementOpsAppliedStats);
if (!status.isOK() && status.code() == ErrorCodes::WriteConflict) {
throw WriteConflictException();
}
@@ -390,10 +390,12 @@ Status SyncTail::syncApply(OperationContext* txn,
return Status(ErrorCodes::BadValue, ss);
}
-Status SyncTail::syncApply(OperationContext* txn, const BSONObj& op, bool convertUpdateToUpsert) {
+Status SyncTail::syncApply(OperationContext* txn,
+ const BSONObj& op,
+ bool inSteadyStateReplication) {
return SyncTail::syncApply(txn,
op,
- convertUpdateToUpsert,
+ inSteadyStateReplication,
applyOperation_inlock,
applyCommand_inlock,
stdx::bind(&Counter64::increment, &opsAppliedStats, 1ULL));
@@ -1045,8 +1047,8 @@ bool SyncTail::shouldRetry(OperationContext* txn, const BSONObj& o) {
void multiSyncApply(MultiApplier::OperationPtrs* ops, SyncTail*) {
initializeWriterThread();
auto txn = cc().makeOperationContext();
- auto syncApply = [](OperationContext* txn, const BSONObj& op, bool convertUpdateToUpsert) {
- return SyncTail::syncApply(txn, op, convertUpdateToUpsert);
+ auto syncApply = [](OperationContext* txn, const BSONObj& op, bool inSteadyStateReplication) {
+ return SyncTail::syncApply(txn, op, inSteadyStateReplication);
};
fassertNoTrace(16359, multiSyncApply_noAbort(txn.get(), ops, syncApply));
@@ -1067,7 +1069,9 @@ Status multiSyncApply_noAbort(OperationContext* txn,
[](const OplogEntry* l, const OplogEntry* r) { return l->ns < r->ns; });
}
- bool convertUpdatesToUpserts = true;
+ // This function is only called in steady state replication.
+ const bool inSteadyStateReplication = true;
+
// doNotGroupBeforePoint is used to prevent retrying bad group inserts by marking the final op
// of a failed group and not allowing further group inserts until that op has been processed.
auto doNotGroupBeforePoint = oplogEntryPointers->begin();
@@ -1116,7 +1120,7 @@ Status multiSyncApply_noAbort(OperationContext* txn,
try {
// Apply the group of inserts.
uassertStatusOK(
- syncApply(txn, groupedInsertBuilder.done(), convertUpdatesToUpserts));
+ syncApply(txn, groupedInsertBuilder.done(), inSteadyStateReplication));
// It succeeded, advance the oplogEntriesIterator to the end of the
// group of inserts.
oplogEntriesIterator = endOfGroupableOpsIterator - 1;
@@ -1136,7 +1140,7 @@ Status multiSyncApply_noAbort(OperationContext* txn,
try {
// Apply an individual (non-grouped) op.
- const Status status = syncApply(txn, entry->raw, convertUpdatesToUpserts);
+ const Status status = syncApply(txn, entry->raw, inSteadyStateReplication);
if (!status.isOK()) {
severe() << "Error applying operation (" << redact(entry->raw)
@@ -1175,15 +1179,24 @@ Status multiInitialSyncApply_noAbort(OperationContext* txn,
// allow us to get through the magic barrier
txn->lockState()->setShouldConflictWithSecondaryBatchApplication(false);
- bool convertUpdatesToUpserts = false;
+ // This function is only called in initial sync, as its name suggests.
+ const bool inSteadyStateReplication = false;
for (auto it = ops->begin(); it != ops->end(); ++it) {
auto& entry = **it;
try {
- const Status s = SyncTail::syncApply(txn, entry.raw, convertUpdatesToUpserts);
+ const Status s = SyncTail::syncApply(txn, entry.raw, inSteadyStateReplication);
if (!s.isOK()) {
+ // Don't retry on commands.
+ if (entry.isCommand()) {
+ error() << "Error applying command (" << redact(entry.raw)
+ << "): " << redact(s);
+ return s;
+ }
+
+ // We might need to fetch the missing docs from the sync source.
if (st->shouldRetry(txn, entry.raw)) {
- const Status s2 = SyncTail::syncApply(txn, entry.raw, convertUpdatesToUpserts);
+ const Status s2 = SyncTail::syncApply(txn, entry.raw, inSteadyStateReplication);
if (!s2.isOK()) {
severe() << "Error applying operation (" << redact(entry.raw)
<< "): " << redact(s2);
diff --git a/src/mongo/db/repl/sync_tail.h b/src/mongo/db/repl/sync_tail.h
index b2b06bb59ec..1bd30076161 100644
--- a/src/mongo/db/repl/sync_tail.h
+++ b/src/mongo/db/repl/sync_tail.h
@@ -65,22 +65,26 @@ public:
* Used for applying from an oplog.
* 'db' is the database where the op will be applied.
* 'opObj' is a BSONObj describing the op to be applied.
- * 'convertUpdateToUpsert' indicates to convert some updates to upserts for idempotency reasons.
+ * 'inSteadyStateReplication' indicates to convert some updates to upserts for idempotency
+ * reasons.
* 'opCounter' is used to update server status metrics.
* Returns failure status if the op was an update that could not be applied.
*/
using ApplyOperationInLockFn = stdx::function<Status(OperationContext* txn,
Database* db,
const BSONObj& opObj,
- bool convertUpdateToUpsert,
+ bool inSteadyStateReplication,
IncrementOpsAppliedStatsFn opCounter)>;
/**
* Type of function that takes a command op and applies it locally.
* Used for applying from an oplog.
+ * inSteadyStateReplication indicates whether we are in steady state replication, rather than
+ * initial sync.
* Returns failure status if the op that could not be applied.
*/
- using ApplyCommandInLockFn = stdx::function<Status(OperationContext*, const BSONObj&)>;
+ using ApplyCommandInLockFn =
+ stdx::function<Status(OperationContext*, const BSONObj&, bool inSteadyStateReplication)>;
SyncTail(BackgroundSync* q, MultiSyncApplyFunc func);
SyncTail(BackgroundSync* q, MultiSyncApplyFunc func, std::unique_ptr<OldThreadPool> writerPool);
@@ -98,12 +102,12 @@ public:
*/
static Status syncApply(OperationContext* txn,
const BSONObj& o,
- bool convertUpdateToUpsert,
+ bool inSteadyStateReplication,
ApplyOperationInLockFn applyOperationInLock,
ApplyCommandInLockFn applyCommandInLock,
IncrementOpsAppliedStatsFn incrementOpsAppliedStats);
- static Status syncApply(OperationContext* txn, const BSONObj& o, bool convertUpdateToUpsert);
+ static Status syncApply(OperationContext* txn, const BSONObj& o, bool inSteadyStateReplication);
void oplogApplication(ReplicationCoordinator* replCoord);
bool peek(OperationContext* txn, BSONObj* obj);
@@ -266,7 +270,7 @@ Status multiInitialSyncApply(MultiApplier::OperationPtrs* ops, SyncTail* st);
* SyncTail::syncApply.
*/
using SyncApplyFn =
- stdx::function<Status(OperationContext* txn, const BSONObj& o, bool convertUpdateToUpsert)>;
+ stdx::function<Status(OperationContext* txn, const BSONObj& o, bool inSteadyStateReplication)>;
Status multiSyncApply_noAbort(OperationContext* txn,
MultiApplier::OperationPtrs* ops,
SyncApplyFn syncApply);
diff --git a/src/mongo/db/repl/sync_tail_test.cpp b/src/mongo/db/repl/sync_tail_test.cpp
index d316a07217c..b3d560e96a1 100644
--- a/src/mongo/db/repl/sync_tail_test.cpp
+++ b/src/mongo/db/repl/sync_tail_test.cpp
@@ -124,9 +124,9 @@ void SyncTailTest::setUp() {
_applyOp = [](OperationContext* txn,
Database* db,
const BSONObj& op,
- bool convertUpdateToUpsert,
+ bool inSteadyStateReplication,
stdx::function<void()>) { return Status::OK(); };
- _applyCmd = [](OperationContext* txn, const BSONObj& op) { return Status::OK(); };
+ _applyCmd = [](OperationContext* txn, const BSONObj& op, bool) { return Status::OK(); };
_incOps = [this]() { _opsApplied++; };
}
@@ -233,6 +233,11 @@ OplogEntry makeUpdateDocumentOplogEntry(OpTime opTime,
return OplogEntry(bob.obj());
}
+Status failedApplyCommand(OperationContext* txn, const BSONObj& theOperation, bool) {
+ FAIL("applyCommand unexpectedly invoked.");
+ return Status::OK();
+}
+
TEST_F(SyncTailTest, SyncApplyNoNamespaceBadOp) {
const BSONObj op = BSON("op"
<< "x");
@@ -267,7 +272,7 @@ TEST_F(SyncTailTest, SyncApplyNoOp) {
SyncTail::ApplyOperationInLockFn applyOp = [&](OperationContext* txn,
Database* db,
const BSONObj& theOperation,
- bool convertUpdateToUpsert,
+ bool inSteadyStateReplication,
stdx::function<void()>) {
applyOpCalled = true;
ASSERT_TRUE(txn);
@@ -276,17 +281,12 @@ TEST_F(SyncTailTest, SyncApplyNoOp) {
ASSERT_TRUE(documentValidationDisabled(txn));
ASSERT_TRUE(db);
ASSERT_BSONOBJ_EQ(op, theOperation);
- ASSERT_FALSE(convertUpdateToUpsert);
- return Status::OK();
- };
- SyncTail::ApplyCommandInLockFn applyCmd = [&](OperationContext* txn,
- const BSONObj& theOperation) {
- FAIL("applyCommand unexpectedly invoked.");
+ ASSERT_FALSE(inSteadyStateReplication);
return Status::OK();
};
ASSERT_TRUE(_txn->writesAreReplicated());
ASSERT_FALSE(documentValidationDisabled(_txn.get()));
- ASSERT_OK(SyncTail::syncApply(_txn.get(), op, false, applyOp, applyCmd, _incOps));
+ ASSERT_OK(SyncTail::syncApply(_txn.get(), op, false, applyOp, failedApplyCommand, _incOps));
ASSERT_TRUE(applyOpCalled);
}
@@ -299,7 +299,7 @@ TEST_F(SyncTailTest, SyncApplyNoOpApplyOpThrowsException) {
SyncTail::ApplyOperationInLockFn applyOp = [&](OperationContext* txn,
Database* db,
const BSONObj& theOperation,
- bool convertUpdateToUpsert,
+ bool inSteadyStateReplication,
stdx::function<void()>) {
applyOpCalled++;
if (applyOpCalled < 5) {
@@ -307,12 +307,7 @@ TEST_F(SyncTailTest, SyncApplyNoOpApplyOpThrowsException) {
}
return Status::OK();
};
- SyncTail::ApplyCommandInLockFn applyCmd = [&](OperationContext* txn,
- const BSONObj& theOperation) {
- FAIL("applyCommand unexpectedly invoked.");
- return Status::OK();
- };
- ASSERT_OK(SyncTail::syncApply(_txn.get(), op, false, applyOp, applyCmd, _incOps));
+ ASSERT_OK(SyncTail::syncApply(_txn.get(), op, false, applyOp, failedApplyCommand, _incOps));
ASSERT_EQUALS(5, applyOpCalled);
}
@@ -325,7 +320,7 @@ void SyncTailTest::_testSyncApplyInsertDocument(LockMode expectedMode) {
SyncTail::ApplyOperationInLockFn applyOp = [&](OperationContext* txn,
Database* db,
const BSONObj& theOperation,
- bool convertUpdateToUpsert,
+ bool inSteadyStateReplication,
stdx::function<void()>) {
applyOpCalled = true;
ASSERT_TRUE(txn);
@@ -335,17 +330,12 @@ void SyncTailTest::_testSyncApplyInsertDocument(LockMode expectedMode) {
ASSERT_TRUE(documentValidationDisabled(txn));
ASSERT_TRUE(db);
ASSERT_BSONOBJ_EQ(op, theOperation);
- ASSERT_TRUE(convertUpdateToUpsert);
- return Status::OK();
- };
- SyncTail::ApplyCommandInLockFn applyCmd = [&](OperationContext* txn,
- const BSONObj& theOperation) {
- FAIL("applyCommand unexpectedly invoked.");
+ ASSERT_TRUE(inSteadyStateReplication);
return Status::OK();
};
ASSERT_TRUE(_txn->writesAreReplicated());
ASSERT_FALSE(documentValidationDisabled(_txn.get()));
- ASSERT_OK(SyncTail::syncApply(_txn.get(), op, true, applyOp, applyCmd, _incOps));
+ ASSERT_OK(SyncTail::syncApply(_txn.get(), op, true, applyOp, failedApplyCommand, _incOps));
ASSERT_TRUE(applyOpCalled);
}
@@ -386,7 +376,7 @@ TEST_F(SyncTailTest, SyncApplyIndexBuild) {
SyncTail::ApplyOperationInLockFn applyOp = [&](OperationContext* txn,
Database* db,
const BSONObj& theOperation,
- bool convertUpdateToUpsert,
+ bool inSteadyStateReplication,
stdx::function<void()>) {
applyOpCalled = true;
ASSERT_TRUE(txn);
@@ -395,17 +385,12 @@ TEST_F(SyncTailTest, SyncApplyIndexBuild) {
ASSERT_TRUE(documentValidationDisabled(txn));
ASSERT_TRUE(db);
ASSERT_BSONOBJ_EQ(op, theOperation);
- ASSERT_FALSE(convertUpdateToUpsert);
- return Status::OK();
- };
- SyncTail::ApplyCommandInLockFn applyCmd = [&](OperationContext* txn,
- const BSONObj& theOperation) {
- FAIL("applyCommand unexpectedly invoked.");
+ ASSERT_FALSE(inSteadyStateReplication);
return Status::OK();
};
ASSERT_TRUE(_txn->writesAreReplicated());
ASSERT_FALSE(documentValidationDisabled(_txn.get()));
- ASSERT_OK(SyncTail::syncApply(_txn.get(), op, false, applyOp, applyCmd, _incOps));
+ ASSERT_OK(SyncTail::syncApply(_txn.get(), op, false, applyOp, failedApplyCommand, _incOps));
ASSERT_TRUE(applyOpCalled);
}
@@ -418,21 +403,21 @@ TEST_F(SyncTailTest, SyncApplyCommand) {
SyncTail::ApplyOperationInLockFn applyOp = [&](OperationContext* txn,
Database* db,
const BSONObj& theOperation,
- bool convertUpdateToUpsert,
+ bool inSteadyStateReplication,
stdx::function<void()>) {
FAIL("applyOperation unexpectedly invoked.");
return Status::OK();
};
- SyncTail::ApplyCommandInLockFn applyCmd = [&](OperationContext* txn,
- const BSONObj& theOperation) {
- applyCmdCalled = true;
- ASSERT_TRUE(txn);
- ASSERT_TRUE(txn->lockState()->isW());
- ASSERT_TRUE(txn->writesAreReplicated());
- ASSERT_FALSE(documentValidationDisabled(txn));
- ASSERT_BSONOBJ_EQ(op, theOperation);
- return Status::OK();
- };
+ SyncTail::ApplyCommandInLockFn applyCmd =
+ [&](OperationContext* txn, const BSONObj& theOperation, bool inSteadyStateReplication) {
+ applyCmdCalled = true;
+ ASSERT_TRUE(txn);
+ ASSERT_TRUE(txn->lockState()->isW());
+ ASSERT_TRUE(txn->writesAreReplicated());
+ ASSERT_FALSE(documentValidationDisabled(txn));
+ ASSERT_BSONOBJ_EQ(op, theOperation);
+ return Status::OK();
+ };
ASSERT_TRUE(_txn->writesAreReplicated());
ASSERT_FALSE(documentValidationDisabled(_txn.get()));
ASSERT_OK(SyncTail::syncApply(_txn.get(), op, false, applyOp, applyCmd, _incOps));
@@ -449,19 +434,19 @@ TEST_F(SyncTailTest, SyncApplyCommandThrowsException) {
SyncTail::ApplyOperationInLockFn applyOp = [&](OperationContext* txn,
Database* db,
const BSONObj& theOperation,
- bool convertUpdateToUpsert,
+ bool inSteadyStateReplication,
stdx::function<void()>) {
FAIL("applyOperation unexpectedly invoked.");
return Status::OK();
};
- SyncTail::ApplyCommandInLockFn applyCmd = [&](OperationContext* txn,
- const BSONObj& theOperation) {
- applyCmdCalled++;
- if (applyCmdCalled < 5) {
- throw WriteConflictException();
- }
- return Status::OK();
- };
+ SyncTail::ApplyCommandInLockFn applyCmd =
+ [&](OperationContext* txn, const BSONObj& theOperation, bool inSteadyStateReplication) {
+ applyCmdCalled++;
+ if (applyCmdCalled < 5) {
+ throw WriteConflictException();
+ }
+ return Status::OK();
+ };
ASSERT_OK(SyncTail::syncApply(_txn.get(), op, false, applyOp, applyCmd, _incOps));
ASSERT_EQUALS(5, applyCmdCalled);
ASSERT_EQUALS(1U, _opsApplied);
@@ -923,4 +908,28 @@ TEST_F(SyncTailTest, MultiInitialSyncApplyPassesThroughShouldSyncTailRetryError)
multiInitialSyncApply_noAbort(_txn.get(), &ops, &syncTail));
}
+TEST_F(SyncTailTest, MultiInitialSyncApplyFailsOnRenameCollection) {
+ SyncTail syncTail(nullptr, SyncTail::MultiSyncApplyFunc(), nullptr);
+
+ BSONObjBuilder bob;
+ bob.appendElements(OpTime(Timestamp(1, 0), 1LL).toBSON());
+ bob.append("h", 1LL);
+ bob.append("op", "c");
+ bob.append("ns", "test.$cmd");
+ bob.append("o",
+ BSON("renameCollection"
+ << "test.foo"
+ << "to"
+ << "test.bar"
+ << "stayTemp"
+ << false
+ << "dropTarget"
+ << false));
+ auto op = OplogEntry(bob.obj());
+
+ MultiApplier::OperationPtrs ops = {&op};
+ ASSERT_EQUALS(ErrorCodes::OplogOperationUnsupported,
+ multiInitialSyncApply_noAbort(_txn.get(), &ops, &syncTail));
+}
+
} // namespace