summaryrefslogtreecommitdiff
path: root/src/mongo/db/free_mon/free_mon_processor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/free_mon/free_mon_processor.cpp')
-rw-r--r--src/mongo/db/free_mon/free_mon_processor.cpp1027
1 files changed, 0 insertions, 1027 deletions
diff --git a/src/mongo/db/free_mon/free_mon_processor.cpp b/src/mongo/db/free_mon/free_mon_processor.cpp
deleted file mode 100644
index c91d62204b8..00000000000
--- a/src/mongo/db/free_mon/free_mon_processor.cpp
+++ /dev/null
@@ -1,1027 +0,0 @@
-/**
- * Copyright (C) 2018-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.
- */
-
-#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kControl
-
-#include "mongo/platform/basic.h"
-
-#include "mongo/db/free_mon/free_mon_processor.h"
-
-#include <functional>
-#include <numeric>
-#include <snappy.h>
-#include <tuple>
-#include <utility>
-
-#include "mongo/base/data_range.h"
-#include "mongo/base/status.h"
-#include "mongo/base/string_data.h"
-#include "mongo/bson/bsonobj.h"
-#include "mongo/db/free_mon/free_mon_storage.h"
-#include "mongo/db/service_context.h"
-#include "mongo/idl/idl_parser.h"
-#include "mongo/logv2/log.h"
-#include "mongo/util/assert_util.h"
-
-namespace mongo {
-
-namespace {
-
-constexpr auto kMinProtocolVersion = 1;
-constexpr auto kMaxProtocolVersion = 2;
-constexpr auto kStorageVersion = 1;
-
-constexpr auto kRegistrationIdMaxLength = 4096;
-constexpr auto kInformationalURLMaxLength = 4096;
-constexpr auto kInformationalMessageMaxLength = 4096;
-constexpr auto kUserReminderMaxLength = 4096;
-
-constexpr auto kReportingIntervalSecondsMin = 1;
-constexpr auto kReportingIntervalSecondsMax = 30 * 60 * 60 * 24;
-
-constexpr auto kMetricsRequestArrayElement = "data"_sd;
-
-int64_t randomJitter(PseudoRandom& random, int64_t min, int64_t max) {
- dassert(max > min);
- return (std::abs(random.nextInt64()) % (max - min)) + min;
-}
-
-} // namespace
-
-void RegistrationRetryCounter::reset() {
- _current = _min;
- _base = _min;
- _retryCount = 0;
- _total = Hours(0);
-}
-
-bool RegistrationRetryCounter::incrementError() {
- if (_retryCount < kStage1RetryCountMax) {
- _base = 2 * _base;
- _current = _base + Seconds(randomJitter(_random, kStage1JitterMin, kStage1JitterMax));
- ++_retryCount;
- } else {
- _current = _base + Seconds(randomJitter(_random, kStage2JitterMin, kStage2JitterMax));
- }
-
- _total += _current;
-
- if (_total > kStage2DurationMax) {
- return false;
- }
-
- return true;
-}
-
-void MetricsRetryCounter::reset() {
- _current = _min;
- _base = _min;
- _retryCount = 0;
- _total = Hours(0);
-}
-
-bool MetricsRetryCounter::incrementError() {
- _base = static_cast<int>(pow(2, std::min(6, static_cast<int>(_retryCount)))) * _min;
- _current = _base + Seconds(randomJitter(_random, _min.count() / 2, _min.count()));
- ++_retryCount;
-
- _total += _current;
-
- if (_total > kDurationMax) {
- return false;
- }
-
- return true;
-}
-
-FreeMonProcessor::FreeMonProcessor(FreeMonCollectorCollection& registration,
- FreeMonCollectorCollection& metrics,
- FreeMonNetworkInterface* network,
- bool useCrankForTest,
- Seconds metricsGatherInterval)
- : _registration(registration),
- _metrics(metrics),
- _network(network),
- _random(Date_t::now().asInt64()),
- _registrationRetry(RegistrationRetryCounter(_random)),
- _metricsRetry(MetricsRetryCounter(_random)),
- _metricsGatherInterval(metricsGatherInterval),
- _queue(useCrankForTest) {
- _registrationRetry->reset();
- _metricsRetry->reset();
-}
-
-void FreeMonProcessor::enqueue(std::shared_ptr<FreeMonMessage> msg) {
- _queue.enqueue(std::move(msg));
-}
-
-void FreeMonProcessor::stop() {
- _queue.stop();
-}
-
-void FreeMonProcessor::turnCrankForTest(size_t countMessagesToIgnore) {
- _countdown.reset(countMessagesToIgnore);
-
- _queue.turnCrankForTest(countMessagesToIgnore);
-
- _countdown.wait();
-}
-
-void FreeMonProcessor::deprioritizeFirstMessageForTest(FreeMonMessageType type) {
- _queue.deprioritizeFirstMessageForTest(type);
-}
-
-void FreeMonProcessor::run() {
- try {
-
- Client::initThread("FreeMonProcessor");
- Client* client = &cc();
-
- while (true) {
- auto item = _queue.dequeue(client->getServiceContext()->getPreciseClockSource());
- if (!item.is_initialized()) {
- // Shutdown was triggered
- return;
- }
-
- auto msg = item.get();
-
- // Do work here
- switch (msg->getType()) {
- case FreeMonMessageType::RegisterCommand: {
- doCommandRegister(client, msg);
- break;
- }
- case FreeMonMessageType::RegisterServer: {
- doServerRegister(
- client,
- checked_cast<
- FreeMonMessageWithPayload<FreeMonMessageType::RegisterServer>*>(
- msg.get()));
- break;
- }
- case FreeMonMessageType::UnregisterCommand: {
- doCommandUnregister(client,
- checked_cast<FreeMonWaitableMessageWithPayload<
- FreeMonMessageType::UnregisterCommand>*>(msg.get()));
- break;
- }
- case FreeMonMessageType::AsyncRegisterComplete: {
- doAsyncRegisterComplete(
- client,
- checked_cast<
- FreeMonMessageWithPayload<FreeMonMessageType::AsyncRegisterComplete>*>(
- msg.get()));
- break;
- }
- case FreeMonMessageType::AsyncRegisterFail: {
- doAsyncRegisterFail(
- client,
- checked_cast<
- FreeMonMessageWithPayload<FreeMonMessageType::AsyncRegisterFail>*>(
- msg.get()));
- break;
- }
- case FreeMonMessageType::MetricsCollect: {
- doMetricsCollect(client);
- break;
- }
- case FreeMonMessageType::MetricsSend: {
- doMetricsSend(client);
- break;
- }
- case FreeMonMessageType::AsyncMetricsComplete: {
- doAsyncMetricsComplete(
- client,
- checked_cast<
- FreeMonMessageWithPayload<FreeMonMessageType::AsyncMetricsComplete>*>(
- msg.get()));
- break;
- }
- case FreeMonMessageType::AsyncMetricsFail: {
- doAsyncMetricsFail(
- client,
- checked_cast<
- FreeMonMessageWithPayload<FreeMonMessageType::AsyncMetricsFail>*>(
- msg.get()));
- break;
- }
- case FreeMonMessageType::OnTransitionToPrimary: {
- doOnTransitionToPrimary(client);
- break;
- }
- case FreeMonMessageType::NotifyOnUpsert: {
- doNotifyOnUpsert(
- client,
- checked_cast<
- FreeMonMessageWithPayload<FreeMonMessageType::NotifyOnUpsert>*>(
- msg.get()));
- break;
- }
- case FreeMonMessageType::NotifyOnDelete: {
- doNotifyOnDelete(client);
- break;
- }
- case FreeMonMessageType::NotifyOnRollback: {
- doNotifyOnRollback(client);
- break;
- }
- default:
- MONGO_UNREACHABLE;
- }
-
- // Record that we have finished processing the message for testing purposes.
- _countdown.countDown();
- }
- } catch (...) {
- // Stop the queue
- _queue.stop();
-
- LOGV2_WARNING(20619,
- "Uncaught exception in '{error}' in free monitoring subsystem. "
- "Shutting down the free monitoring subsystem.",
- "Uncaught exception in free monitoring subsystem. "
- "Shutting down the free monitoring subsystem.",
- "error"_attr = exceptionToStatus());
- }
-}
-
-void FreeMonProcessor::readState(OperationContext* opCtx, bool updateInMemory) {
- auto state = FreeMonStorage::read(opCtx);
-
- _lastReadState = state;
-
- if (state.is_initialized()) {
- invariant(state.get().getVersion() == kStorageVersion);
-
- if (updateInMemory) {
- _state = state.get();
- }
- } else if (!state.is_initialized()) {
- // Default the state
- auto state = _state.synchronize();
- state->setVersion(kStorageVersion);
- state->setState(StorageStateEnum::disabled);
- state->setRegistrationId("");
- state->setInformationalURL("");
- state->setMessage("");
- state->setUserReminder("");
- }
-}
-
-void FreeMonProcessor::readState(Client* client, bool updateInMemory) {
- auto opCtx = client->makeOperationContext();
- readState(opCtx.get(), updateInMemory);
-}
-
-void FreeMonProcessor::writeState(Client* client) {
-
- // Do a compare and swap
- // Verify the document is the same as the one on disk, if it is the same, then do the update
- // If the local document is different, then oh-well we do nothing, and wait until the next round
-
- // Has our in-memory state changed, if so consider writing
- if (_lastReadState != _state.get()) {
-
- // The read and write are bound the same operation context
- {
- auto optCtx = client->makeOperationContext();
-
- auto state = FreeMonStorage::read(optCtx.get());
-
- // If our in-memory copy matches the last read, then write it to disk
- if (state == _lastReadState) {
- FreeMonStorage::replace(optCtx.get(), _state.get());
-
- _lastReadState = boost::make_optional(_state.get());
- }
- }
- }
-}
-
-void FreeMonProcessor::doServerRegister(
- Client* client, const FreeMonMessageWithPayload<FreeMonMessageType::RegisterServer>* msg) {
-
- // Enqueue the first metrics gather first so we have something to send on intial registration
- enqueue(FreeMonMessage::createNow(FreeMonMessageType::MetricsCollect));
-
- // If we are asked to register now, then kick off a registration request
- const auto regType = msg->getPayload().first;
- if (regType == RegistrationType::RegisterOnStart) {
- enqueue(FreeMonRegisterCommandMessage::createNow({msg->getPayload().second, boost::none}));
- } else {
- invariant((regType == RegistrationType::RegisterAfterOnTransitionToPrimary) ||
- (regType == RegistrationType::RegisterAfterOnTransitionToPrimaryIfEnabled));
- // Check if we need to wait to become primary:
- // If the 'admin.system.version' has content, do not wait and just re-register
- // If the collection is empty, wait until we become primary
- // If we become secondary, OpObserver hooks will tell us our registration id
-
- auto optCtx = client->makeOperationContext();
-
- // Check if there is an existing document
- auto state = FreeMonStorage::read(optCtx.get());
-
- // If there is no document, we may be:
- // 1. in a replica set and may need to register after becoming primary since we cannot
- // record the registration id until after becoming primary
- // 2. a standalone which has never been registered
- //
- if (!state.is_initialized()) {
- _registerOnTransitionToPrimary = regType;
- } else {
- // We are standalone or secondary, if we have a registration id, then send a
- // registration notification, else wait for the user to register us.
- if (state.get().getState() == StorageStateEnum::enabled) {
- enqueue(FreeMonRegisterCommandMessage::createNow(
- {msg->getPayload().second, boost::none}));
- }
- }
-
- // Ensure we read the state once.
- // This is important on a disabled secondary so that the in-memory state knows we are
- // disabled.
- readState(optCtx.get());
- }
-}
-
-namespace {
-template <typename T>
-std::unique_ptr<Future<void>> doAsyncCallback(FreeMonProcessor* proc,
- Future<T> future,
- std::function<void(const T&)> onSuccess,
- std::function<void(Status)> onErrorFunc) {
-
- // Grab a weak_ptr to be sure that FreeMonProcessor is alive during the callback
- std::weak_ptr<FreeMonProcessor> wpProc(proc->shared_from_this());
-
- auto spError = std::make_shared<bool>(false);
-
- return std::make_unique<Future<void>>(std::move(future)
- .onError([=](Status s) {
- *(spError.get()) = true;
- if (auto spProc = wpProc.lock()) {
- onErrorFunc(s);
- }
-
- return T();
- })
- .then([=](const auto& resp) {
- // If we hit an error, then do not call onSuccess
- if (*(spError.get()) == true) {
- return;
- }
-
- // Use a shared pointer here because the callback
- // could return after we disappear
- if (auto spProc = wpProc.lock()) {
- onSuccess(resp);
- }
- }));
-}
-} // namespace
-
-void FreeMonProcessor::doCommandRegister(Client* client,
- std::shared_ptr<FreeMonMessage> sharedMsg) {
- auto msg = checked_cast<FreeMonRegisterCommandMessage*>(sharedMsg.get());
-
- if (_futureRegistrationResponse) {
- msg->setStatus(Status(ErrorCodes::FreeMonHttpInFlight,
- "Free Monitoring Registration request in-flight already"));
- return;
- }
-
- _pendingRegisters.push_back(sharedMsg);
-
- readState(client);
-
- FreeMonRegistrationRequest req;
-
- if (msg->getPayload().second) {
- req.setId(StringData(msg->getPayload().second.get()));
- } else {
- auto regid = _state->getRegistrationId();
- if (!regid.empty()) {
- req.setId(regid);
- }
- }
-
- req.setVersion(kMaxProtocolVersion);
-
- req.setLocalTime(client->getServiceContext()->getPreciseClockSource()->now());
-
- if (!msg->getPayload().first.empty()) {
- // Cache the tags for subsequent retries
- _tags = msg->getPayload().first;
- }
-
- if (!_tags.empty()) {
- req.setTags(transformVector(msg->getPayload().first));
- }
-
- // Collect the data
- auto collect = _registration.collect(client);
-
- req.setPayload(std::get<0>(collect));
-
- // Record that the registration is pending
- _state->setState(StorageStateEnum::pending);
- _registrationStatus = FreeMonRegistrationStatus::kPending;
-
- writeState(client);
-
- // Send the async request
- _futureRegistrationResponse = doAsyncCallback<FreeMonRegistrationResponse>(
- this,
- _network->sendRegistrationAsync(req),
- [this](const auto& resp) {
- this->enqueue(
- FreeMonMessageWithPayload<FreeMonMessageType::AsyncRegisterComplete>::createNow(
- resp));
- },
- [this](Status s) {
- this->enqueue(
- FreeMonMessageWithPayload<FreeMonMessageType::AsyncRegisterFail>::createNow(s));
- });
-}
-
-Status FreeMonProcessor::validateRegistrationResponse(const FreeMonRegistrationResponse& resp) {
- // Any validation failure stops registration from proceeding to upload
- if (!(resp.getVersion() >= kMinProtocolVersion && resp.getVersion() <= kMaxProtocolVersion)) {
- return Status(ErrorCodes::FreeMonHttpPermanentFailure,
- str::stream()
- << "Unexpected registration response protocol version, expected ("
- << kMinProtocolVersion << ", " << kMaxProtocolVersion << "), received '"
- << resp.getVersion() << "'");
- }
-
- if (resp.getId().size() >= kRegistrationIdMaxLength) {
- return Status(ErrorCodes::FreeMonHttpPermanentFailure,
- str::stream() << "Id is '" << resp.getId().size()
- << "' bytes in length, maximum allowed length is '"
- << kRegistrationIdMaxLength << "'");
- }
-
- if (resp.getInformationalURL().size() >= kInformationalURLMaxLength) {
- return Status(ErrorCodes::FreeMonHttpPermanentFailure,
- str::stream() << "InformationURL is '" << resp.getInformationalURL().size()
- << "' bytes in length, maximum allowed length is '"
- << kInformationalURLMaxLength << "'");
- }
-
- if (resp.getMessage().size() >= kInformationalMessageMaxLength) {
- return Status(ErrorCodes::FreeMonHttpPermanentFailure,
- str::stream() << "Message is '" << resp.getMessage().size()
- << "' bytes in length, maximum allowed length is '"
- << kInformationalMessageMaxLength << "'");
- }
-
- if (resp.getUserReminder().is_initialized() &&
- resp.getUserReminder().get().size() >= kUserReminderMaxLength) {
- return Status(ErrorCodes::FreeMonHttpPermanentFailure,
- str::stream() << "UserReminder is '" << resp.getUserReminder().get().size()
- << "' bytes in length, maximum allowed length is '"
- << kUserReminderMaxLength << "'");
- }
-
- if (resp.getReportingInterval() < kReportingIntervalSecondsMin ||
- resp.getReportingInterval() > kReportingIntervalSecondsMax) {
- return Status(ErrorCodes::FreeMonHttpPermanentFailure,
- str::stream() << "Reporting Interval '" << resp.getReportingInterval()
- << "' must be in the range [" << kReportingIntervalSecondsMin
- << "," << kReportingIntervalSecondsMax << "]");
- }
-
- // Did cloud ask us to stop uploading?
- if (resp.getHaltMetricsUploading()) {
- return Status(ErrorCodes::FreeMonHttpPermanentFailure,
- str::stream() << "Halting metrics upload due to response");
- }
-
- return Status::OK();
-}
-
-
-void FreeMonProcessor::notifyPendingRegisters(const Status s) {
- for (auto&& pendingRegister : _pendingRegisters) {
- (checked_cast<FreeMonRegisterCommandMessage*>(pendingRegister.get()))->setStatus(s);
- }
- _pendingRegisters.clear();
-}
-
-
-Status FreeMonProcessor::validateMetricsResponse(const FreeMonMetricsResponse& resp) {
- // Any validation failure stops registration from proceeding to upload
- if (!(resp.getVersion() >= kMinProtocolVersion && resp.getVersion() <= kMaxProtocolVersion)) {
- return Status(ErrorCodes::FreeMonHttpPermanentFailure,
- str::stream() << "Unexpected metrics response protocol version, expected ("
- << kMinProtocolVersion << ", " << kMaxProtocolVersion
- << "), received '" << resp.getVersion() << "'");
- }
-
- if (resp.getId().is_initialized() && resp.getId().get().size() >= kRegistrationIdMaxLength) {
- return Status(ErrorCodes::FreeMonHttpPermanentFailure,
- str::stream() << "Id is '" << resp.getId().get().size()
- << "' bytes in length, maximum allowed length is '"
- << kRegistrationIdMaxLength << "'");
- }
-
- if (resp.getInformationalURL().is_initialized() &&
- resp.getInformationalURL().get().size() >= kInformationalURLMaxLength) {
- return Status(ErrorCodes::FreeMonHttpPermanentFailure,
- str::stream()
- << "InformationURL is '" << resp.getInformationalURL().get().size()
- << "' bytes in length, maximum allowed length is '"
- << kInformationalURLMaxLength << "'");
- }
-
- if (resp.getMessage().is_initialized() &&
- resp.getMessage().get().size() >= kInformationalMessageMaxLength) {
- return Status(ErrorCodes::FreeMonHttpPermanentFailure,
- str::stream() << "Message is '" << resp.getMessage().get().size()
- << "' bytes in length, maximum allowed length is '"
- << kInformationalMessageMaxLength << "'");
- }
-
- if (resp.getUserReminder().is_initialized() &&
- resp.getUserReminder().get().size() >= kUserReminderMaxLength) {
- return Status(ErrorCodes::FreeMonHttpPermanentFailure,
- str::stream() << "UserReminder is '" << resp.getUserReminder().get().size()
- << "' bytes in length, maximum allowed length is '"
- << kUserReminderMaxLength << "'");
- }
-
- if (resp.getReportingInterval() < kReportingIntervalSecondsMin ||
- resp.getReportingInterval() > kReportingIntervalSecondsMax) {
- return Status(ErrorCodes::FreeMonHttpPermanentFailure,
- str::stream() << "Reporting Interval '" << resp.getReportingInterval()
- << "' must be in the range [" << kReportingIntervalSecondsMin
- << "," << kReportingIntervalSecondsMax << "]");
- }
-
- // Did cloud ask us to stop uploading?
- if (resp.getHaltMetricsUploading()) {
- return Status(ErrorCodes::FreeMonHttpPermanentFailure,
- str::stream() << "Halting metrics upload due to response");
- }
-
- return Status::OK();
-}
-
-
-void FreeMonProcessor::doAsyncRegisterComplete(
- Client* client,
- const FreeMonMessageWithPayload<FreeMonMessageType::AsyncRegisterComplete>* msg) {
-
- // Our request is no longer in-progress so delete it
- _futureRegistrationResponse.reset();
-
- if (_registrationStatus != FreeMonRegistrationStatus::kPending) {
- notifyPendingRegisters(Status(ErrorCodes::BadValue, "Registration was canceled"));
-
- return;
- }
-
- auto& resp = msg->getPayload();
-
- Status s = validateRegistrationResponse(resp);
- if (!s.isOK()) {
- LOGV2_WARNING(20620,
- "Free Monitoring registration halted due to {error}",
- "Free Monitoring registration halted due to error",
- "error"_attr = s);
-
- // Disable on any error
- _state->setState(StorageStateEnum::disabled);
- _registrationStatus = FreeMonRegistrationStatus::kDisabled;
-
- // Persist state
- writeState(client);
-
- notifyPendingRegisters(s);
-
- // If validation fails, we do not retry
- return;
- }
-
- // Update in-memory state
- _registrationRetry->setMin(Seconds(resp.getReportingInterval()));
- _metricsGatherInterval = Seconds(resp.getReportingInterval());
-
- {
- auto state = _state.synchronize();
- state->setRegistrationId(resp.getId());
-
- if (resp.getUserReminder().is_initialized()) {
- state->setUserReminder(resp.getUserReminder().get());
- } else {
- state->setUserReminder("");
- }
-
- state->setMessage(resp.getMessage());
- state->setInformationalURL(resp.getInformationalURL());
-
- state->setState(StorageStateEnum::enabled);
- }
-
- _registrationStatus = FreeMonRegistrationStatus::kEnabled;
-
- // Persist state
- writeState(client);
-
- // Reset retry counter
- _registrationRetry->reset();
-
- // Notify waiters
- notifyPendingRegisters(Status::OK());
-
- LOGV2(20615,
- "Free Monitoring is Enabled. Frequency: {interval} seconds",
- "Free Monitoring is Enabled",
- "interval"_attr = resp.getReportingInterval());
-
- // Enqueue next metrics upload immediately to deliver a good experience
- enqueue(FreeMonMessage::createNow(FreeMonMessageType::MetricsSend));
-}
-
-void FreeMonProcessor::doAsyncRegisterFail(
- Client* client, const FreeMonMessageWithPayload<FreeMonMessageType::AsyncRegisterFail>* msg) {
-
- // Our request is no longer in-progress so delete it
- _futureRegistrationResponse.reset();
-
- if (_registrationStatus != FreeMonRegistrationStatus::kPending) {
- notifyPendingRegisters(Status(ErrorCodes::BadValue, "Registration was canceled"));
-
- return;
- }
-
- if (!_registrationRetry->incrementError()) {
- // We have exceeded our retry
- LOGV2_WARNING(20621, "Free Monitoring is abandoning registration after excess retries");
- return;
- }
-
- LOGV2_DEBUG(20616,
- 1,
- "Free Monitoring Registration Failed with status '{error}', retrying in {interval}",
- "Free Monitoring Registration Failed, will retry after interval",
- "error"_attr = msg->getPayload(),
- "interval"_attr = _registrationRetry->getNextDuration());
-
- // Enqueue a register retry
- enqueue(FreeMonRegisterCommandMessage::createWithDeadline(
- {_tags, boost::none}, _registrationRetry->getNextDeadline(client)));
-}
-
-void FreeMonProcessor::doCommandUnregister(
- Client* client, FreeMonWaitableMessageWithPayload<FreeMonMessageType::UnregisterCommand>* msg) {
- // Treat this request as idempotent
- readState(client);
-
- _state->setState(StorageStateEnum::disabled);
- _registrationStatus = FreeMonRegistrationStatus::kDisabled;
-
- writeState(client);
-
- LOGV2(20617, "Free Monitoring is Disabled");
-
- msg->setStatus(Status::OK());
-}
-
-void FreeMonProcessor::doMetricsCollect(Client* client) {
- // Collect the time at the beginning so the time to collect does not affect the schedule
- Date_t now = client->getServiceContext()->getPreciseClockSource()->now();
-
- // Collect the data
- auto collect = _metrics.collect(client);
-
- _metricsBuffer.push(std::get<0>(collect));
-
- // Enqueue the next metrics collect based on when we started processing the last collection.
- enqueue(FreeMonMessage::createWithDeadline(FreeMonMessageType::MetricsCollect,
- now + _metricsGatherInterval));
-}
-
-std::string compressMetrics(MetricsBuffer& buffer) {
- BSONObjBuilder builder;
-
- {
- BSONArrayBuilder arrayBuilder(builder.subarrayStart(kMetricsRequestArrayElement));
-
- for (const auto& obj : buffer) {
- arrayBuilder.append(obj);
- }
- }
-
- BSONObj obj = builder.done();
-
- std::string outBuffer;
- snappy::Compress(obj.objdata(), obj.objsize(), &outBuffer);
-
- return outBuffer;
-}
-
-void FreeMonProcessor::doMetricsSend(Client* client) {
- // We want to read state from disk in case we asked to stop but otherwise
- // use the in-memory state. It is important not to treat disk state as authoritative
- // on secondaries.
- readState(client, false);
-
- // Only continue metrics send if the local disk state (in-case user deleted local document)
- // and in-memory status both say to continue.
- if (_registrationStatus != FreeMonRegistrationStatus::kEnabled ||
- _state->getState() != StorageStateEnum::enabled) {
- // If we are recently disabled, then stop sending metrics
- return;
- }
-
- // Build outbound request
- FreeMonMetricsRequest req;
-
- req.setVersion(kMaxProtocolVersion);
- req.setLocalTime(client->getServiceContext()->getPreciseClockSource()->now());
- req.setEncoding(MetricsEncodingEnum::snappy);
-
- req.setId(_state->getRegistrationId());
-
- // Get the buffered metrics
- auto metrics = compressMetrics(_metricsBuffer);
- req.setMetrics(ConstDataRange(metrics.data(), metrics.size()));
-
- _lastMetricsSend = Date_t::now();
-
- // Send the async request
- doAsyncCallback<FreeMonMetricsResponse>(
- this,
- _network->sendMetricsAsync(req),
- [this](const auto& resp) {
- this->enqueue(
- FreeMonMessageWithPayload<FreeMonMessageType::AsyncMetricsComplete>::createNow(
- resp));
- },
- [this](Status s) {
- this->enqueue(
- FreeMonMessageWithPayload<FreeMonMessageType::AsyncMetricsFail>::createNow(s));
- });
-}
-
-void FreeMonProcessor::doAsyncMetricsComplete(
- Client* client,
- const FreeMonMessageWithPayload<FreeMonMessageType::AsyncMetricsComplete>* msg) {
-
- // If we have disabled the store between the metrics send message and the metrcs complete
- // message then it means that we need to stop processing metrics on this instance. We ignore the
- // message entirely including an errors as the disabling of the store takes priority.
- if (_lastReadState == boost::none) {
- return;
- }
-
- auto& resp = msg->getPayload();
-
- Status s = validateMetricsResponse(resp);
- if (!s.isOK()) {
- LOGV2_WARNING(20622,
- "Free Monitoring metrics uploading halted due to {error}",
- "Free Monitoring metrics uploading halted due to error",
- "error"_attr = s);
-
- // Disable free monitoring on validation errors
- _state->setState(StorageStateEnum::disabled);
- _registrationStatus = FreeMonRegistrationStatus::kDisabled;
-
- writeState(client);
-
- // If validation fails, we do not retry
- return;
- }
-
- // If cloud said delete, not just halt, so erase state
- if (resp.getPermanentlyDelete() == true) {
- auto opCtxUnique = client->makeOperationContext();
- FreeMonStorage::deleteState(opCtxUnique.get());
-
- _state->setState(StorageStateEnum::pending);
- _registrationStatus = FreeMonRegistrationStatus::kDisabled;
-
- // Clear out the in-memory state
- _lastReadState = boost::none;
-
- return;
- }
-
- // Update in-memory state of buffered metrics
- // TODO: do we reset only the metrics we send or all pending on success?
-
- _metricsBuffer.reset();
-
- {
- auto state = _state.synchronize();
-
- if (resp.getId().is_initialized()) {
- state->setRegistrationId(resp.getId().get());
- }
-
- if (resp.getUserReminder().is_initialized()) {
- state->setUserReminder(resp.getUserReminder().get());
- }
-
- if (resp.getInformationalURL().is_initialized()) {
- state->setInformationalURL(resp.getInformationalURL().get());
- }
-
- if (resp.getMessage().is_initialized()) {
- state->setMessage(resp.getMessage().get());
- }
- }
-
- // Persist state
- writeState(client);
-
- // Reset retry counter
- _metricsGatherInterval = Seconds(resp.getReportingInterval());
- _metricsRetry->setMin(Seconds(resp.getReportingInterval()));
- _metricsRetry->reset();
-
- if (resp.getResendRegistration().is_initialized() && resp.getResendRegistration()) {
- enqueue(FreeMonRegisterCommandMessage::createNow({_tags, boost::none}));
- } else {
- // Enqueue next metrics upload
- enqueue(FreeMonMessage::createWithDeadline(FreeMonMessageType::MetricsSend,
- _metricsRetry->getNextDeadline(client)));
- }
-}
-
-void FreeMonProcessor::doAsyncMetricsFail(
- Client* client, const FreeMonMessageWithPayload<FreeMonMessageType::AsyncMetricsFail>* msg) {
-
- if (!_metricsRetry->incrementError()) {
- // We have exceeded our retry
- LOGV2_WARNING(20623, "Free Monitoring is abandoning metrics upload after excess retries");
- return;
- }
-
- LOGV2_DEBUG(20618,
- 1,
- "Free Monitoring Metrics upload failed with status {error}, retrying in {interval}",
- "Free Monitoring Metrics upload failed, will retry after interval",
- "error"_attr = msg->getPayload(),
- "interval"_attr = _metricsRetry->getNextDuration());
-
- // Enqueue next metrics upload
- enqueue(FreeMonMessage::createWithDeadline(FreeMonMessageType::MetricsSend,
- _metricsRetry->getNextDeadline(client)));
-}
-
-void FreeMonProcessor::getStatus(OperationContext* opCtx,
- BSONObjBuilder* status,
- FreeMonGetStatusEnum mode) {
- if (!_lastReadState.get()) {
- // _state gets initialized by readState() regardless,
- // use _lastReadState to differential "undecided" from default.
- status->append("state", "undecided");
- return;
- }
-
- if (mode == FreeMonGetStatusEnum::kServerStatus) {
- status->append("state", StorageState_serializer(_state->getState()));
- status->append("retryIntervalSecs",
- durationCount<Seconds>(_metricsRetry->getNextDuration()));
- auto lastMetricsSend = _lastMetricsSend.get();
- if (lastMetricsSend) {
- status->append("lastRunTime", lastMetricsSend->toString());
- }
- status->append("registerErrors", static_cast<long long>(_registrationRetry->getCount()));
- status->append("metricsErrors", static_cast<long long>(_metricsRetry->getCount()));
- } else {
- auto state = _state.synchronize();
- status->append("state", StorageState_serializer(state->getState()));
- status->append("message", state->getMessage());
- status->append("url", state->getInformationalURL());
- status->append("userReminder", state->getUserReminder());
- }
-}
-
-void FreeMonProcessor::doOnTransitionToPrimary(Client* client) {
- if (_registerOnTransitionToPrimary == RegistrationType::RegisterAfterOnTransitionToPrimary) {
- enqueue(
- FreeMonRegisterCommandMessage::createNow({std::vector<std::string>(), boost::none}));
-
- } else if (_registerOnTransitionToPrimary ==
- RegistrationType::RegisterAfterOnTransitionToPrimaryIfEnabled) {
- readState(client);
- if (_state->getState() == StorageStateEnum::enabled) {
- enqueue(FreeMonRegisterCommandMessage::createNow(
- {std::vector<std::string>(), boost::none}));
- }
- }
-
- // On transition to primary once
- _registerOnTransitionToPrimary = RegistrationType::DoNotRegister;
-}
-
-void FreeMonProcessor::processInMemoryStateChange(const FreeMonStorageState& originalState,
- const FreeMonStorageState& newState) {
- // Are we transition from disabled -> enabled?
- if (originalState.getState() != newState.getState()) {
- if (originalState.getState() != StorageStateEnum::enabled &&
- newState.getState() == StorageStateEnum::enabled) {
-
- // Secondary needs to start registration
- enqueue(FreeMonRegisterCommandMessage::createNow(
- {std::vector<std::string>(), newState.getRegistrationId().toString()}));
- }
- }
-}
-
-void FreeMonProcessor::doNotifyOnUpsert(
- Client* client, const FreeMonMessageWithPayload<FreeMonMessageType::NotifyOnUpsert>* msg) {
- try {
- const BSONObj& doc = msg->getPayload();
- auto newState = FreeMonStorageState::parse(IDLParserErrorContext("free_mon_storage"), doc);
-
- // Likely, the update changed something
- if (newState != _state) {
- uassert(50839,
- str::stream() << "Unexpected free monitoring storage version "
- << newState.getVersion(),
- newState.getVersion() == kStorageVersion);
-
- processInMemoryStateChange(_state.get(), newState);
-
- // Note: enabled -> disabled is handled implicitly by register and send metrics checks
- // after _state is updated below
-
- // Copy the fields
- _state = newState;
- }
-
- } catch (...) {
-
- // Stop the queue
- _queue.stop();
-
- LOGV2_WARNING(20624,
- "Uncaught exception in '{exception}' in free monitoring op observer. "
- "Shutting down the free monitoring subsystem.",
- "exception"_attr = exceptionToStatus());
- }
-}
-
-void FreeMonProcessor::doNotifyOnDelete(Client* client) {
- // The config document was either deleted or the entire collection was dropped, we treat them
- // the same and stop free monitoring. We continue collecting though.
-
- // So we mark the internal state as disabled which stop registration and metrics send
- _state->setState(StorageStateEnum::pending);
- _registrationStatus = FreeMonRegistrationStatus::kDisabled;
-
- // Clear out the in-memory state
- _lastReadState = boost::none;
-}
-
-void FreeMonProcessor::doNotifyOnRollback(Client* client) {
- // We have rolled back, the state on disk reflects our new reality
- // We should re-read the disk state and proceed.
-
- // copy the in-memory state
- auto originalState = _state.get();
-
- // Re-read state from disk
- readState(client);
-
- auto newState = _state.get();
-
- if (newState != originalState) {
- processInMemoryStateChange(originalState, newState);
- }
-}
-
-
-} // namespace mongo