/**
* 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 .
*
* 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::kReplication
#include "mongo/platform/basic.h"
#include "mongo/db/repl/drop_pending_collection_reaper.h"
#include
#include
#include "mongo/db/client.h"
#include "mongo/db/operation_context.h"
#include "mongo/db/repl/storage_interface.h"
#include "mongo/db/service_context.h"
#include "mongo/util/log.h"
namespace mongo {
namespace repl {
namespace {
const auto getDropPendingCollectionReaper =
ServiceContext::declareDecoration>();
} // namespace
DropPendingCollectionReaper* DropPendingCollectionReaper::get(ServiceContext* service) {
return getDropPendingCollectionReaper(service).get();
}
DropPendingCollectionReaper* DropPendingCollectionReaper::get(ServiceContext& service) {
return getDropPendingCollectionReaper(service).get();
}
DropPendingCollectionReaper* DropPendingCollectionReaper::get(OperationContext* opCtx) {
return get(opCtx->getClient()->getServiceContext());
}
void DropPendingCollectionReaper::set(ServiceContext* service,
std::unique_ptr newReaper) {
auto& reaper = getDropPendingCollectionReaper(service);
reaper = std::move(newReaper);
}
DropPendingCollectionReaper::DropPendingCollectionReaper(StorageInterface* storageInterface)
: _storageInterface(storageInterface) {}
void DropPendingCollectionReaper::addDropPendingNamespace(
const OpTime& dropOpTime, const NamespaceString& dropPendingNamespace) {
invariant(dropPendingNamespace.isDropPendingNamespace());
stdx::lock_guard lock(_mutex);
const auto equalRange = _dropPendingNamespaces.equal_range(dropOpTime);
const auto& lowerBound = equalRange.first;
const auto& upperBound = equalRange.second;
auto matcher = [&dropPendingNamespace](const auto& pair) {
return pair.second == dropPendingNamespace;
};
if (std::find_if(lowerBound, upperBound, matcher) == upperBound) {
_dropPendingNamespaces.insert(std::make_pair(dropOpTime, dropPendingNamespace));
} else {
severe() << "Failed to add drop-pending collection " << dropPendingNamespace
<< " with drop optime " << dropOpTime << ": duplicate optime and namespace pair.";
fassertFailedNoTrace(40448);
}
}
boost::optional DropPendingCollectionReaper::getEarliestDropOpTime() {
stdx::lock_guard lock(_mutex);
auto it = _dropPendingNamespaces.cbegin();
if (it == _dropPendingNamespaces.cend()) {
return boost::none;
}
return it->first;
}
bool DropPendingCollectionReaper::rollBackDropPendingCollection(
OperationContext* opCtx, const OpTime& opTime, const NamespaceString& collectionNamespace) {
// renames because these are internal operations.
UnreplicatedWritesBlock uwb(opCtx);
const auto pendingNss = collectionNamespace.makeDropPendingNamespace(opTime);
{
stdx::lock_guard lock(_mutex);
const auto equalRange = _dropPendingNamespaces.equal_range(opTime);
const auto& lowerBound = equalRange.first;
const auto& upperBound = equalRange.second;
auto matcher = [&pendingNss](const auto& pair) { return pair.second == pendingNss; };
auto it = std::find_if(lowerBound, upperBound, matcher);
if (it == upperBound) {
warning() << "Cannot find drop-pending namespace at OpTime " << opTime
<< " for collection " << collectionNamespace << " to roll back.";
return false;
}
_dropPendingNamespaces.erase(it);
}
log() << "Rolling back collection drop for " << pendingNss << " with drop OpTime " << opTime
<< " to namespace " << collectionNamespace;
return true;
}
void DropPendingCollectionReaper::dropCollectionsOlderThan(OperationContext* opCtx,
const OpTime& opTime) {
DropPendingNamespaces toDrop;
{
stdx::lock_guard lock(_mutex);
for (auto it = _dropPendingNamespaces.cbegin();
it != _dropPendingNamespaces.cend() && it->first <= opTime;
++it) {
toDrop.insert(*it);
}
}
if (toDrop.empty()) {
return;
}
{
// Every node cleans up its own drop-pending collections. We should never replicate these
// drops because these are internal operations.
UnreplicatedWritesBlock uwb(opCtx);
for (const auto& opTimeAndNamespace : toDrop) {
const auto& dropOpTime = opTimeAndNamespace.first;
const auto& nss = opTimeAndNamespace.second;
log() << "Completing collection drop for " << nss << " with drop optime " << dropOpTime
<< " (notification optime: " << opTime << ")";
auto status = _storageInterface->dropCollection(opCtx, nss);
if (!status.isOK()) {
warning() << "Failed to remove drop-pending collection " << nss
<< " with drop optime " << dropOpTime
<< " (notification optime: " << opTime << "): " << status;
}
}
}
{
// Entries must be removed AFTER drops are completed, so that getEarliestDropOpTime()
// returns appropriate results.
stdx::lock_guard lock(_mutex);
auto it = _dropPendingNamespaces.cbegin();
while (it != _dropPendingNamespaces.cend() && it->first <= opTime) {
if (toDrop.find(it->first) != toDrop.cend()) {
it = _dropPendingNamespaces.erase(it);
} else {
++it;
}
}
}
}
} // namespace repl
} // namespace mongo