diff options
Diffstat (limited to 'src/mongo/db/repl/oplog.cpp')
| -rw-r--r-- | src/mongo/db/repl/oplog.cpp | 131 |
1 files changed, 71 insertions, 60 deletions
diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp index 43a1eba5042..76df271ed78 100644 --- a/src/mongo/db/repl/oplog.cpp +++ b/src/mongo/db/repl/oplog.cpp @@ -116,6 +116,11 @@ namespace { // cached copy...so don't rename, drop, etc.!!! Collection* _localOplogCollection = nullptr; +// Specifies whether we abort initial sync when attempting to apply a renameCollection operation. +// If set to true, users risk corrupting their data. This should only be enabled by expert users +// of the server who understand the risks this poses. +MONGO_EXPORT_SERVER_PARAMETER(allowUnsafeRenamesDuringInitialSync, bool, false); + PseudoRandom hashGenerator(std::unique_ptr<SecureRandom>(SecureRandom::create())->nextInt64()); // Synchronizes the section where a new Timestamp is generated and when it actually @@ -663,6 +668,45 @@ std::map<std::string, ApplyOpMetadata> opsMap = { } // namespace +std::pair<BSONObj, NamespaceString> prepForApplyOpsIndexInsert(const BSONElement& fieldO, + const BSONObj& op, + const NamespaceString& requestNss) { + uassert(ErrorCodes::NoSuchKey, + str::stream() << "Missing expected index spec in field 'o': " << op, + !fieldO.eoo()); + uassert(ErrorCodes::TypeMismatch, + str::stream() << "Expected object for index spec in field 'o': " << op, + fieldO.isABSONObj()); + BSONObj indexSpec = fieldO.embeddedObject(); + + std::string indexNs; + uassertStatusOK(bsonExtractStringField(indexSpec, "ns", &indexNs)); + const NamespaceString indexNss(indexNs); + uassert(ErrorCodes::InvalidNamespace, + str::stream() << "Invalid namespace in index spec: " << op, + indexNss.isValid()); + uassert(ErrorCodes::InvalidNamespace, + str::stream() << "Database name mismatch for database (" << requestNss.db() + << ") while creating index: " + << op, + requestNss.db() == indexNss.db()); + + if (!indexSpec["v"]) { + // If the "v" field isn't present in the index specification, then we assume it is a + // v=1 index from an older version of MongoDB. This is because + // (1) we haven't built v=0 indexes as the default for a long time, and + // (2) the index version has been included in the corresponding oplog entry since + // v=2 indexes were introduced. + BSONObjBuilder bob; + + bob.append("v", static_cast<int>(IndexVersion::kV1)); + bob.appendElements(indexSpec); + + indexSpec = bob.obj(); + } + + return std::make_pair(indexSpec, indexNss); +} // @return failure status if an update should have happened and the document DNE. // See replset initial sync code. Status applyOperation_inlock(OperationContext* txn, @@ -688,6 +732,7 @@ Status applyOperation_inlock(OperationContext* txn, o = fieldO.embeddedObject(); const StringData ns = fieldNs.valueStringData(); + NamespaceString requestNss{ns}; BSONObj o2; if (fieldO2.isABSONObj()) @@ -718,27 +763,11 @@ Status applyOperation_inlock(OperationContext* txn, invariant(*opType != 'c'); // commands are processed in applyCommand_inlock() if (*opType == 'i') { - if (nsToCollectionSubstring(ns) == "system.indexes") { - uassert(ErrorCodes::NoSuchKey, - str::stream() << "Missing expected index spec in field 'o': " << op, - !fieldO.eoo()); - uassert(ErrorCodes::TypeMismatch, - str::stream() << "Expected object for index spec in field 'o': " << op, - fieldO.isABSONObj()); - BSONObj indexSpec = fieldO.embeddedObject(); - - std::string indexNs; - uassertStatusOK(bsonExtractStringField(indexSpec, "ns", &indexNs)); - const NamespaceString indexNss(indexNs); - uassert(ErrorCodes::InvalidNamespace, - str::stream() << "Invalid namespace in index spec: " << op, - indexNss.isValid()); - uassert(ErrorCodes::InvalidNamespace, - str::stream() << "Database name mismatch for database (" - << nsToDatabaseSubstring(ns) - << ") while creating index: " - << op, - nsToDatabaseSubstring(ns) == indexNss.db()); + if (requestNss.isSystemDotIndexes()) { + BSONObj indexSpec; + NamespaceString indexNss; + std::tie(indexSpec, indexNss) = + repl::prepForApplyOpsIndexInsert(fieldO, op, requestNss); // Check if collection exists. auto indexCollection = db->getCollection(indexNss); @@ -749,20 +778,6 @@ Status applyOperation_inlock(OperationContext* txn, opCounters->gotInsert(); - if (!indexSpec["v"]) { - // If the "v" field isn't present in the index specification, then we assume it is a - // v=1 index from an older version of MongoDB. This is because - // (1) we haven't built v=0 indexes as the default for a long time, and - // (2) the index version has been included in the corresponding oplog entry since - // v=2 indexes were introduced. - BSONObjBuilder bob; - - bob.append("v", static_cast<int>(IndexVersion::kV1)); - bob.appendElements(indexSpec); - - indexSpec = bob.obj(); - } - bool relaxIndexConstraints = ReplicationCoordinator::get(txn)->shouldRelaxIndexConstraints(indexNss); if (indexSpec["background"].trueValue()) { @@ -863,13 +878,12 @@ Status applyOperation_inlock(OperationContext* txn, BSONObjBuilder b; b.append(o.getField("_id")); - const NamespaceString requestNs(ns); - UpdateRequest request(requestNs); + UpdateRequest request(requestNss); request.setQuery(b.done()); request.setUpdates(o); request.setUpsert(); - UpdateLifecycleImpl updateLifecycle(requestNs); + UpdateLifecycleImpl updateLifecycle(requestNss); request.setLifecycle(&updateLifecycle); UpdateResult res = update(txn, db, request); @@ -894,13 +908,12 @@ Status applyOperation_inlock(OperationContext* txn, str::stream() << "Failed to apply update due to missing _id: " << op.toString(), updateCriteria.hasField("_id")); - const NamespaceString requestNs(ns); - UpdateRequest request(requestNs); + UpdateRequest request(requestNss); request.setQuery(updateCriteria); request.setUpdates(o); request.setUpsert(upsert); - UpdateLifecycleImpl updateLifecycle(requestNs); + UpdateLifecycleImpl updateLifecycle(requestNss); request.setLifecycle(&updateLifecycle); UpdateResult ur = update(txn, db, request); @@ -954,7 +967,12 @@ Status applyOperation_inlock(OperationContext* txn, o.hasField("_id")); if (opType[1] == 0) { - deleteObjects(txn, collection, ns, o, PlanExecutor::YIELD_MANUAL, /*justOne*/ valueB); + deleteObjects(txn, + collection, + requestNss.ns().c_str(), + o, + PlanExecutor::YIELD_MANUAL, + /*justOne*/ valueB); } else verify(opType[1] == 'b'); // "db" advertisement if (incrementOpsAppliedStats) { @@ -970,16 +988,6 @@ Status applyOperation_inlock(OperationContext* txn, 14825, str::stream() << "error in applyOperation : unknown opType " << *opType); } - // AuthorizationManager's logOp method registers a RecoveryUnit::Change and to do so we need - // to a new WriteUnitOfWork, if we dont have a wrapping unit of work already. If we already - // have a wrapping WUOW, the extra nexting is harmless. The logOp really should have been - // done in the WUOW that did the write, but this won't happen because applyOps turns off - // observers. - WriteUnitOfWork wuow(txn); - getGlobalAuthorizationManager()->logOp( - txn, opType, ns.toString().c_str(), o, fieldO2.isABSONObj() ? &o2 : NULL); - wuow.commit(); - return Status::OK(); } @@ -1021,9 +1029,15 @@ Status applyCommand_inlock(OperationContext* txn, // 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)); + if (!allowUnsafeRenamesDuringInitialSync.load()) { + return Status(ErrorCodes::OplogOperationUnsupported, + str::stream() + << "Applying renameCollection not supported in initial sync: " + << redact(op)); + } + warning() << "allowUnsafeRenamesDuringInitialSync set to true. Applying renameCollection " + "operation during initial sync even though it may lead to data corruption: " + << redact(op); } // Applying commands in repl is done under Global W-lock, so it is safe to not @@ -1071,11 +1085,8 @@ Status applyCommand_inlock(OperationContext* txn, break; } default: - if (_oplogCollectionName == masterSlaveOplogName) { - error() << "Failed command " << redact(o) << " on " << nss.db() - << " with status " << status << " during oplog application"; - } else if (curOpToApply.acceptableErrors.find(status.code()) == - curOpToApply.acceptableErrors.end()) { + if (curOpToApply.acceptableErrors.find(status.code()) == + curOpToApply.acceptableErrors.end()) { error() << "Failed command " << redact(o) << " on " << nss.db() << " with status " << status << " during oplog application"; return status; |
