summaryrefslogtreecommitdiff
path: root/src/mongo/db/prefetch.cpp
diff options
context:
space:
mode:
authorEric Milkie <milkie@10gen.com>2014-10-02 16:17:25 -0400
committerEric Milkie <milkie@10gen.com>2014-10-03 15:39:19 -0400
commite0509e511bdbac2329d32a7238b51cd6b5ddd591 (patch)
treee566d6a137c55483e3a17200b1e4dbd67e1f5ead /src/mongo/db/prefetch.cpp
parentd32dc5f88567b11eb503a8343cb8b4b3d8c68c76 (diff)
downloadmongo-e0509e511bdbac2329d32a7238b51cd6b5ddd591.tar.gz
SERVER-15312 move replIndexPrefetch parameter out of legacy and into Applier (BGSync)
Diffstat (limited to 'src/mongo/db/prefetch.cpp')
-rw-r--r--src/mongo/db/prefetch.cpp243
1 files changed, 151 insertions, 92 deletions
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<TimerStats> displayPrefetchIndexPages(
- "repl.preload.indexes",
- &prefetchIndexStats );
- static TimerStats prefetchDocStats;
- static ServerStatusMetricField<TimerStats> 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<TimerStats> displayPrefetchIndexPages("repl.preload.indexes",
+ &prefetchIndexStats );
+ TimerStats prefetchDocStats;
+ ServerStatusMetricField<TimerStats> 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