summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandolph Tan <randolph@10gen.com>2018-02-07 17:59:23 -0500
committerRandolph Tan <randolph@10gen.com>2018-02-23 10:44:35 -0500
commitf6007fbc72791a1752edb502894e33ee831e4ace (patch)
tree8c544c575af8267a165234428976452f8b277d90
parent04cc084048a5f37c9d10ee3bac9dbef1e89672c5 (diff)
downloadmongo-f6007fbc72791a1752edb502894e33ee831e4ace.tar.gz
SERVER-32615 Make user write commands in sharded cluster go through _configCreate
-rw-r--r--jstests/sharding/coll_epoch_test1.js52
-rw-r--r--src/mongo/base/error_codes.err2
-rw-r--r--src/mongo/db/catalog/SConscript1
-rw-r--r--src/mongo/db/catalog/database_impl.cpp3
-rw-r--r--src/mongo/db/commands/write_commands/write_commands.cpp17
-rw-r--r--src/mongo/db/ops/write_ops_exec.cpp13
-rw-r--r--src/mongo/db/s/SConscript1
-rw-r--r--src/mongo/db/s/config/configsvr_create_collection_command.cpp4
-rw-r--r--src/mongo/db/s/config/sharding_catalog_manager_add_shard_test.cpp4
-rw-r--r--src/mongo/db/s/implicit_create_collection.cpp150
-rw-r--r--src/mongo/db/s/implicit_create_collection.h43
-rw-r--r--src/mongo/s/SConscript1
-rw-r--r--src/mongo/s/cannot_implicitly_create_collection_info.cpp49
-rw-r--r--src/mongo/s/cannot_implicitly_create_collection_info.h59
-rw-r--r--src/mongo/s/commands/cluster_write_cmd.cpp7
-rw-r--r--src/mongo/s/request_types/create_collection.idl1
-rw-r--r--src/mongo/s/write_ops/batch_write_exec.cpp9
-rw-r--r--src/mongo/s/write_ops/batch_write_op.cpp2
-rw-r--r--src/mongo/s/write_ops/batched_command_request.cpp7
-rw-r--r--src/mongo/s/write_ops/batched_command_request.h9
-rw-r--r--src/mongo/s/write_ops/batched_command_request_test.cpp5
-rw-r--r--src/mongo/s/write_ops/write_op.cpp3
22 files changed, 403 insertions, 39 deletions
diff --git a/jstests/sharding/coll_epoch_test1.js b/jstests/sharding/coll_epoch_test1.js
index fde6f44696c..63a271f922c 100644
--- a/jstests/sharding/coll_epoch_test1.js
+++ b/jstests/sharding/coll_epoch_test1.js
@@ -82,33 +82,35 @@
// Test that inserts and queries go to correct shard even when the collection has been unsharded
// and resharded from another mongos on a different primary
//
-
- jsTest.log("Re-creating sharded collection with different primary...");
-
- var getOtherShard = function(shardId) {
- for (var i = 0; i < shards.length; ++i) {
- if (shards[i].shardName != shardId)
- return shards[i].shardName;
+ // TODO: SERVER-32607 uncomment after the proper database version checks are in place.
+ /*
+ jsTest.log("Re-creating sharded collection with different primary...");
+
+ var getOtherShard = function(shardId) {
+ for (var i = 0; i < shards.length; ++i) {
+ if (shards[i].shardName != shardId)
+ return shards[i].shardName;
+ }
+ };
+
+ assert.commandWorked(admin.runCommand({
+ movePrimary: coll.getDB() + "",
+ to: getOtherShard(config.databases.findOne({_id: coll.getDB() + ""}).primary)
+ }));
+ assert.commandWorked(admin.runCommand({shardCollection: coll + "", key: {_id: 1}}));
+
+ bulk = insertMongos.getCollection(coll + "").initializeUnorderedBulkOp();
+ for (var i = 0; i < 100; i++) {
+ bulk.insert({test: "d"});
}
- };
-
- assert.commandWorked(admin.runCommand({
- movePrimary: coll.getDB() + "",
- to: getOtherShard(config.databases.findOne({_id: coll.getDB() + ""}).primary)
- }));
- assert.commandWorked(admin.runCommand({shardCollection: coll + "", key: {_id: 1}}));
+ assert.writeOK(bulk.execute());
- bulk = insertMongos.getCollection(coll + "").initializeUnorderedBulkOp();
- for (var i = 0; i < 100; i++) {
- bulk.insert({test: "d"});
- }
- assert.writeOK(bulk.execute());
-
- assert.eq(100, staleMongos.getCollection(coll + "").find({test: "d"}).itcount());
- assert.eq(0,
- staleMongos.getCollection(coll + "").find({test: {$in: ["a", "b", "c"]}}).itcount());
-
- assert(coll.drop());
+ assert.eq(100, staleMongos.getCollection(coll + "").find({test: "d"}).itcount());
+ assert.eq(0,
+ staleMongos.getCollection(coll + "").find({test: {$in: ["a", "b",
+ "c"]}}).itcount());
+ assert(coll.drop());
+ */
st.stop();
})();
diff --git a/src/mongo/base/error_codes.err b/src/mongo/base/error_codes.err
index 8dbfac9cfd9..542d75a8b93 100644
--- a/src/mongo/base/error_codes.err
+++ b/src/mongo/base/error_codes.err
@@ -224,7 +224,7 @@ error_code("IllegalOpMsgFlag", 223)
error_code("QueryFeatureNotAllowed", 224)
error_code("TransactionTooOld", 225)
error_code("AtomicityFailure", 226)
-error_code("CannotImplicitlyCreateCollection", 227);
+error_code("CannotImplicitlyCreateCollection", 227, extra="CannotImplicitlyCreateCollectionInfo");
error_code("SessionTransferIncomplete", 228)
error_code("MustDowngrade", 229)
error_code("DNSHostNotFound", 230)
diff --git a/src/mongo/db/catalog/SConscript b/src/mongo/db/catalog/SConscript
index 92a508ffeff..1c45edcd9d3 100644
--- a/src/mongo/db/catalog/SConscript
+++ b/src/mongo/db/catalog/SConscript
@@ -287,6 +287,7 @@ env.Library(
'$BUILD_DIR/mongo/db/system_index',
'$BUILD_DIR/mongo/db/ttl_collection_cache',
'$BUILD_DIR/mongo/db/views/views_mongod',
+ '$BUILD_DIR/mongo/s/stale_config',
],
LIBDEPS_PRIVATE=[
'$BUILD_DIR/mongo/db/logical_clock',
diff --git a/src/mongo/db/catalog/database_impl.cpp b/src/mongo/db/catalog/database_impl.cpp
index 7206621a5f5..858a579982e 100644
--- a/src/mongo/db/catalog/database_impl.cpp
+++ b/src/mongo/db/catalog/database_impl.cpp
@@ -71,6 +71,7 @@
#include "mongo/db/system_index.h"
#include "mongo/db/views/view_catalog.h"
#include "mongo/platform/random.h"
+#include "mongo/s/cannot_implicitly_create_collection_info.h"
#include "mongo/stdx/memory.h"
#include "mongo/util/assert_util.h"
#include "mongo/util/fail_point_service.h"
@@ -766,7 +767,7 @@ Collection* DatabaseImpl::createCollection(OperationContext* opCtx,
invariant(!options.isView());
NamespaceString nss(ns);
- uassert(ErrorCodes::CannotImplicitlyCreateCollection,
+ uassert(CannotImplicitlyCreateCollectionInfo(nss),
"request doesn't allow collection to be created implicitly",
OperationShardingState::get(opCtx).allowImplicitCollectionCreation());
diff --git a/src/mongo/db/commands/write_commands/write_commands.cpp b/src/mongo/db/commands/write_commands/write_commands.cpp
index 428fb40b7e2..a781b2682ed 100644
--- a/src/mongo/db/commands/write_commands/write_commands.cpp
+++ b/src/mongo/db/commands/write_commands/write_commands.cpp
@@ -99,13 +99,16 @@ void serializeReply(OperationContext* opCtx,
if (shouldSkipOutput(opCtx))
return;
- if (continueOnError && !result.results.empty() &&
- result.results.back() == ErrorCodes::StaleConfig) {
- // For ordered:false commands we need to duplicate the StaleConfig result for all ops
- // after we stopped. See handleError() in write_ops_exec.cpp for more info.
- auto err = result.results.back();
- while (result.results.size() < opsInBatch) {
- result.results.emplace_back(err);
+ if (continueOnError && !result.results.empty()) {
+ const auto& lastResult = result.results.back();
+ if (lastResult == ErrorCodes::StaleConfig ||
+ lastResult == ErrorCodes::CannotImplicitlyCreateCollection) {
+ // For ordered:false commands we need to duplicate these error results for all ops
+ // after we stopped. See handleError() in write_ops_exec.cpp for more info.
+ auto err = result.results.back();
+ while (result.results.size() < opsInBatch) {
+ result.results.emplace_back(err);
+ }
}
}
diff --git a/src/mongo/db/ops/write_ops_exec.cpp b/src/mongo/db/ops/write_ops_exec.cpp
index ba28fca2ccb..413f729b347 100644
--- a/src/mongo/db/ops/write_ops_exec.cpp
+++ b/src/mongo/db/ops/write_ops_exec.cpp
@@ -62,6 +62,7 @@
#include "mongo/db/repl/replication_coordinator.h"
#include "mongo/db/retryable_writes_stats.h"
#include "mongo/db/s/collection_sharding_state.h"
+#include "mongo/db/s/implicit_create_collection.h"
#include "mongo/db/s/shard_filtering_metadata_refresh.h"
#include "mongo/db/s/sharding_state.h"
#include "mongo/db/session_catalog.h"
@@ -69,6 +70,7 @@
#include "mongo/db/stats/top.h"
#include "mongo/db/write_concern.h"
#include "mongo/rpc/get_status_from_command_result.h"
+#include "mongo/s/cannot_implicitly_create_collection_info.h"
#include "mongo/stdx/memory.h"
#include "mongo/util/fail_point_service.h"
#include "mongo/util/log.h"
@@ -229,6 +231,17 @@ bool handleError(OperationContext* opCtx,
// Command reply serializer will handle repeating this error if needed.
out->results.emplace_back(ex.toStatus());
return false;
+ } else if (auto cannotImplicitCreateCollInfo =
+ ex.extraInfo<CannotImplicitlyCreateCollectionInfo>()) {
+ // Don't try doing more ops since they will fail with the same error.
+ // Command reply serializer will handle repeating this error if needed.
+ out->results.emplace_back(ex.toStatus());
+
+ if (ShardingState::get(opCtx)->enabled()) {
+ onCannotImplicitlyCreateCollection(opCtx, cannotImplicitCreateCollInfo->getNss());
+ }
+
+ return false;
}
out->results.emplace_back(ex.toStatus());
diff --git a/src/mongo/db/s/SConscript b/src/mongo/db/s/SConscript
index 99b820224da..f2fbcb15906 100644
--- a/src/mongo/db/s/SConscript
+++ b/src/mongo/db/s/SConscript
@@ -22,6 +22,7 @@ env.Library(
env.Library(
target='sharding_runtime_d',
source=[
+ 'implicit_create_collection.cpp',
'migration_destination_manager.cpp',
'session_catalog_migration_destination.cpp',
'shard_filtering_metadata_refresh.cpp',
diff --git a/src/mongo/db/s/config/configsvr_create_collection_command.cpp b/src/mongo/db/s/config/configsvr_create_collection_command.cpp
index 1e733dfda54..7eba3758208 100644
--- a/src/mongo/db/s/config/configsvr_create_collection_command.cpp
+++ b/src/mongo/db/s/config/configsvr_create_collection_command.cpp
@@ -109,7 +109,9 @@ public:
IDLParserErrorContext("ConfigsvrCreateCollection"), cmdObj);
CollectionOptions options;
- uassertStatusOK(options.parse(createCmd.getOptions()));
+ if (auto requestOptions = createCmd.getOptions()) {
+ uassertStatusOK(options.parse(*requestOptions));
+ }
ShardingCatalogManager::get(opCtx)->createCollection(opCtx, createCmd.getNs(), options);
diff --git a/src/mongo/db/s/config/sharding_catalog_manager_add_shard_test.cpp b/src/mongo/db/s/config/sharding_catalog_manager_add_shard_test.cpp
index b0507b34451..806616b87e0 100644
--- a/src/mongo/db/s/config/sharding_catalog_manager_add_shard_test.cpp
+++ b/src/mongo/db/s/config/sharding_catalog_manager_add_shard_test.cpp
@@ -354,7 +354,9 @@ TEST_F(AddShardTest, CreateShardIdentityUpsertForAddShard) {
<< BSON("w"
<< "majority"
<< "wtimeout"
- << 15000));
+ << 15000)
+ << "allowImplicitCollectionCreation"
+ << true);
ASSERT_BSONOBJ_EQ(expectedBSON,
ShardingCatalogManager::get(operationContext())
->createShardIdentityUpsertForAddShard(operationContext(), shardName));
diff --git a/src/mongo/db/s/implicit_create_collection.cpp b/src/mongo/db/s/implicit_create_collection.cpp
new file mode 100644
index 00000000000..17f68349c30
--- /dev/null
+++ b/src/mongo/db/s/implicit_create_collection.cpp
@@ -0,0 +1,150 @@
+/**
+ * Copyright (C) 2018 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kSharding
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/s/implicit_create_collection.h"
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "mongo/db/catalog/catalog_raii.h"
+#include "mongo/db/commands.h"
+#include "mongo/db/namespace_string.h"
+#include "mongo/db/operation_context.h"
+#include "mongo/db/service_context.h"
+#include "mongo/s/grid.h"
+#include "mongo/s/request_types/create_collection_gen.h"
+
+#include "mongo/stdx/condition_variable.h"
+#include "mongo/stdx/mutex.h"
+#include "mongo/util/scopeguard.h"
+
+namespace mongo {
+
+namespace {
+
+/**
+ * Responsible for explicitly creating collections in the sharding catalog. Also takes care of
+ * making sure that concurrent attempts to create a collection for the same ns will be
+ * synchronized and avoid duplicate work as much as possible.
+ */
+
+class CreateCollectionSerializer {
+public:
+ explicit CreateCollectionSerializer(NamespaceString ns) : _ns(std::move(ns)) {}
+
+ /**
+ * Initialize this collection so it will be officially tracked in a sharded environment
+ * by sending the command to the config server to create an entry for this collection in
+ * the sharding catalog.
+ */
+ void onCannotImplicitlyCreateCollection(OperationContext* opCtx) {
+ invariant(!opCtx->lockState()->isLocked());
+
+ {
+ stdx::unique_lock<stdx::mutex> lg(_mutex);
+ while (_isInProgress) {
+ opCtx->waitForConditionOrInterrupt(_cvIsInProgress, lg);
+ }
+
+ _isInProgress = true;
+ }
+
+ ON_BLOCK_EXIT([&] {
+ stdx::lock_guard<stdx::mutex> lg(_mutex);
+ _isInProgress = false;
+ _cvIsInProgress.notify_one();
+ });
+
+ {
+ AutoGetCollection autoColl(opCtx, _ns, MODE_IS);
+ if (autoColl.getCollection() != nullptr) {
+ // Collection already created, no more work needs to be done.
+ return;
+ }
+ }
+
+ ConfigsvrCreateCollection configCreateCmd;
+ configCreateCmd.setNs(_ns);
+
+ uassertStatusOK(
+ Grid::get(opCtx)->shardRegistry()->getConfigShard()->runCommandWithFixedRetryAttempts(
+ opCtx,
+ ReadPreferenceSetting{ReadPreference::PrimaryOnly},
+ "admin",
+ CommandHelpers::appendMajorityWriteConcern(configCreateCmd.toBSON()),
+ Shard::RetryPolicy::kIdempotent));
+ }
+
+private:
+ const NamespaceString _ns;
+
+ stdx::mutex _mutex;
+ stdx::condition_variable _cvIsInProgress;
+ bool _isInProgress = false;
+};
+
+class CreateCollectionSerializerMap {
+public:
+ std::shared_ptr<CreateCollectionSerializer> getForNs(const NamespaceString& ns) {
+ stdx::lock_guard<stdx::mutex> lg(_mutex);
+ auto iter = _inProgressMap.find(ns.ns());
+ if (iter == _inProgressMap.end()) {
+ std::tie(iter, std::ignore) =
+ _inProgressMap.emplace(ns.ns(), std::make_shared<CreateCollectionSerializer>(ns));
+ }
+
+ return iter->second;
+ }
+
+ void cleanupNs(const NamespaceString& ns) {
+ stdx::lock_guard<stdx::mutex> lg(_mutex);
+ _inProgressMap.erase(ns.ns());
+ }
+
+private:
+ stdx::mutex _mutex;
+ std::map<std::string, std::shared_ptr<CreateCollectionSerializer>> _inProgressMap;
+};
+
+const auto createCollectionSerializerMap =
+ ServiceContext::declareDecoration<CreateCollectionSerializerMap>();
+
+} // unnamed namespace
+
+void onCannotImplicitlyCreateCollection(OperationContext* opCtx, const NamespaceString& ns) {
+ auto& handlerMap = createCollectionSerializerMap(opCtx->getServiceContext());
+ handlerMap.getForNs(ns)->onCannotImplicitlyCreateCollection(opCtx);
+ handlerMap.cleanupNs(ns);
+}
+
+} // namespace mongo
diff --git a/src/mongo/db/s/implicit_create_collection.h b/src/mongo/db/s/implicit_create_collection.h
new file mode 100644
index 00000000000..0f3120572ac
--- /dev/null
+++ b/src/mongo/db/s/implicit_create_collection.h
@@ -0,0 +1,43 @@
+/**
+ * Copyright (C) 2018 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+namespace mongo {
+
+class NamespaceString;
+class OperationContext;
+
+/**
+ * Initialize this collection so it will be officially tracked in a sharded environment
+ * by sending the command to the config server to create an entry for this collection in
+ * the sharding catalog.
+ */
+void onCannotImplicitlyCreateCollection(OperationContext* opCtx, const NamespaceString& ns);
+
+} // namespace mongo
diff --git a/src/mongo/s/SConscript b/src/mongo/s/SConscript
index 250708fa8c2..c6d286efa4b 100644
--- a/src/mongo/s/SConscript
+++ b/src/mongo/s/SConscript
@@ -285,6 +285,7 @@ env.Library(
env.Library(
target='stale_config',
source=[
+ 'cannot_implicitly_create_collection_info.cpp',
'stale_exception.cpp',
],
LIBDEPS=[
diff --git a/src/mongo/s/cannot_implicitly_create_collection_info.cpp b/src/mongo/s/cannot_implicitly_create_collection_info.cpp
new file mode 100644
index 00000000000..5beb1701e85
--- /dev/null
+++ b/src/mongo/s/cannot_implicitly_create_collection_info.cpp
@@ -0,0 +1,49 @@
+/**
+ * Copyright (C) 2018 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include <mongo/platform/basic.h>
+
+#include "mongo/s/cannot_implicitly_create_collection_info.h"
+
+#include "mongo/base/init.h"
+
+namespace mongo {
+
+MONGO_INIT_REGISTER_ERROR_EXTRA_INFO(CannotImplicitlyCreateCollectionInfo);
+
+void CannotImplicitlyCreateCollectionInfo::serialize(BSONObjBuilder* bob) const {
+ bob->append("ns", _ns.ns());
+}
+
+std::shared_ptr<const ErrorExtraInfo> CannotImplicitlyCreateCollectionInfo::parse(
+ const BSONObj& obj) {
+ NamespaceString ns(obj["ns"].str());
+ return std::make_shared<CannotImplicitlyCreateCollectionInfo>(std::move(ns));
+}
+
+} // namespace mongo
diff --git a/src/mongo/s/cannot_implicitly_create_collection_info.h b/src/mongo/s/cannot_implicitly_create_collection_info.h
new file mode 100644
index 00000000000..0978f838e21
--- /dev/null
+++ b/src/mongo/s/cannot_implicitly_create_collection_info.h
@@ -0,0 +1,59 @@
+/**
+* Copyright (C) 2018 MongoDB, Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*
+* As a special exception, the copyright holders give permission to link the
+* code of portions of this program with the OpenSSL library under certain
+* conditions as described in each individual source file and distribute
+* linked combinations including the program with the OpenSSL library. You
+* must comply with the GNU Affero General Public License in all respects
+* for all of the code used other than as permitted herein. If you modify
+* file(s) with this exception, you may extend this exception to your
+* version of the file(s), but you are not obligated to do so. If you do not
+* wish to do so, delete this exception statement from your version. If you
+* delete this exception statement from all source files in the program,
+* then also delete it in the license file.
+*/
+
+#pragma once
+
+#include "mongo/base/error_codes.h"
+#include "mongo/db/jsobj.h"
+#include "mongo/db/namespace_string.h"
+#include "mongo/util/assert_util.h"
+//#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+
+class CannotImplicitlyCreateCollectionInfo final : public ErrorExtraInfo {
+public:
+ static constexpr auto code = ErrorCodes::CannotImplicitlyCreateCollection;
+
+ CannotImplicitlyCreateCollectionInfo(NamespaceString ns) : _ns(std::move(ns)) {}
+
+ NamespaceString getNss() const {
+ return _ns;
+ }
+
+ void serialize(BSONObjBuilder* bob) const final;
+ static std::shared_ptr<const ErrorExtraInfo> parse(const BSONObj&);
+
+private:
+ NamespaceString _ns;
+};
+
+using ImplicitCreateCollectionException =
+ ExceptionFor<ErrorCodes::CannotImplicitlyCreateCollection>;
+
+} // namespace mongo
diff --git a/src/mongo/s/commands/cluster_write_cmd.cpp b/src/mongo/s/commands/cluster_write_cmd.cpp
index 9ea306bd545..516cc2d7b82 100644
--- a/src/mongo/s/commands/cluster_write_cmd.cpp
+++ b/src/mongo/s/commands/cluster_write_cmd.cpp
@@ -196,7 +196,12 @@ public:
bool enhancedRun(OperationContext* opCtx,
const OpMsgRequest& request,
BSONObjBuilder& result) final {
- const auto batchedRequest(parseRequest(_writeType, request));
+ auto batchedRequest(parseRequest(_writeType, request));
+
+ auto db = batchedRequest.getNS().db();
+ if (db != NamespaceString::kAdminDb && db != NamespaceString::kConfigDb) {
+ batchedRequest.setAllowImplicitCreate(false);
+ }
BatchWriteExecStats stats;
BatchedCommandResponse response;
diff --git a/src/mongo/s/request_types/create_collection.idl b/src/mongo/s/request_types/create_collection.idl
index e34f5b2e55c..d7257f1d599 100644
--- a/src/mongo/s/request_types/create_collection.idl
+++ b/src/mongo/s/request_types/create_collection.idl
@@ -43,4 +43,5 @@ structs:
description: "The namespace of the collection to be created."
options:
type: object
+ optional: true
description: "collection creation options"
diff --git a/src/mongo/s/write_ops/batch_write_exec.cpp b/src/mongo/s/write_ops/batch_write_exec.cpp
index 9ee10f364b3..f2882370c56 100644
--- a/src/mongo/s/write_ops/batch_write_exec.cpp
+++ b/src/mongo/s/write_ops/batch_write_exec.cpp
@@ -261,6 +261,7 @@ void BatchWriteExec::executeBatch(OperationContext* opCtx,
if (responseStatus.isOK()) {
TrackedErrors trackedErrors;
trackedErrors.startTracking(ErrorCodes::StaleShardVersion);
+ trackedErrors.startTracking(ErrorCodes::CannotImplicitlyCreateCollection);
LOG(4) << "Write results received from " << shardHost.toString() << ": "
<< redact(batchedCommandResponse.toString());
@@ -276,6 +277,14 @@ void BatchWriteExec::executeBatch(OperationContext* opCtx,
++stats->numStaleBatches;
}
+ const auto& cannotImplicitlyCreateErrors =
+ trackedErrors.getErrors(ErrorCodes::CannotImplicitlyCreateCollection);
+ if (!cannotImplicitlyCreateErrors.empty()) {
+ // This forces the chunk manager to reload so we can attach the correct
+ // version on retry and make sure we route to the correct shard.
+ targeter.noteCouldNotTarget();
+ }
+
// Remember that we successfully wrote to this shard
// NOTE: This will record lastOps for shards where we actually didn't update
// or delete any documents, which preserves old behavior but is conservative
diff --git a/src/mongo/s/write_ops/batch_write_op.cpp b/src/mongo/s/write_ops/batch_write_op.cpp
index 4c1aacff063..7948a7a9cae 100644
--- a/src/mongo/s/write_ops/batch_write_op.cpp
+++ b/src/mongo/s/write_ops/batch_write_op.cpp
@@ -451,6 +451,8 @@ BatchedCommandRequest BatchWriteOp::buildBatchRequest(
}
}
+ request.setAllowImplicitCreate(_clientRequest.isImplicitCreateAllowed());
+
return request;
}
diff --git a/src/mongo/s/write_ops/batched_command_request.cpp b/src/mongo/s/write_ops/batched_command_request.cpp
index 2f7c9fcd269..087018b7829 100644
--- a/src/mongo/s/write_ops/batched_command_request.cpp
+++ b/src/mongo/s/write_ops/batched_command_request.cpp
@@ -36,6 +36,7 @@ namespace mongo {
namespace {
const auto kWriteConcern = "writeConcern"_sd;
+const auto kAllowImplicitCollectionCreation = "allowImplicitCollectionCreation"_sd;
template <class T>
BatchedCommandRequest constructBatchedCommandRequest(const OpMsgRequest& request) {
@@ -51,6 +52,10 @@ BatchedCommandRequest constructBatchedCommandRequest(const OpMsgRequest& request
batchRequest.setWriteConcern(writeConcernField.Obj());
}
+ if (auto allowImplicitElement = request.body[kAllowImplicitCollectionCreation]) {
+ batchRequest.setAllowImplicitCreate(allowImplicitElement.boolean());
+ }
+
return batchRequest;
}
@@ -161,6 +166,8 @@ void BatchedCommandRequest::serialize(BSONObjBuilder* builder) const {
if (_writeConcern) {
builder->append(kWriteConcern, *_writeConcern);
}
+
+ builder->append(kAllowImplicitCollectionCreation, _allowImplicitCollectionCreation);
}
BSONObj BatchedCommandRequest::toBSON() const {
diff --git a/src/mongo/s/write_ops/batched_command_request.h b/src/mongo/s/write_ops/batched_command_request.h
index 5c416679060..02236e203ce 100644
--- a/src/mongo/s/write_ops/batched_command_request.h
+++ b/src/mongo/s/write_ops/batched_command_request.h
@@ -127,6 +127,14 @@ public:
BSONObj toBSON() const;
std::string toString() const;
+ void setAllowImplicitCreate(bool doAllow) {
+ _allowImplicitCollectionCreation = doAllow;
+ }
+
+ bool isImplicitCreateAllowed() const {
+ return _allowImplicitCollectionCreation;
+ }
+
/**
* Generates a new request, the same as the old, but with insert _ids if required.
*/
@@ -142,6 +150,7 @@ private:
boost::optional<ChunkVersion> _shardVersion;
boost::optional<BSONObj> _writeConcern;
+ bool _allowImplicitCollectionCreation = true;
};
/**
diff --git a/src/mongo/s/write_ops/batched_command_request_test.cpp b/src/mongo/s/write_ops/batched_command_request_test.cpp
index 33f64fb0592..437655463a0 100644
--- a/src/mongo/s/write_ops/batched_command_request_test.cpp
+++ b/src/mongo/s/write_ops/batched_command_request_test.cpp
@@ -47,7 +47,9 @@ TEST(BatchedCommandRequest, BasicInsert) {
<< "writeConcern"
<< BSON("w" << 1)
<< "ordered"
- << true);
+ << true
+ << "allowImplicitCollectionCreation"
+ << false);
for (auto docSeq : {false, true}) {
const auto opMsgRequest(toOpMsg("TestDB", origInsertRequestObj, docSeq));
@@ -55,6 +57,7 @@ TEST(BatchedCommandRequest, BasicInsert) {
ASSERT_EQ("TestDB.test", insertRequest.getInsertRequest().getNamespace().ns());
ASSERT(!insertRequest.hasShardVersion());
+ ASSERT_FALSE(insertRequest.isImplicitCreateAllowed());
}
}
diff --git a/src/mongo/s/write_ops/write_op.cpp b/src/mongo/s/write_ops/write_op.cpp
index 474d33854cd..b125f35a970 100644
--- a/src/mongo/s/write_ops/write_op.cpp
+++ b/src/mongo/s/write_ops/write_op.cpp
@@ -117,7 +117,8 @@ size_t WriteOp::getNumTargeted() {
}
static bool isRetryErrCode(int errCode) {
- return errCode == ErrorCodes::StaleShardVersion;
+ return errCode == ErrorCodes::StaleShardVersion ||
+ errCode == ErrorCodes::CannotImplicitlyCreateCollection;
}
// Aggregate a bunch of errors for a single op together