예제 #1
0
 /**
  * Insert an item at the back of this queue.
  *
  * @param item the item to insert
  **/
 void push(T &&item) {
     emplace(std::move(item));
 }
boost::optional<Date_t> CollectionRangeDeleter::cleanUpNextRange(
    OperationContext* opCtx,
    NamespaceString const& nss,
    OID const& epoch,
    int maxToDelete,
    CollectionRangeDeleter* forTestOnly) {

    StatusWith<int> wrote = 0;

    auto range = boost::optional<ChunkRange>(boost::none);
    auto notification = DeleteNotification();

    {
        UninterruptibleLockGuard noInterrupt(opCtx->lockState());
        AutoGetCollection autoColl(opCtx, nss, MODE_IX);

        auto* const collection = autoColl.getCollection();
        auto* const css = CollectionShardingRuntime::get(opCtx, nss);
        auto& metadataManager = css->_metadataManager;
        auto* const self = forTestOnly ? forTestOnly : &metadataManager->_rangesToClean;

        const auto scopedCollectionMetadata =
            metadataManager->getActiveMetadata(metadataManager, boost::none);

        if (!scopedCollectionMetadata) {
            LOG(0) << "Abandoning any range deletions because the metadata for " << nss.ns()
                   << " was reset";
            stdx::lock_guard<stdx::mutex> lk(css->_metadataManager->_managerLock);
            css->_metadataManager->_clearAllCleanups(lk);
            return boost::none;
        }

        const auto& metadata = *scopedCollectionMetadata;

        if (!forTestOnly && (!collection || !metadata->isSharded())) {
            if (!collection) {
                LOG(0) << "Abandoning any range deletions left over from dropped " << nss.ns();
            } else {
                LOG(0) << "Abandoning any range deletions left over from previously sharded"
                       << nss.ns();
            }

            stdx::lock_guard<stdx::mutex> lk(css->_metadataManager->_managerLock);
            css->_metadataManager->_clearAllCleanups(lk);
            return boost::none;
        }

        if (!forTestOnly && metadata->getCollVersion().epoch() != epoch) {
            LOG(1) << "Range deletion task for " << nss.ns() << " epoch " << epoch << " woke;"
                   << " (current is " << metadata->getCollVersion() << ")";
            return boost::none;
        }

        bool writeOpLog = false;

        {
            stdx::lock_guard<stdx::mutex> scopedLock(css->_metadataManager->_managerLock);
            if (self->isEmpty()) {
                LOG(1) << "No further range deletions scheduled on " << nss.ns();
                return boost::none;
            }

            auto& orphans = self->_orphans;
            if (orphans.empty()) {
                // We have delayed deletions; see if any are ready.
                auto& df = self->_delayedOrphans.front();
                if (df.whenToDelete > Date_t::now()) {
                    LOG(0) << "Deferring deletion of " << nss.ns() << " range "
                           << redact(df.range.toString()) << " until " << df.whenToDelete;
                    return df.whenToDelete;
                }

                // Move a single range from _delayedOrphans to _orphans
                orphans.splice(orphans.end(), self->_delayedOrphans, self->_delayedOrphans.begin());
                LOG(1) << "Proceeding with deferred deletion of " << nss.ns() << " range "
                       << redact(orphans.front().range.toString());

                writeOpLog = true;
            }

            invariant(!orphans.empty());
            const auto& frontRange = orphans.front().range;
            range.emplace(frontRange.getMin().getOwned(), frontRange.getMax().getOwned());
            notification = orphans.front().notification;
        }

        invariant(range);

        if (writeOpLog) {
            // Secondaries will watch for this update, and kill any queries that may depend on
            // documents in the range -- excepting any queries with a read-concern option
            // 'ignoreChunkMigration'
            try {
                AutoGetCollection autoAdmin(
                    opCtx, NamespaceString::kServerConfigurationNamespace, MODE_IX);

                Helpers::upsert(opCtx,
                                NamespaceString::kServerConfigurationNamespace.ns(),
                                BSON("_id"
                                     << "startRangeDeletion"
                                     << "ns"
                                     << nss.ns()
                                     << "epoch"
                                     << epoch
                                     << "min"
                                     << range->getMin()
                                     << "max"
                                     << range->getMax()));
            } catch (const DBException& e) {
                stdx::lock_guard<stdx::mutex> scopedLock(css->_metadataManager->_managerLock);
                css->_metadataManager->_clearAllCleanups(
                    scopedLock,
                    e.toStatus("cannot push startRangeDeletion record to Op Log,"
                               " abandoning scheduled range deletions"));
                return boost::none;
            }
        }

        try {
            wrote = self->_doDeletion(
                opCtx, collection, metadata->getKeyPattern(), *range, maxToDelete);
        } catch (const DBException& e) {
            wrote = e.toStatus();
            warning() << e.what();
        }
    }  // drop autoColl

    if (!wrote.isOK() || wrote.getValue() == 0) {
        if (wrote.isOK()) {
            LOG(0) << "No documents remain to delete in " << nss << " range "
                   << redact(range->toString());
        }

        // Wait for majority replication even when wrote isn't OK or == 0, because it might have
        // been OK and/or > 0 previously, and the deletions must be persistent before notifying
        // clients in _pop().

        LOG(0) << "Waiting for majority replication of local deletions in " << nss.ns() << " range "
               << redact(range->toString());

        repl::ReplClientInfo::forClient(opCtx->getClient()).setLastOpToSystemLastOpTime(opCtx);
        const auto clientOpTime = repl::ReplClientInfo::forClient(opCtx->getClient()).getLastOp();

        // Wait for replication outside the lock
        const auto status = [&] {
            try {
                WriteConcernResult unusedWCResult;
                return waitForWriteConcern(
                    opCtx, clientOpTime, kMajorityWriteConcern, &unusedWCResult);
            } catch (const DBException& e) {
                return e.toStatus();
            }
        }();

        // Get the lock again to finish off this range (including notifying, if necessary).
        // Don't allow lock interrupts while cleaning up.
        UninterruptibleLockGuard noInterrupt(opCtx->lockState());
        AutoGetCollection autoColl(opCtx, nss, MODE_IX);
        auto* const css = CollectionShardingRuntime::get(opCtx, nss);
        auto& metadataManager = css->_metadataManager;
        auto* const self = forTestOnly ? forTestOnly : &metadataManager->_rangesToClean;

        stdx::lock_guard<stdx::mutex> scopedLock(css->_metadataManager->_managerLock);

        if (!status.isOK()) {
            LOG(0) << "Error when waiting for write concern after removing " << nss << " range "
                   << redact(range->toString()) << " : " << redact(status.reason());

            // If range were already popped (e.g. by dropping nss during the waitForWriteConcern
            // above) its notification would have been triggered, so this check suffices to ensure
            // that it is safe to pop the range here
            if (!notification.ready()) {
                invariant(!self->isEmpty() && self->_orphans.front().notification == notification);
                LOG(0) << "Abandoning deletion of latest range in " << nss.ns() << " after local "
                       << "deletions because of replication failure";
                self->_pop(status);
            }
        } else {
            LOG(0) << "Finished deleting documents in " << nss.ns() << " range "
                   << redact(range->toString());

            self->_pop(wrote.getStatus());
        }

        if (!self->_orphans.empty()) {
            LOG(1) << "Deleting " << nss.ns() << " range "
                   << redact(self->_orphans.front().range.toString()) << " next.";
        }

        return Date_t::now() + stdx::chrono::milliseconds{rangeDeleterBatchDelayMS.load()};
    }

    invariant(range);
    invariant(wrote.getStatus());
    invariant(wrote.getValue() > 0);

    notification.abandon();
    return Date_t::now() + stdx::chrono::milliseconds{rangeDeleterBatchDelayMS.load()};
}
예제 #3
0
 /**
  * Insert an item at the back of this queue.
  *
  * @param item the item to insert
  **/
 void push(const T &item) {
     emplace(item);
 }
예제 #4
0
// Create a substitution that sends each occurrence of `d` to
// its a corresponding `t`. Note that the kind and type of `t`
// must agree with that of `d`.
//
// FIXME: If we use this during template argument deduction, do we
// need this to act as a unifier (i.e. reject re-mappings of
// previously deduced terms?). Probably.
inline void
Substitution::send(Decl& d, Term& t)
{
  emplace(&d, &t);
}