summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBenety Goh <benety@mongodb.com>2017-08-26 09:58:25 -0400
committerBenety Goh <benety@mongodb.com>2017-09-20 18:00:27 -0400
commit79dea38841fedff2b0137069f60fb75921607ead (patch)
tree4eaee23cfe7f66473069035a327fa610c935f118 /src
parent06e0893491b5f0babf7484ec1c4a6a68109aa081 (diff)
downloadmongo-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.cpp117
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));