diff options
Diffstat (limited to 'src/mongo/db/prefetch.cpp')
-rw-r--r-- | src/mongo/db/prefetch.cpp | 325 |
1 files changed, 154 insertions, 171 deletions
diff --git a/src/mongo/db/prefetch.cpp b/src/mongo/db/prefetch.cpp index fd306d8d5e2..77a44c4b834 100644 --- a/src/mongo/db/prefetch.cpp +++ b/src/mongo/db/prefetch.cpp @@ -48,69 +48,63 @@ namespace mongo { - using std::endl; - using std::string; +using std::endl; +using std::string; 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.) - 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 BackgroundSync::IndexPrefetchConfig& prefetchConfig, - const BSONObj& obj) { - - // 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) { +// 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.) +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 BackgroundSync::IndexPrefetchConfig& prefetchConfig, + const BSONObj& obj) { + // 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 BackgroundSync::PREFETCH_NONE: return; - case BackgroundSync::PREFETCH_ID_ONLY: - { - TimerHolder timer( &prefetchIndexStats); + case BackgroundSync::PREFETCH_ID_ONLY: { + TimerHolder timer(&prefetchIndexStats); // on the update op case, the call to prefetchRecordPages will touch the _id index. // thus perhaps this option isn't very useful? try { IndexDescriptor* desc = collection->getIndexCatalog()->findIdIndex(txn); - if ( !desc ) + if (!desc) return; - IndexAccessMethod* iam = collection->getIndexCatalog()->getIndex( desc ); - invariant( iam ); + IndexAccessMethod* iam = collection->getIndexCatalog()->getIndex(desc); + invariant(iam); iam->touch(txn, obj); - } - catch (const DBException& e) { + } catch (const DBException& e) { LOG(2) << "ignoring exception in prefetchIndexPages(): " << e.what() << endl; } break; } - case BackgroundSync::PREFETCH_ALL: - { + case BackgroundSync::PREFETCH_ALL: { // indexCount includes all indexes, including ones // in the process of being built IndexCatalog::IndexIterator ii = - collection->getIndexCatalog()->getIndexIterator( txn, true ); - while ( ii.more() ) { - TimerHolder timer( &prefetchIndexStats); + collection->getIndexCatalog()->getIndexIterator(txn, true); + while (ii.more()) { + TimerHolder timer(&prefetchIndexStats); // This will page in all index pages for the given object. try { IndexDescriptor* desc = ii.next(); - IndexAccessMethod* iam = collection->getIndexCatalog()->getIndex( desc ); - verify( iam ); + IndexAccessMethod* iam = collection->getIndexCatalog()->getIndex(desc); + verify(iam); iam->touch(txn, obj); - } - catch (const DBException& e) { + } catch (const DBException& e) { LOG(2) << "ignoring exception in prefetchIndexPages(): " << e.what() << endl; } } @@ -118,126 +112,115 @@ namespace { } default: fassertFailed(16427); - } } - - // page in the data pages for a record associated with an object - void prefetchRecordPages(OperationContext* txn, - Database* db, - const char* ns, - const BSONObj& obj) { - - BSONElement _id; - if( obj.getObjectID(_id) ) { - TimerHolder timer(&prefetchDocStats); - BSONObjBuilder builder; - builder.append(_id); - BSONObj result; - try { - if (Helpers::findById(txn, db, ns, builder.done(), result)) { - // do we want to use Record::touch() here? it's pretty similar. - volatile char _dummy_char = '\0'; - - // Touch the first word on every page in order to fault it into memory - for (int i = 0; i < result.objsize(); i += g_minOSPageSizeBytes) { - _dummy_char += *(result.objdata() + i); - } - // hit the last page, in case we missed it above - _dummy_char += *(result.objdata() + result.objsize() - 1); +} + +// page in the data pages for a record associated with an object +void prefetchRecordPages(OperationContext* txn, Database* db, const char* ns, const BSONObj& obj) { + BSONElement _id; + if (obj.getObjectID(_id)) { + TimerHolder timer(&prefetchDocStats); + BSONObjBuilder builder; + builder.append(_id); + BSONObj result; + try { + if (Helpers::findById(txn, db, ns, builder.done(), result)) { + // do we want to use Record::touch() here? it's pretty similar. + volatile char _dummy_char = '\0'; + + // Touch the first word on every page in order to fault it into memory + for (int i = 0; i < result.objsize(); i += g_minOSPageSizeBytes) { + _dummy_char += *(result.objdata() + i); } + // hit the last page, in case we missed it above + _dummy_char += *(result.objdata() + result.objsize() - 1); } - catch(const DBException& e) { - LOG(2) << "ignoring exception in prefetchRecordPages(): " << e.what() << endl; - } + } catch (const DBException& e) { + LOG(2) << "ignoring exception in prefetchRecordPages(): " << e.what() << endl; } } -} // namespace - - // prefetch for an oplog operation - void prefetchPagesForReplicatedOp(OperationContext* txn, - Database* db, - const BSONObj& op) { - invariant(db); - const BackgroundSync::IndexPrefetchConfig prefetchConfig = - BackgroundSync::get()->getIndexPrefetchConfig(); - const char *opField; - const char *opType = op.getStringField("op"); - switch (*opType) { - case 'i': // insert - case 'd': // delete +} +} // namespace + +// prefetch for an oplog operation +void prefetchPagesForReplicatedOp(OperationContext* txn, Database* db, const BSONObj& op) { + invariant(db); + 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 + case 'u': // update opField = "o2"; break; default: // prefetch ignores other ops return; - } - - BSONObj obj = op.getObjectField(opField); - const char *ns = op.getStringField("ns"); - - // This will have to change for engines other than MMAP V1, because they might not have - // means for directly prefetching pages from the collection. For this purpose, acquire S - // lock on the database, instead of optimizing with IS. - Lock::CollectionLock collLock(txn->lockState(), ns, MODE_S); + } - Collection* collection = db->getCollection( ns ); - if (!collection) { - return; - } + BSONObj obj = op.getObjectField(opField); + const char* ns = op.getStringField("ns"); - LOG(4) << "index prefetch for op " << *opType << endl; + // This will have to change for engines other than MMAP V1, because they might not have + // means for directly prefetching pages from the collection. For this purpose, acquire S + // lock on the database, instead of optimizing with IS. + Lock::CollectionLock collLock(txn->lockState(), ns, MODE_S); - // 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); + Collection* collection = db->getCollection(ns); + if (!collection) { + return; + } - // 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, db, ns, obj); - } + LOG(4) << "index prefetch for op " << *opType << endl; + + // 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, db, ns, obj); } +} - class ReplIndexPrefetch : public ServerParameter { - public: - ReplIndexPrefetch() - : ServerParameter( ServerParameterSet::getGlobal(), "replIndexPrefetch" ) { - } +class ReplIndexPrefetch : public ServerParameter { +public: + ReplIndexPrefetch() : ServerParameter(ServerParameterSet::getGlobal(), "replIndexPrefetch") {} - virtual ~ReplIndexPrefetch() { - } + virtual ~ReplIndexPrefetch() {} - const char * _value() { - if (getGlobalReplicationCoordinator()->getReplicationMode() != - ReplicationCoordinator::modeReplSet) { - return "uninitialized"; - } - BackgroundSync::IndexPrefetchConfig ip = - BackgroundSync::get()->getIndexPrefetchConfig(); - switch (ip) { + 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: @@ -246,44 +229,44 @@ namespace { 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" ); - } + virtual void append(OperationContext* txn, BSONObjBuilder& b, const string& name) { + b.append(name, _value()); + } - std::string prefetch = newValueElement.valuestrsafe(); - return setFromString( prefetch ); + virtual Status set(const BSONElement& newValueElement) { + if (getGlobalReplicationCoordinator()->getReplicationMode() != + ReplicationCoordinator::modeReplSet) { + return Status(ErrorCodes::BadValue, "replication is not enabled"); } - virtual Status setFromString( const string& prefetch ) { - log() << "changing replication index prefetch behavior to " << prefetch << endl; + std::string prefetch = newValueElement.valuestrsafe(); + return setFromString(prefetch); + } - BackgroundSync::IndexPrefetchConfig prefetchConfig; + virtual Status setFromString(const string& prefetch) { + log() << "changing replication index prefetch behavior to " << prefetch << endl; - 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::IndexPrefetchConfig prefetchConfig; - BackgroundSync::get()->setIndexPrefetchConfig(prefetchConfig); - return Status::OK(); + 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); } - } replIndexPrefetch; + BackgroundSync::get()->setIndexPrefetchConfig(prefetchConfig); + return Status::OK(); + } + +} replIndexPrefetch; -} // namespace repl -} // namespace mongo +} // namespace repl +} // namespace mongo |