diff options
author | Benety Goh <benety@mongodb.com> | 2017-08-26 09:58:25 -0400 |
---|---|---|
committer | Benety Goh <benety@mongodb.com> | 2017-09-20 18:00:27 -0400 |
commit | 79dea38841fedff2b0137069f60fb75921607ead (patch) | |
tree | 4eaee23cfe7f66473069035a327fa610c935f118 /src | |
parent | 06e0893491b5f0babf7484ec1c4a6a68109aa081 (diff) | |
download | mongo-79dea38841fedff2b0137069f60fb75921607ead.tar.gz |
SERVER-30830 applyOps checks if collection/database exists before applying CRUD operation
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/repl/apply_ops.cpp | 117 |
1 files changed, 71 insertions, 46 deletions
diff --git a/src/mongo/db/repl/apply_ops.cpp b/src/mongo/db/repl/apply_ops.cpp index 30eaee93482..aeb493e610c 100644 --- a/src/mongo/db/repl/apply_ops.cpp +++ b/src/mongo/db/repl/apply_ops.cpp @@ -123,8 +123,7 @@ Status _applyOps(OperationContext* opCtx, if (*opType == 'n') continue; - const std::string ns = opObj["ns"].String(); - const NamespaceString nss{ns}; + const NamespaceString nss(opObj["ns"].String()); // Need to check this here, or OldClientContext may fail an invariant. if (*opType != 'c' && !nss.isValid()) @@ -136,63 +135,89 @@ Status _applyOps(OperationContext* opCtx, invariant(opCtx->lockState()->isW()); invariant(*opType != 'c'); - if (!dbHolder().get(opCtx, ns)) { + auto db = dbHolder().get(opCtx, nss.ns()); + if (!db) { throw DBException( ErrorCodes::NamespaceNotFound, "cannot create a database in atomic applyOps mode; will retry without " "atomicity"); } - OldClientContext ctx(opCtx, ns); + // When processing an update on a non-existent collection, applyOperation_inlock() + // returns UpdateOperationFailed on updates and allows the collection to be + // implicitly created on upserts. We detect both cases here and fail early with + // NamespaceNotFound. + // Additionally for inserts, we fail early on non-existent collections. + auto collection = db->getCollection(opCtx, nss); + if (!collection && !nss.isSystemDotIndexes() && (*opType == 'i' || *opType == 'u')) { + throw DBException( + ErrorCodes::NamespaceNotFound, + str::stream() + << "cannot apply insert or update operation on a non-existent namespace " + << nss.ns() + << " in atomic applyOps mode: " + << redact(opObj)); + } + + OldClientContext ctx(opCtx, nss.ns()); status = repl::applyOperation_inlock(opCtx, ctx.db(), opObj, alwaysUpsert); if (!status.isOK()) return status; } else { try { - writeConflictRetry(opCtx, "applyOps", ns, [&] { - if (*opType == 'c') { - invariant(opCtx->lockState()->isW()); - status = repl::applyCommand_inlock(opCtx, opObj, true); - } else { - Lock::DBLock dbWriteLock(opCtx, nss.db(), MODE_IX); - Lock::CollectionLock lock(opCtx->lockState(), nss.ns(), MODE_IX); - OldClientContext ctx(opCtx, ns); - const char* names[] = {"o", "ns"}; - BSONElement fields[2]; - opObj.getFields(2, names, fields); - BSONElement& fieldO = fields[0]; - BSONElement& fieldNs = fields[1]; - const StringData ns = fieldNs.valueStringData(); - NamespaceString requestNss{ns}; - - if (nss.isSystemDotIndexes()) { - BSONObj indexSpec; - NamespaceString indexNss; - std::tie(indexSpec, indexNss) = - repl::prepForApplyOpsIndexInsert(fieldO, opObj, requestNss); - BSONObjBuilder command; - command.append("createIndexes", indexNss.coll()); - { - BSONArrayBuilder indexes(command.subarrayStart("indexes")); - indexes.append(indexSpec); - indexes.doneFast(); + status = writeConflictRetry( + opCtx, "applyOps", nss.ns(), [opCtx, nss, opObj, opType, alwaysUpsert] { + if (*opType == 'c') { + invariant(opCtx->lockState()->isW()); + return repl::applyCommand_inlock(opCtx, opObj, true); + } + + AutoGetCollection autoColl(opCtx, nss, MODE_IX); + if (!autoColl.getCollection() && !nss.isSystemDotIndexes()) { + // For idempotency reasons, return success on delete operations. + if (*opType == 'd') { + return Status::OK(); } - const BSONObj commandObj = command.done(); - - DBDirectClient client(opCtx); - BSONObj infoObj; - client.runCommand(nsToDatabase(ns), commandObj, infoObj); - status = getStatusFromCommandResult(infoObj); - - // Only uassert to stop applyOps only when building indexes, - // but not for CRUD ops. - uassertStatusOK(status); - } else { - status = - repl::applyOperation_inlock(opCtx, ctx.db(), opObj, alwaysUpsert); + throw DBException(ErrorCodes::NamespaceNotFound, + str::stream() + << "cannot apply insert or update operation on a " + "non-existent namespace " + << nss.ns() + << ": " + << mongo::redact(opObj)); + } + + OldClientContext ctx(opCtx, nss.ns()); + + if (!nss.isSystemDotIndexes()) { + return repl::applyOperation_inlock( + opCtx, ctx.db(), opObj, alwaysUpsert); + } + + auto fieldO = opObj["o"]; + BSONObj indexSpec; + NamespaceString indexNss; + std::tie(indexSpec, indexNss) = + repl::prepForApplyOpsIndexInsert(fieldO, opObj, nss); + BSONObjBuilder command; + command.append("createIndexes", indexNss.coll()); + { + BSONArrayBuilder indexes(command.subarrayStart("indexes")); + indexes.append(indexSpec); + indexes.doneFast(); } - } - }); + const BSONObj commandObj = command.done(); + + DBDirectClient client(opCtx); + BSONObj infoObj; + client.runCommand(nss.db().toString(), commandObj, infoObj); + + // Uassert to stop applyOps only when building indexes, but not for CRUD + // ops. + uassertStatusOK(getStatusFromCommandResult(infoObj)); + + return Status::OK(); + }); } catch (const DBException& ex) { ab.append(false); result->append("applied", ++(*numApplied)); |