diff options
author | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2018-04-10 13:29:16 -0400 |
---|---|---|
committer | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2018-04-10 13:29:16 -0400 |
commit | b9cbfd8fd3cc8438a3e8e3986505f348416f5da4 (patch) | |
tree | c1b602f7ebc359f026e8120510ca86418b6514c1 /src/mongo/db/free_mon | |
parent | 2dc3299c530e41f05c032e8eb42a5b3b8a14f93e (diff) | |
download | mongo-b9cbfd8fd3cc8438a3e8e3986505f348416f5da4.tar.gz |
SERVER-34224 Implement FreeMonStorage Standalone and IDL for storage
Diffstat (limited to 'src/mongo/db/free_mon')
-rw-r--r-- | src/mongo/db/free_mon/SConscript | 3 | ||||
-rw-r--r-- | src/mongo/db/free_mon/free_mon_storage.cpp | 135 | ||||
-rw-r--r-- | src/mongo/db/free_mon/free_mon_storage.h | 67 | ||||
-rw-r--r-- | src/mongo/db/free_mon/free_mon_storage.idl | 57 | ||||
-rw-r--r-- | src/mongo/db/free_mon/free_mon_storage_test.cpp | 191 |
5 files changed, 453 insertions, 0 deletions
diff --git a/src/mongo/db/free_mon/SConscript b/src/mongo/db/free_mon/SConscript index 0064e28495d..6a2b57d4a05 100644 --- a/src/mongo/db/free_mon/SConscript +++ b/src/mongo/db/free_mon/SConscript @@ -11,6 +11,8 @@ fmEnv.Library( target='free_mon', source=[ 'free_mon_queue.cpp', + 'free_mon_storage.cpp', + env.Idlc('free_mon_storage.idl')[0], ], LIBDEPS_PRIVATE=[ '$BUILD_DIR/mongo/base', @@ -26,6 +28,7 @@ env.CppUnitTest( target='free_mon_test', source=[ 'free_mon_queue_test.cpp', + 'free_mon_storage_test.cpp', ], LIBDEPS=[ '$BUILD_DIR/mongo/db/repl/replmocks', diff --git a/src/mongo/db/free_mon/free_mon_storage.cpp b/src/mongo/db/free_mon/free_mon_storage.cpp new file mode 100644 index 00000000000..627ad076424 --- /dev/null +++ b/src/mongo/db/free_mon/free_mon_storage.cpp @@ -0,0 +1,135 @@ +/** + * 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/db/free_mon/free_mon_storage.h" + +#include "mongo/base/status.h" +#include "mongo/base/string_data.h" +#include "mongo/bson/bsonelement.h" +#include "mongo/bson/bsonmisc.h" +#include "mongo/db/concurrency/d_concurrency.h" +#include "mongo/db/concurrency/lock_manager_defs.h" +#include "mongo/db/namespace_string.h" +#include "mongo/db/operation_context.h" +#include "mongo/db/repl/storage_interface.h" +#include "mongo/util/assert_util.h" + +namespace mongo { + +namespace { + +static const NamespaceString adminSystemVersionNss("admin.system.version"); +constexpr auto kFreeMonDocIdKey = "free_monitoring"; + +// mms-automation stores its document in local.clustermanager +static const NamespaceString localClusterManagerNss("local.clustermanager"); + +} // namespace + +boost::optional<FreeMonStorageState> FreeMonStorage::read(OperationContext* opCtx) { + BSONObj deleteKey = BSON("_id" << kFreeMonDocIdKey); + BSONElement elementKey = deleteKey.firstElement(); + + auto storageInterface = repl::StorageInterface::get(opCtx); + + Lock::DBLock dblk(opCtx, adminSystemVersionNss.db(), MODE_IS); + Lock::CollectionLock lk(opCtx->lockState(), adminSystemVersionNss.ns(), MODE_IS); + + auto swObj = storageInterface->findById(opCtx, adminSystemVersionNss, elementKey); + if (!swObj.isOK()) { + if (swObj.getStatus() == ErrorCodes::NoSuchKey) { + return {}; + } + + uassertStatusOK(swObj.getStatus()); + } + + return FreeMonStorageState::parse(IDLParserErrorContext("FreeMonStorage"), swObj.getValue()); +} + +void FreeMonStorage::replace(OperationContext* opCtx, const FreeMonStorageState& doc) { + BSONObj deleteKey = BSON("_id" << kFreeMonDocIdKey); + BSONElement elementKey = deleteKey.firstElement(); + + BSONObj obj = doc.toBSON(); + + auto storageInterface = repl::StorageInterface::get(opCtx); + { + Lock::DBLock dblk(opCtx, adminSystemVersionNss.db(), MODE_IS); + Lock::CollectionLock lk(opCtx->lockState(), adminSystemVersionNss.ns(), MODE_IS); + + auto swObj = storageInterface->upsertById(opCtx, adminSystemVersionNss, elementKey, obj); + if (!swObj.isOK()) { + uassertStatusOK(swObj); + } + } +} + +void FreeMonStorage::deleteState(OperationContext* opCtx) { + BSONObj deleteKey = BSON("_id" << kFreeMonDocIdKey); + BSONElement elementKey = deleteKey.firstElement(); + + auto storageInterface = repl::StorageInterface::get(opCtx); + { + Lock::DBLock dblk(opCtx, adminSystemVersionNss.db(), MODE_IS); + Lock::CollectionLock lk(opCtx->lockState(), adminSystemVersionNss.ns(), MODE_IS); + + auto swObj = storageInterface->deleteById(opCtx, adminSystemVersionNss, elementKey); + if (!swObj.isOK()) { + // Ignore errors about no document + if (swObj.getStatus() == ErrorCodes::NoSuchKey) { + return; + } + + uassertStatusOK(swObj); + } + } +} + +boost::optional<BSONObj> FreeMonStorage::readClusterManagerState(OperationContext* opCtx) { + auto storageInterface = repl::StorageInterface::get(opCtx); + + Lock::DBLock dblk(opCtx, adminSystemVersionNss.db(), MODE_IS); + Lock::CollectionLock lk(opCtx->lockState(), adminSystemVersionNss.ns(), MODE_IS); + + auto swObj = storageInterface->findSingleton(opCtx, localClusterManagerNss); + if (!swObj.isOK()) { + // Ignore errors about not-finding documents or having too many documents + if (swObj.getStatus() == ErrorCodes::NamespaceNotFound || + swObj.getStatus() == ErrorCodes::CollectionIsEmpty || + swObj.getStatus() == ErrorCodes::TooManyMatchingDocuments) { + return {}; + } + + uassertStatusOK(swObj.getStatus()); + } + + return swObj.getValue(); +} + +} // namespace mongo diff --git a/src/mongo/db/free_mon/free_mon_storage.h b/src/mongo/db/free_mon/free_mon_storage.h new file mode 100644 index 00000000000..fd7d81e68bf --- /dev/null +++ b/src/mongo/db/free_mon/free_mon_storage.h @@ -0,0 +1,67 @@ +/** + * 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 <boost/optional.hpp> + +#include "mongo/bson/bsonobj.h" +#include "mongo/db/free_mon/free_mon_storage_gen.h" +#include "mongo/db/operation_context.h" + +namespace mongo { + +/** + * Storage tier for Free Monitoring. Provides access to storage engine. + */ +class FreeMonStorage { +public: + /** + * Reads document from disk if it exists. + */ + static boost::optional<FreeMonStorageState> read(OperationContext* opCtx); + + /** + * Replaces document on disk with contents of document. Creates document if it does not exist. + */ + static void replace(OperationContext* opCtx, const FreeMonStorageState& doc); + + /** + * Deletes document on disk if it exists. + */ + static void deleteState(OperationContext* opCtx); + + /** + * Reads the singelton document from local.clustermanager. + * + * Returns nothing if there are more then one document or it does not exist. + */ + static boost::optional<BSONObj> readClusterManagerState(OperationContext* opCtx); +}; + +} // namespace mongo diff --git a/src/mongo/db/free_mon/free_mon_storage.idl b/src/mongo/db/free_mon/free_mon_storage.idl new file mode 100644 index 00000000000..a181d39f23a --- /dev/null +++ b/src/mongo/db/free_mon/free_mon_storage.idl @@ -0,0 +1,57 @@ +# Copyright (C) 2017 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/>. +# +global: + cpp_namespace: "mongo" + +imports: + - "mongo/idl/basic_types.idl" + +enums: + StorageState: + description: "Action types" + type: string + values: + disabled: disabled + enabled: enabled + +structs: + FreeMonStorageState: + description: "Persisted document in admin.system.version" + strict: false + generate_comparison_operators: true + fields: + _id: + description: "Key of the Free Monitoring singleton document" + type: "string" + default: '"free_monitoring"' + version: + description: "Storage version, initial version is 1" + type: long + state: + description: "Indicates whether it is disabled or enabled" + type: StorageState + registrationId: + description: "Registration Id" + type: string + informationalURL: + description: "Informational HTTP web page for metrics" + type: string + message: + description: "Informational message for shell to display to user" + type: string + userReminder: + description: "Message to display to user to remind them about service" + type: string + diff --git a/src/mongo/db/free_mon/free_mon_storage_test.cpp b/src/mongo/db/free_mon/free_mon_storage_test.cpp new file mode 100644 index 00000000000..550691f2a24 --- /dev/null +++ b/src/mongo/db/free_mon/free_mon_storage_test.cpp @@ -0,0 +1,191 @@ +/** + * 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/base/string_data.h" +#include "mongo/bson/bsonelement.h" +#include "mongo/bson/bsonmisc.h" +#include "mongo/db/catalog/collection_options.h" +#include "mongo/db/concurrency/d_concurrency.h" +#include "mongo/db/concurrency/lock_manager_defs.h" +#include "mongo/db/free_mon/free_mon_storage.h" +#include "mongo/db/namespace_string.h" +#include "mongo/db/repl/replication_coordinator_mock.h" +#include "mongo/db/repl/storage_interface.h" +#include "mongo/db/repl/storage_interface_impl.h" +#include "mongo/db/service_context.h" +#include "mongo/db/service_context_d_test_fixture.h" +#include "mongo/executor/network_interface_mock.h" +#include "mongo/executor/thread_pool_task_executor.h" +#include "mongo/executor/thread_pool_task_executor_test_fixture.h" +#include "mongo/unittest/unittest.h" +#include "mongo/util/uuid.h" + +namespace mongo { +namespace { + +class FreeMonStorageTest : public ServiceContextMongoDTest { +private: + void setUp() final; + void tearDown() final; + +protected: + /** + * Looks up the current ReplicationCoordinator. + * The result is cast to a ReplicationCoordinatorMock to provide access to test features. + */ + repl::ReplicationCoordinatorMock* _getReplCoord() const; + + ServiceContext::UniqueOperationContext _opCtx; + + executor::NetworkInterfaceMock* _mockNetwork{nullptr}; + + std::unique_ptr<executor::ThreadPoolTaskExecutor> _mockThreadPool; + + repl::StorageInterface* _storage{nullptr}; +}; + +void FreeMonStorageTest::setUp() { + ServiceContextMongoDTest::setUp(); + auto service = getServiceContext(); + + repl::ReplicationCoordinator::set(service, + std::make_unique<repl::ReplicationCoordinatorMock>(service)); + + _opCtx = cc().makeOperationContext(); + + repl::StorageInterface::set(service, std::make_unique<repl::StorageInterfaceImpl>()); + _storage = repl::StorageInterface::get(service); + + // Transition to PRIMARY so that the server can accept writes. + ASSERT_OK(_getReplCoord()->setFollowerMode(repl::MemberState::RS_PRIMARY)); + + // Create collection with one document. + CollectionOptions collectionOptions; + collectionOptions.uuid = UUID::gen(); + auto statusCC = _storage->createCollection( + _opCtx.get(), NamespaceString("admin", "system.version"), collectionOptions); + ASSERT_OK(statusCC); +} + +void FreeMonStorageTest::tearDown() { + _opCtx = {}; + ServiceContextMongoDTest::tearDown(); +} + +repl::ReplicationCoordinatorMock* FreeMonStorageTest::_getReplCoord() const { + auto replCoord = repl::ReplicationCoordinator::get(_opCtx.get()); + ASSERT(replCoord) << "No ReplicationCoordinator installed"; + auto replCoordMock = dynamic_cast<repl::ReplicationCoordinatorMock*>(replCoord); + ASSERT(replCoordMock) << "Unexpected type for installed ReplicationCoordinator"; + return replCoordMock; +} + +// Positive: Test Storage works +TEST_F(FreeMonStorageTest, TestStorage) { + + FreeMonStorageState initialState = + FreeMonStorageState::parse(IDLParserErrorContext("foo"), + BSON("version" << 1LL << "state" + << "enabled" + << "registrationId" + << "1234" + << "informationalURL" + << "http://example.com" + << "message" + << "hello" + << "userReminder" + << "")); + + { + auto emptyDoc = FreeMonStorage::read(_opCtx.get()); + ASSERT_FALSE(emptyDoc.is_initialized()); + } + + FreeMonStorage::replace(_opCtx.get(), initialState); + + { + auto persistedDoc = FreeMonStorage::read(_opCtx.get()); + + ASSERT_TRUE(persistedDoc.is_initialized()); + + ASSERT_TRUE(persistedDoc == initialState); + } + + FreeMonStorage::deleteState(_opCtx.get()); + + { + auto emptyDoc = FreeMonStorage::read(_opCtx.get()); + ASSERT_FALSE(emptyDoc.is_initialized()); + } + + // Verfiy delete of nothing succeeds + FreeMonStorage::deleteState(_opCtx.get()); +} + +void insertDoc(OperationContext* optCtx, const NamespaceString nss, StringData id) { + auto storageInterface = repl::StorageInterface::get(optCtx); + + Lock::DBLock dblk(optCtx, nss.db(), MODE_IS); + Lock::CollectionLock lk(optCtx->lockState(), nss.ns(), MODE_IS); + + BSONObj fakeDoc = BSON("_id" << id); + BSONElement elementKey = fakeDoc.firstElement(); + + ASSERT_OK(storageInterface->upsertById(optCtx, nss, elementKey, fakeDoc)); +} + +// Positive: Test local.clustermanager +TEST_F(FreeMonStorageTest, TestClusterManagerStorage) { + const NamespaceString localClusterManagerNss("local.clustermanager"); + + // Verify read of non-existent collection works + ASSERT_FALSE(FreeMonStorage::readClusterManagerState(_opCtx.get()).is_initialized()); + + CollectionOptions collectionOptions; + collectionOptions.uuid = UUID::gen(); + auto statusCC = + _storage->createCollection(_opCtx.get(), localClusterManagerNss, collectionOptions); + ASSERT_OK(statusCC); + + // Verify read of empty collection works + ASSERT_FALSE(FreeMonStorage::readClusterManagerState(_opCtx.get()).is_initialized()); + + insertDoc(_opCtx.get(), localClusterManagerNss, "foo1"); + + // Verify read of singleton collection works + ASSERT_TRUE(FreeMonStorage::readClusterManagerState(_opCtx.get()).is_initialized()); + + insertDoc(_opCtx.get(), localClusterManagerNss, "bar1"); + + // Verify read of two doc collection fails + ASSERT_FALSE(FreeMonStorage::readClusterManagerState(_opCtx.get()).is_initialized()); +} +} // namespace +} // namespace mongo |