From e0509e511bdbac2329d32a7238b51cd6b5ddd591 Mon Sep 17 00:00:00 2001 From: Eric Milkie Date: Thu, 2 Oct 2014 16:17:25 -0400 Subject: SERVER-15312 move replIndexPrefetch parameter out of legacy and into Applier (BGSync) --- src/mongo/db/prefetch.cpp | 243 ++++++++++++++++++++++++++++------------------ 1 file changed, 151 insertions(+), 92 deletions(-) (limited to 'src/mongo/db/prefetch.cpp') diff --git a/src/mongo/db/prefetch.cpp b/src/mongo/db/prefetch.cpp index 21b0d15d770..0f4a6c76a77 100644 --- a/src/mongo/db/prefetch.cpp +++ b/src/mongo/db/prefetch.cpp @@ -26,123 +26,53 @@ * it in the license file. */ -#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kQuery +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kReplication #include "mongo/platform/basic.h" #include "mongo/db/prefetch.h" +#include "mongo/db/catalog/collection.h" +#include "mongo/db/commands/server_status_metric.h" #include "mongo/db/dbhelpers.h" -#include "mongo/db/diskloc.h" #include "mongo/db/index/index_access_method.h" -#include "mongo/db/catalog/collection.h" #include "mongo/db/jsobj.h" -#include "mongo/db/repl/rs.h" +#include "mongo/db/repl/bgsync.h" +#include "mongo/db/repl/repl_coordinator.h" +#include "mongo/db/repl/repl_coordinator_global.h" +#include "mongo/db/server_parameters.h" #include "mongo/db/stats/timer_stats.h" -#include "mongo/db/commands/server_status_metric.h" #include "mongo/util/log.h" +#include "mongo/util/mmap.h" namespace mongo { - +namespace repl { +namespace { // todo / idea: the prefetcher, when it fetches _id, on an upsert, will see if the record exists. if it does not, // at write time, we can just do an insert, which will be faster. //The count (of batches) and time spent fetching pages before application // -- meaning depends on the prefetch behavior: all, _id index, none, etc.) - static TimerStats prefetchIndexStats; - static ServerStatusMetricField displayPrefetchIndexPages( - "repl.preload.indexes", - &prefetchIndexStats ); - static TimerStats prefetchDocStats; - static ServerStatusMetricField displayPrefetchDocPages( - "repl.preload.docs", - &prefetchDocStats ); - - void prefetchIndexPages(OperationContext* txn, - Collection* collection, - const repl::ReplSetImpl::IndexPrefetchConfig& prefetchConfig, - const BSONObj& obj); - - void prefetchRecordPages(OperationContext* txn, const char* ns, const BSONObj& obj); - - - // prefetch for an oplog operation - void prefetchPagesForReplicatedOp(OperationContext* txn, - Database* db, - const repl::ReplSetImpl::IndexPrefetchConfig& prefetchConfig, - const BSONObj& op) { - const char *opField; - const char *opType = op.getStringField("op"); - switch (*opType) { - case 'i': // insert - case 'd': // delete - opField = "o"; - break; - case 'u': // update - opField = "o2"; - break; - default: - // prefetch ignores other ops - return; - } - - BSONObj obj = op.getObjectField(opField); - const char *ns = op.getStringField("ns"); - - Collection* collection = db->getCollection( txn, ns ); - if ( !collection ) - return; - - LOG(4) << "index prefetch for op " << *opType << endl; - - DEV txn->lockState()->assertAtLeastReadLocked(ns); - - // should we prefetch index pages on updates? if the update is in-place and doesn't change - // indexed values, it is actually slower - a lot slower if there are a dozen indexes or - // lots of multikeys. possible variations (not all mutually exclusive): - // 1) current behavior: full prefetch - // 2) don't do it for updates - // 3) don't do multikey indexes for updates - // 4) don't prefetchIndexPages on some heuristic; e.g., if it's an $inc. - // 5) if not prefetching index pages (#2), we should do it if we are upsertings and it - // will be an insert. to do that we could do the prefetchRecordPage first and if DNE - // then we do #1. - // - // note that on deletes 'obj' does not have all the keys we would want to prefetch on. - // a way to achieve that would be to prefetch the record first, and then afterwards do - // this part. - // - prefetchIndexPages(txn, collection, prefetchConfig, obj); - - // do not prefetch the data for inserts; it doesn't exist yet - // - // we should consider doing the record prefetch for the delete op case as we hit the record - // when we delete. note if done we only want to touch the first page. - // - // update: do record prefetch. - if ((*opType == 'u') && - // do not prefetch the data for capped collections because - // they typically do not have an _id index for findById() to use. - !collection->isCapped()) { - prefetchRecordPages(txn, ns, obj); - } - } + TimerStats prefetchIndexStats; + ServerStatusMetricField displayPrefetchIndexPages("repl.preload.indexes", + &prefetchIndexStats ); + TimerStats prefetchDocStats; + ServerStatusMetricField displayPrefetchDocPages("repl.preload.docs", + &prefetchDocStats ); // page in pages needed for all index lookups on a given object void prefetchIndexPages(OperationContext* txn, Collection* collection, - const repl::ReplSetImpl::IndexPrefetchConfig& prefetchConfig, + const BackgroundSync::IndexPrefetchConfig& prefetchConfig, const BSONObj& obj) { - DiskLoc unusedDl; // unused - BSONObjSet unusedKeys; // do we want prefetchConfig to be (1) as-is, (2) for update ops only, or (3) configured per op type? // One might want PREFETCH_NONE for updates, but it's more rare that it is a bad idea for inserts. // #3 (per op), a big issue would be "too many knobs". switch (prefetchConfig) { - case repl::ReplSetImpl::PREFETCH_NONE: + case BackgroundSync::PREFETCH_NONE: return; - case repl::ReplSetImpl::PREFETCH_ID_ONLY: + case BackgroundSync::PREFETCH_ID_ONLY: { TimerHolder timer( &prefetchIndexStats); // on the update op case, the call to prefetchRecordPages will touch the _id index. @@ -160,7 +90,7 @@ namespace mongo { } break; } - case repl::ReplSetImpl::PREFETCH_ALL: + case BackgroundSync::PREFETCH_ALL: { // indexCount includes all indexes, including ones // in the process of being built @@ -178,7 +108,6 @@ namespace mongo { catch (const DBException& e) { LOG(2) << "ignoring exception in prefetchIndexPages(): " << e.what() << endl; } - unusedKeys.clear(); } break; } @@ -215,4 +144,134 @@ namespace mongo { } } } -} +} // namespace + + // prefetch for an oplog operation + void prefetchPagesForReplicatedOp(OperationContext* txn, + Database* db, + const BSONObj& op) { + const BackgroundSync::IndexPrefetchConfig prefetchConfig = + BackgroundSync::get()->getIndexPrefetchConfig(); + const char *opField; + const char *opType = op.getStringField("op"); + switch (*opType) { + case 'i': // insert + case 'd': // delete + opField = "o"; + break; + case 'u': // update + opField = "o2"; + break; + default: + // prefetch ignores other ops + return; + } + + BSONObj obj = op.getObjectField(opField); + const char *ns = op.getStringField("ns"); + + Collection* collection = db->getCollection( txn, ns ); + if ( !collection ) + return; + + LOG(4) << "index prefetch for op " << *opType << endl; + + DEV txn->lockState()->assertAtLeastReadLocked(ns); + + // should we prefetch index pages on updates? if the update is in-place and doesn't change + // indexed values, it is actually slower - a lot slower if there are a dozen indexes or + // lots of multikeys. possible variations (not all mutually exclusive): + // 1) current behavior: full prefetch + // 2) don't do it for updates + // 3) don't do multikey indexes for updates + // 4) don't prefetchIndexPages on some heuristic; e.g., if it's an $inc. + // 5) if not prefetching index pages (#2), we should do it if we are upsertings and it + // will be an insert. to do that we could do the prefetchRecordPage first and if DNE + // then we do #1. + // + // note that on deletes 'obj' does not have all the keys we would want to prefetch on. + // a way to achieve that would be to prefetch the record first, and then afterwards do + // this part. + // + prefetchIndexPages(txn, collection, prefetchConfig, obj); + + // do not prefetch the data for inserts; it doesn't exist yet + // + // we should consider doing the record prefetch for the delete op case as we hit the record + // when we delete. note if done we only want to touch the first page. + // + // update: do record prefetch. + if ((*opType == 'u') && + // do not prefetch the data for capped collections because + // they typically do not have an _id index for findById() to use. + !collection->isCapped()) { + prefetchRecordPages(txn, ns, obj); + } + } + + class ReplIndexPrefetch : public ServerParameter { + public: + ReplIndexPrefetch() + : ServerParameter( ServerParameterSet::getGlobal(), "replIndexPrefetch" ) { + } + + virtual ~ReplIndexPrefetch() { + } + + const char * _value() { + if (getGlobalReplicationCoordinator()->getReplicationMode() != + ReplicationCoordinator::modeReplSet) { + return "uninitialized"; + } + BackgroundSync::IndexPrefetchConfig ip = + BackgroundSync::get()->getIndexPrefetchConfig(); + switch (ip) { + case BackgroundSync::PREFETCH_NONE: + return "none"; + case BackgroundSync::PREFETCH_ID_ONLY: + return "_id_only"; + case BackgroundSync::PREFETCH_ALL: + return "all"; + default: + return "invalid"; + } + } + + virtual void append(OperationContext* txn, BSONObjBuilder& b, const string& name) { + b.append( name, _value() ); + } + + virtual Status set( const BSONElement& newValueElement ) { + if (getGlobalReplicationCoordinator()->getReplicationMode() != + ReplicationCoordinator::modeReplSet) { + return Status( ErrorCodes::BadValue, "replication is not enabled" ); + } + + std::string prefetch = newValueElement.valuestrsafe(); + return setFromString( prefetch ); + } + + virtual Status setFromString( const string& prefetch ) { + log() << "changing replication index prefetch behavior to " << prefetch << endl; + + BackgroundSync::IndexPrefetchConfig prefetchConfig; + + if (prefetch == "none") + prefetchConfig = BackgroundSync::PREFETCH_NONE; + else if (prefetch == "_id_only") + prefetchConfig = BackgroundSync::PREFETCH_ID_ONLY; + else if (prefetch == "all") + prefetchConfig = BackgroundSync::PREFETCH_ALL; + else { + return Status( ErrorCodes::BadValue, + str::stream() << "unrecognized indexPrefetch setting: " << prefetch ); + } + + BackgroundSync::get()->setIndexPrefetchConfig(prefetchConfig); + return Status::OK(); + } + + } replIndexPrefetch; + +} // namespace repl +} // namespace mongo -- cgit v1.2.1