summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjannaerin <golden.janna@gmail.com>2021-12-20 17:03:13 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-12-20 18:26:25 +0000
commitb53fc88485f274d9b20cc93b53058f77d8859d93 (patch)
tree3768f4a4a5a382df1b594cf690fa4214285f57e4
parent3b11d6fa60ff69f9ae52a90690cd05404625284e (diff)
downloadmongo-b53fc88485f274d9b20cc93b53058f77d8859d93.tar.gz
SERVER-61983 Create TenantNamespace class
-rw-r--r--src/mongo/db/SConscript3
-rw-r--r--src/mongo/db/tenant_namespace.h161
-rw-r--r--src/mongo/db/tenant_namespace_test.cpp138
3 files changed, 302 insertions, 0 deletions
diff --git a/src/mongo/db/SConscript b/src/mongo/db/SConscript
index 5bf18e77cb8..2becfdb5dfc 100644
--- a/src/mongo/db/SConscript
+++ b/src/mongo/db/SConscript
@@ -2485,6 +2485,7 @@ if wiredtiger:
'session_catalog_mongod_test.cpp',
'session_catalog_test.cpp',
'startup_warnings_mongod_test.cpp',
+ 'tenant_namespace_test.cpp',
'thread_client_test.cpp',
'time_proof_service_test.cpp',
'transaction_api_test.cpp',
@@ -2541,6 +2542,7 @@ if wiredtiger:
'logical_session_id_helpers',
'logical_time',
'mirror_maestro',
+ 'multitenancy_params',
'namespace_string',
'op_observer',
'op_observer_impl',
@@ -2558,6 +2560,7 @@ if wiredtiger:
'repl/storage_interface_impl',
'rw_concern_d',
's/shard_server_test_fixture',
+ 'server_feature_flags',
'server_options_core',
'server_options_servers',
'service_context',
diff --git a/src/mongo/db/tenant_namespace.h b/src/mongo/db/tenant_namespace.h
new file mode 100644
index 00000000000..080a12c5ead
--- /dev/null
+++ b/src/mongo/db/tenant_namespace.h
@@ -0,0 +1,161 @@
+/**
+ * Copyright (C) 2021-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * 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
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * 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 Server Side 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 <algorithm>
+#include <boost/optional.hpp>
+#include <iosfwd>
+#include <string>
+
+#include "mongo/base/string_data.h"
+#include "mongo/bson/util/builder.h"
+#include "mongo/db/multitenancy_gen.h"
+#include "mongo/db/namespace_string.h"
+#include "mongo/db/server_feature_flags_gen.h"
+#include "mongo/logv2/log_attr.h"
+
+namespace mongo {
+
+class TenantNamespace {
+public:
+ /**
+ * Constructs an empty TenantNamespace.
+ */
+ TenantNamespace() : _tenantId(boost::none), _nss(), _tenantNsStr() {}
+
+ /**
+ * Constructs a TenantNamespace from the given tenantId and NamespaceString.
+ *
+ * If featureFlagRequireTenantID is set, tenantId is required.
+ */
+ TenantNamespace(boost::optional<mongo::OID> tenantId, NamespaceString nss) {
+ // TODO SERVER-62114 Check instead if gMultitenancySupport is enabled.
+ if (gFeatureFlagRequireTenantID.isEnabledAndIgnoreFCV())
+ invariant(tenantId);
+
+ _tenantId = tenantId;
+ _nss = nss;
+ _tenantNsStr = _tenantId
+ ? boost::make_optional(_tenantId->toString() + "_" + _nss.toString())
+ : boost::none;
+ }
+
+ /**
+ * Constructs a TenantNamespace from the string "ns". When the server parameter
+ * "multitenancySupport” is enabled, the tenantId will be parsed separately from the database
+ * name. If it is disabled, the tenantId will be parsed as a prefix of the database name, and
+ * the tenantId field will be empty. For example:
+ * if “multitenancySupport” is enabled, "tenant1_dbA.collA" will be parsed as:
+ * _tenantId = tenant1
+ * _nss = NamespaceString(dbA.collA)
+ *
+ * if “multitenancySupport” is disabled, "tenant1_dbA.collA" will be parsed as:
+ * _tenantId = boost::none
+ * _nss = NamespaceString(tenant1_dbA.collA), and the _nss,db()
+ *
+ * This method should only be used when reading a namespace from disk. To construct a
+ * TenantNamespace otherwise, use the standard constructor above.
+ *
+ * If featureFlagRequireTenantID is set, tenantId is required.
+ */
+ static TenantNamespace parseTenantNamespaceFromDisk(StringData ns) {
+ if (!gMultitenancySupport) {
+ return TenantNamespace(boost::none, NamespaceString(ns));
+ }
+
+ auto tenantDelim = ns.find('_');
+ if (tenantDelim == std::string::npos)
+ return TenantNamespace(boost::none, NamespaceString(ns));
+
+ auto tenantId = OID(ns.substr(0, tenantDelim));
+ auto nss = NamespaceString(ns.substr(tenantDelim + 1, ns.size() - 1 - tenantDelim));
+ return TenantNamespace(tenantId, nss);
+ }
+
+ boost::optional<mongo::OID> tenantId() const {
+ return _tenantId;
+ }
+
+ StringData db() const {
+ return _nss.db();
+ }
+
+ StringData coll() const {
+ return _nss.coll();
+ }
+
+ NamespaceString getNss() const {
+ return _nss;
+ }
+
+ std::string toString() const {
+ if (_tenantNsStr)
+ return *_tenantNsStr;
+
+ invariant(!_tenantId);
+ return _nss.ns();
+ }
+
+ // Relops among `TenantNamespace`.
+ friend bool operator==(const TenantNamespace& a, const TenantNamespace& b) {
+ return a.toString() == b.toString();
+ }
+ friend bool operator!=(const TenantNamespace& a, const TenantNamespace& b) {
+ return a.toString() != b.toString();
+ }
+ friend bool operator<(const TenantNamespace& a, const TenantNamespace& b) {
+ return a.toString() < b.toString();
+ }
+ friend bool operator>(const TenantNamespace& a, const TenantNamespace& b) {
+ return a.toString() > b.toString();
+ }
+ friend bool operator<=(const TenantNamespace& a, const TenantNamespace& b) {
+ return a.toString() <= b.toString();
+ }
+ friend bool operator>=(const TenantNamespace& a, const TenantNamespace& b) {
+ return a.toString() >= b.toString();
+ }
+
+ template <typename H>
+ friend H AbslHashValue(H h, const TenantNamespace& tenantNs) {
+ return H::combine(std::move(h), tenantNs.toString());
+ }
+
+ friend auto logAttrs(const TenantNamespace& nss) {
+ return "tenantNamespace"_attr = nss;
+ }
+
+private:
+ boost::optional<mongo::OID> _tenantId;
+ NamespaceString _nss;
+ boost::optional<std::string> _tenantNsStr; // Only set if _tenantId exists
+};
+
+} // namespace mongo
diff --git a/src/mongo/db/tenant_namespace_test.cpp b/src/mongo/db/tenant_namespace_test.cpp
new file mode 100644
index 00000000000..d3f3f418ba5
--- /dev/null
+++ b/src/mongo/db/tenant_namespace_test.cpp
@@ -0,0 +1,138 @@
+/**
+ * Copyright (C) 2021-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * 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
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * 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 Server Side 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/db/multitenancy_gen.h"
+#include "mongo/db/server_feature_flags_gen.h"
+#include "mongo/db/tenant_namespace.h"
+#include "mongo/idl/server_parameter_test_util.h"
+#include "mongo/unittest/death_test.h"
+#include "mongo/unittest/unittest.h"
+
+namespace mongo {
+namespace {
+
+TEST(TenantNamespaceTest, TenantNamespaceMultitenancySupportDisabledBasic) {
+ TenantNamespace tenantNs(boost::none, NamespaceString("a.b"));
+ ASSERT(!tenantNs.tenantId());
+ ASSERT_EQUALS(std::string("a"), tenantNs.db());
+ ASSERT_EQUALS(std::string("b"), tenantNs.coll());
+ ASSERT_EQUALS(std::string("a.b"), tenantNs.toString());
+}
+
+TEST(TenantNamespaceTest, TenantNamespaceParseFromDiskMultitenancySupportDisabled) {
+ TenantNamespace tenantNs = TenantNamespace::parseTenantNamespaceFromDisk("a.b");
+ ASSERT(!tenantNs.tenantId());
+ ASSERT_EQUALS(std::string("a"), tenantNs.db());
+ ASSERT_EQUALS(std::string("b"), tenantNs.coll());
+
+ mongo::OID tenantId = OID::gen();
+ std::string ns = tenantId.toString() + "_a.b";
+ TenantNamespace tenantNs2 = TenantNamespace::parseTenantNamespaceFromDisk(ns);
+ ASSERT(!tenantNs2.tenantId());
+ ASSERT_EQUALS(std::string(tenantId.toString() + "_a"), tenantNs2.db());
+ ASSERT_EQUALS(std::string("b"), tenantNs2.coll());
+}
+
+TEST(TenantNamespaceTest, TenantNamespaceMultitenancySupportEnabledFeatureFlagDisabledBasic) {
+ // TODO SERVER-62114 Remove this test case.
+ RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true);
+
+ // If the feature flag is disabled, it's acceptable for the tenantId not to exist.
+ TenantNamespace tenantNs(boost::none, NamespaceString("a.b"));
+ ASSERT(!tenantNs.tenantId());
+ ASSERT_EQUALS(std::string("a"), tenantNs.db());
+ ASSERT_EQUALS(std::string("b"), tenantNs.coll());
+ ASSERT_EQUALS(std::string("a.b"), tenantNs.toString());
+
+ // If the feature flag is disabled but a tenantId is given, the tenantId should be parsed
+ // separately from the db name.
+ mongo::OID tenantId = OID::gen();
+ TenantNamespace tenantNs2(tenantId, NamespaceString("a.b"));
+ ASSERT(tenantNs2.tenantId());
+ ASSERT_EQUALS(tenantId, *tenantNs2.tenantId());
+ ASSERT_EQUALS(std::string("a"), tenantNs2.db());
+ ASSERT_EQUALS(std::string("b"), tenantNs2.coll());
+ ASSERT_EQUALS(std::string(tenantId.toString() + "_a.b"), tenantNs2.toString());
+}
+
+DEATH_TEST(TenantNamespaceTest,
+ TenantNamespaceMultitenancySupportEnabledTenantIDRequired,
+ "invariant") {
+ RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true);
+ // TODO SERVER-62114 Remove enabling this feature flag.
+ RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true);
+
+ // A tenantId is not included, so the server should crash
+ TenantNamespace(boost::none, NamespaceString("a.b"));
+}
+
+DEATH_TEST(TenantNamespaceTest,
+ TenantNamespaceParseFromDiskMultitenancySupportEnabledTenantIDRequired,
+ "invariant") {
+ RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true);
+ // TODO SERVER-62114 Remove enabling this feature flag.
+ RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true);
+
+ // A tenantId is not included, so the server should crash
+ auto tenantNs = TenantNamespace::parseTenantNamespaceFromDisk("a.b");
+}
+
+TEST(TenantNamespaceTest, TenantNamespaceMultitenancySupportEnabledBasic) {
+ RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true);
+ // TODO SERVER-62114 Remove enabling this feature flag.
+ RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true);
+
+ mongo::OID tenantId = OID::gen();
+ TenantNamespace tenantNs(tenantId, NamespaceString("a.b"));
+ ASSERT(tenantNs.tenantId());
+ ASSERT_EQUALS(tenantId, *tenantNs.tenantId());
+ ASSERT_EQUALS(std::string("a"), tenantNs.db());
+ ASSERT_EQUALS(std::string("b"), tenantNs.coll());
+ ASSERT_EQUALS(std::string(tenantId.toString() + "_a.b"), tenantNs.toString());
+}
+
+TEST(TenantNamespaceTest, TenantNamespaceParseFromDiskMultitenancySupportEnabled) {
+ RAIIServerParameterControllerForTest multitenanyController("multitenancySupport", true);
+ // TODO SERVER-62114 Remove enabling this feature flag.
+ RAIIServerParameterControllerForTest featureFlagController("featureFlagRequireTenantID", true);
+
+ mongo::OID tenantId = OID::gen();
+ std::string tenantNsStr = str::stream() << tenantId << "_a.b";
+
+ TenantNamespace tenantNs = TenantNamespace::parseTenantNamespaceFromDisk(tenantNsStr);
+ ASSERT(tenantNs.tenantId());
+ ASSERT_EQUALS(tenantId, *tenantNs.tenantId());
+ ASSERT_EQUALS(std::string("a"), tenantNs.db());
+ ASSERT_EQUALS(std::string("b"), tenantNs.coll());
+}
+
+} // namespace
+} // namespace mongo