diff options
author | Kaloian Manassiev <kaloian.manassiev@mongodb.com> | 2014-12-15 15:38:44 -0500 |
---|---|---|
committer | Kaloian Manassiev <kaloian.manassiev@mongodb.com> | 2014-12-17 11:27:15 -0500 |
commit | c5ebc6be8e3a865655acbc5ecd1cb3b96fdf44ff (patch) | |
tree | a8f5a606481a6f1d29deaf445da1a844366b409f | |
parent | f312681a62f15196d0638da50350dd6a9c693e36 (diff) | |
download | mongo-c5ebc6be8e3a865655acbc5ecd1cb3b96fdf44ff.tar.gz |
SERVER-16431 Simplify DB profile code
-rw-r--r-- | src/mongo/db/catalog/database.cpp | 27 | ||||
-rw-r--r-- | src/mongo/db/catalog/database.h | 7 | ||||
-rw-r--r-- | src/mongo/db/catalog/database_holder.cpp | 18 | ||||
-rw-r--r-- | src/mongo/db/catalog/database_holder.h | 6 | ||||
-rw-r--r-- | src/mongo/db/client.h | 1 | ||||
-rw-r--r-- | src/mongo/db/commands/write_commands/batch_executor.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/dbcommands.cpp | 27 | ||||
-rw-r--r-- | src/mongo/db/dbeval.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/instance.cpp | 9 | ||||
-rw-r--r-- | src/mongo/db/introspect.cpp | 233 | ||||
-rw-r--r-- | src/mongo/db/introspect.h | 83 | ||||
-rw-r--r-- | src/mongo/db/operation_context.h | 2 |
12 files changed, 197 insertions, 225 deletions
diff --git a/src/mongo/db/catalog/database.cpp b/src/mongo/db/catalog/database.cpp index 24c0ea01821..4d873eca3e2 100644 --- a/src/mongo/db/catalog/database.cpp +++ b/src/mongo/db/catalog/database.cpp @@ -249,25 +249,28 @@ namespace mongo { } } - bool Database::setProfilingLevel( OperationContext* txn, int newLevel , string& errmsg ) { - if ( _profile == newLevel ) - return true; - - if ( newLevel < 0 || newLevel > 2 ) { - errmsg = "profiling level has to be >=0 and <= 2"; - return false; + Status Database::setProfilingLevel(OperationContext* txn, int newLevel) { + if (_profile == newLevel) { + return Status::OK(); } - if ( newLevel == 0 ) { + if (newLevel == 0) { _profile = 0; - return true; + return Status::OK(); } - if (!getOrCreateProfileCollection(txn, this, true, &errmsg)) - return false; + if (newLevel < 0 || newLevel > 2) { + return Status(ErrorCodes::BadValue, "profiling level has to be >=0 and <= 2"); + } + + Status status = createProfileCollection(txn, this); + if (!status.isOK()) { + return status; + } _profile = newLevel; - return true; + + return Status::OK(); } void Database::getStats( OperationContext* opCtx, BSONObjBuilder* output, double scale ) { diff --git a/src/mongo/db/catalog/database.h b/src/mongo/db/catalog/database.h index 23354d3e307..ea4c2e80724 100644 --- a/src/mongo/db/catalog/database.h +++ b/src/mongo/db/catalog/database.h @@ -69,9 +69,12 @@ namespace mongo { void clearTmpCollections(OperationContext* txn); /** - * @return true if success. false if bad level or error creating profile ns + * Sets a new profiling level for the database and returns the outcome. + * + * @param txn Operation context which to use for creating the profiling collection. + * @param newLevel New profiling level to use. */ - bool setProfilingLevel( OperationContext* txn, int newLevel , std::string& errmsg ); + Status setProfilingLevel(OperationContext* txn, int newLevel); /** * @return true if ns is part of the database diff --git a/src/mongo/db/catalog/database_holder.cpp b/src/mongo/db/catalog/database_holder.cpp index c924375ef0a..dc7bd5106c5 100644 --- a/src/mongo/db/catalog/database_holder.cpp +++ b/src/mongo/db/catalog/database_holder.cpp @@ -30,13 +30,15 @@ #include "mongo/platform/basic.h" +#include "mongo/db/catalog/database_holder.h" + #include "mongo/db/audit.h" #include "mongo/db/auth/auth_index_d.h" #include "mongo/db/background.h" #include "mongo/db/client.h" #include "mongo/db/clientcursor.h" +#include "mongo/db/catalog/database.h" #include "mongo/db/catalog/database_catalog_entry.h" -#include "mongo/db/catalog/database_holder.h" #include "mongo/db/global_environment_experiment.h" #include "mongo/db/operation_context.h" #include "mongo/db/storage/storage_engine.h" @@ -44,11 +46,9 @@ #include "mongo/util/log.h" namespace mongo { - - static DatabaseHolder _dbHolder; - namespace { - static StringData _todb(const StringData& ns) { + + StringData _todb(const StringData& ns) { size_t i = ns.find('.'); if (i == std::string::npos) { uassert(13074, "db name can't be empty", ns.size()); @@ -62,12 +62,18 @@ namespace { return d; } -} + + + DatabaseHolder _dbHolder; + +} // namespace + DatabaseHolder& dbHolder() { return _dbHolder; } + Database* DatabaseHolder::get(OperationContext* txn, const StringData& ns) const { diff --git a/src/mongo/db/catalog/database_holder.h b/src/mongo/db/catalog/database_holder.h index dd3afdf2470..6ad69c5bef2 100644 --- a/src/mongo/db/catalog/database_holder.h +++ b/src/mongo/db/catalog/database_holder.h @@ -28,13 +28,17 @@ #pragma once +#include <set> + #include "mongo/base/string_data.h" -#include "mongo/db/catalog/database.h" #include "mongo/db/namespace_string.h" +#include "mongo/util/concurrency/mutex.h" #include "mongo/util/string_map.h" namespace mongo { + class Database; + /** * Registry of opened databases. */ diff --git a/src/mongo/db/client.h b/src/mongo/db/client.h index 3703d555ff2..2296fd58c9b 100644 --- a/src/mongo/db/client.h +++ b/src/mongo/db/client.h @@ -55,7 +55,6 @@ namespace mongo { class AuthenticationInfo; class Database; class CurOp; - class Client; class Collection; class AbstractMessagingPort; class Locker; diff --git a/src/mongo/db/commands/write_commands/batch_executor.cpp b/src/mongo/db/commands/write_commands/batch_executor.cpp index 956f17a9c0b..31a5923ef3a 100644 --- a/src/mongo/db/commands/write_commands/batch_executor.cpp +++ b/src/mongo/db/commands/write_commands/batch_executor.cpp @@ -638,8 +638,8 @@ namespace mongo { LOG(0) << currentOp->debug().report( *currentOp ) << endl; } - if ( currentOp->shouldDBProfile( executionTime ) ) { - profile( txn, *txn->getClient(), currentOp->getOp(), *currentOp ); + if (currentOp->shouldDBProfile(executionTime)) { + profile(txn, txn->getCurOp()->getOp()); } } diff --git a/src/mongo/db/dbcommands.cpp b/src/mongo/db/dbcommands.cpp index 19958e19d21..90d38d20e00 100644 --- a/src/mongo/db/dbcommands.cpp +++ b/src/mongo/db/dbcommands.cpp @@ -341,10 +341,16 @@ namespace mongo { } - bool run(OperationContext* txn, const string& dbname, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) { + bool run(OperationContext* txn, + const string& dbname, + BSONObj& cmdObj, + int options, + string& errmsg, + BSONObjBuilder& result, + bool fromRepl) { + // Needs to be locked exclusively, because creates the system.profile collection // in the local database. - // ScopedTransaction transaction(txn, MODE_IX); Lock::DBLock dbXLock(txn->lockState(), dbname, MODE_X); Client::Context ctx(txn, dbname); @@ -354,21 +360,26 @@ namespace mongo { result.append("slowms", serverGlobalParams.slowMS); int p = (int) e.number(); - bool ok = false; + Status status = Status::OK(); - if ( p == -1 ) - ok = true; + if (p == -1) + status = Status::OK(); else if ( p >= 0 && p <= 2 ) { - ok = ctx.db()->setProfilingLevel( txn, p , errmsg ); + status = ctx.db()->setProfilingLevel(txn, p); } - BSONElement slow = cmdObj["slowms"]; + const BSONElement slow = cmdObj["slowms"]; if (slow.isNumber()) { serverGlobalParams.slowMS = slow.numberInt(); } - return ok; + if (!status.isOK()) { + errmsg = status.reason(); + } + + return status.isOK(); } + } cmdProfile; class CmdDiagLogging : public Command { diff --git a/src/mongo/db/dbeval.cpp b/src/mongo/db/dbeval.cpp index 72d7ebcd977..b873b64b760 100644 --- a/src/mongo/db/dbeval.cpp +++ b/src/mongo/db/dbeval.cpp @@ -32,16 +32,16 @@ #include "mongo/platform/basic.h" -#include <time.h> - #include "mongo/bson/util/builder.h" #include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/authorization_manager_global.h" #include "mongo/db/auth/authorization_session.h" +#include "mongo/db/client.h" #include "mongo/db/commands.h" #include "mongo/db/introspect.h" #include "mongo/db/jsobj.h" #include "mongo/db/json.h" +#include "mongo/db/operation_context.h" #include "mongo/scripting/engine.h" #include "mongo/util/log.h" @@ -153,6 +153,7 @@ namespace mongo { ScopedTransaction transaction(txn, MODE_X); Lock::GlobalWrite lk(txn->lockState()); + // No WriteUnitOfWork necessary, as dbEval will create its own, see "nolock" case above Client::Context ctx(txn, dbname ); diff --git a/src/mongo/db/instance.cpp b/src/mongo/db/instance.cpp index aad9da30096..ccc949ed6f0 100644 --- a/src/mongo/db/instance.cpp +++ b/src/mongo/db/instance.cpp @@ -489,17 +489,12 @@ namespace { } if ( currentOp.shouldDBProfile( debug.executionTime ) ) { - // performance profiling is on - if (txn->lockState()->isReadLocked()) { - MONGO_LOG_COMPONENT(1, logComponentForOp(op)) - << "note: not profiling because recursive read lock" << endl; - } - else if ( lockedForWriting() ) { + if (lockedForWriting()) { MONGO_LOG_COMPONENT(1, logComponentForOp(op)) << "note: not profiling because doing fsync+lock" << endl; } else { - profile(txn, c, op, currentOp); + profile(txn, op); } } diff --git a/src/mongo/db/introspect.cpp b/src/mongo/db/introspect.cpp index 6dd6c323a99..65a5bee8c86 100644 --- a/src/mongo/db/introspect.cpp +++ b/src/mongo/db/introspect.cpp @@ -1,53 +1,49 @@ -// introspect.cpp - /** -* Copyright (C) 2008 10gen 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 <http://www.gnu.org/licenses/>. -* -* 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. -*/ + * Copyright (C) 2008 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 <http://www.gnu.org/licenses/>. + * + * 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::kDefault -#include "mongo/pch.h" +#include "mongo/platform/basic.h" + +#include "mongo/db/introspect.h" #include "mongo/bson/util/builder.h" #include "mongo/db/auth/authorization_manager.h" #include "mongo/db/auth/authorization_session.h" #include "mongo/db/auth/user_set.h" #include "mongo/db/curop.h" -#include "mongo/db/catalog/database_holder.h" -#include "mongo/db/introspect.h" #include "mongo/db/jsobj.h" -#include "mongo/db/storage_options.h" #include "mongo/db/catalog/collection.h" -#include "mongo/util/goodies.h" #include "mongo/util/log.h" namespace mongo { - namespace { + void _appendUserInfo(const CurOp& c, BSONObjBuilder& builder, AuthorizationSession* authSession) { @@ -75,141 +71,110 @@ namespace { builder.append("user", bestUser.getUser().empty() ? "" : bestUser.getFullName()); } + } // namespace - /** - * @return if collection existed or was created - */ - static bool _profile(OperationContext* txn, - const Client& c, - Database* db, - CurOp& currentOp, - BufBuilder& profileBufBuilder) { - dassert( db ); - - // build object + + void profile(OperationContext* txn, int op) { + // initialize with 1kb to start, to avoid realloc later + BufBuilder profileBufBuilder(1024); + BSONObjBuilder b(profileBufBuilder); - currentOp.debug().append(currentOp, b); + txn->getCurOp()->debug().append(*txn->getCurOp(), b); b.appendDate("ts", jsTime()); - b.append("client", c.clientAddress()); + b.append("client", txn->getClient()->clientAddress()); - AuthorizationSession * authSession = c.getAuthorizationSession(); - _appendUserInfo(currentOp, b, authSession); + AuthorizationSession * authSession = txn->getClient()->getAuthorizationSession(); + _appendUserInfo(*txn->getCurOp(), b, authSession); BSONObj p = b.done(); - WriteUnitOfWork wunit(txn); + const string dbName(nsToDatabase(txn->getCurOp()->getNS())); - // write: not replicated - // get or create the profiling collection - Collection* profileCollection = getOrCreateProfileCollection(txn, db); - if ( !profileCollection ) { - return false; - } - profileCollection->insertDocument( txn, p, false ); - wunit.commit(); - return true; - } + try { + bool acquireDbXLock = false; + while (true) { + ScopedTransaction scopedXact(txn, MODE_IX); - void profile(OperationContext* txn, const Client& c, int op, CurOp& currentOp) { - bool tryAgain = false; - while ( 1 ) { - try { - // initialize with 1kb to start, to avoid realloc later - // doing this outside the dblock to improve performance - BufBuilder profileBufBuilder(1024); - - // NOTE: It's kind of weird that we lock the op's namespace, but have to for now - // since we're sometimes inside the lock already - const string dbname(nsToDatabase(currentOp.getNS())); - scoped_ptr<Lock::DBLock> lk; - - // todo: this can be slow, perhaps can re-work - if ( !txn->lockState()->isDbLockedForMode( dbname, MODE_IX ) ) { - lk.reset( new Lock::DBLock( txn->lockState(), - dbname, - tryAgain ? MODE_X : MODE_IX) ); - } - Database* db = dbHolder().get(txn, dbname); - if (db != NULL) { - Lock::CollectionLock clk(txn->lockState(), db->getProfilingNS(), MODE_X); - Client::Context cx(txn, currentOp.getNS(), false); - if ( !_profile(txn, c, cx.db(), currentOp, profileBufBuilder ) && lk.get() ) { - if ( tryAgain ) { - // we couldn't profile, but that's ok, we should have logged already - break; - } - // we took an IX lock, so now we try again with an X lock - tryAgain = true; - continue; + boost::scoped_ptr<AutoGetDb> autoGetDb; + if (acquireDbXLock) { + autoGetDb.reset(new AutoGetDb(txn, dbName, MODE_X)); + if (autoGetDb->getDb()) { + createProfileCollection(txn, autoGetDb->getDb()); } } else { - mongo::log() << "note: not profiling because db went away - " - << "probably a close on: " << currentOp.getNS(); + autoGetDb.reset(new AutoGetDb(txn, dbName, MODE_IX)); + } + + Database* const db = autoGetDb->getDb(); + if (!db) { + // Database disappeared + log() << "note: not profiling because db went away for " + << txn->getCurOp()->getNS(); + break; + } + + Lock::CollectionLock collLock(txn->lockState(), db->getProfilingNS(), MODE_IX); + + Collection* const coll = db->getCollection(txn, db->getProfilingNS()); + if (coll) { + WriteUnitOfWork wuow(txn); + coll->insertDocument(txn, p, false); + wuow.commit(); + + break; + } + else if (!acquireDbXLock && !txn->lockState()->isLocked()) { + // Try to create the collection only if we are not under lock, in order to + // avoid deadlocks due to lock conversion. This would only be hit if someone + // deletes the profiler collection after setting profile level. + acquireDbXLock = true; + } + else { + // Cannot write the profile information + break; } - return; - } - catch (const AssertionException& assertionEx) { - warning() << "Caught Assertion while trying to profile " << opToString(op) - << " against " << currentOp.getNS() - << ": " << assertionEx.toString() << endl; - return; } } + catch (const AssertionException& assertionEx) { + warning() << "Caught Assertion while trying to profile " + << opToString(op) + << " against " << txn->getCurOp()->getNS() + << ": " << assertionEx.toString() << endl; + } } - Collection* getOrCreateProfileCollection(OperationContext* txn, - Database *db, - bool force, - string* errmsg ) { - fassert(16372, db); - const char* profileName = db->getProfilingNS(); - Collection* collection = db->getCollection( txn, profileName ); - - if ( collection ) { - if ( !collection->isCapped() ) { - string myerrmsg = str::stream() << profileName << " exists but isn't capped"; - log() << myerrmsg << endl; - if ( errmsg ) - *errmsg = myerrmsg; - return NULL; - } - return collection; - } - // does not exist! + Status createProfileCollection(OperationContext* txn, Database *db) { + invariant(txn->lockState()->isDbLockedForMode(db->name(), MODE_X)); + + const std::string dbProfilingNS(db->getProfilingNS()); - if ( force == false && serverGlobalParams.defaultProfile == false ) { - // we don't want it, so why are we here? - static time_t last = time(0) - 10; // warn the first time - if( time(0) > last+10 ) { - log() << "profile: warning ns " << profileName << " does not exist" << endl; - last = time(0); + Collection* const collection = db->getCollection(txn, dbProfilingNS); + if (collection) { + if (!collection->isCapped()) { + return Status(ErrorCodes::NamespaceExists, + str::stream() << dbProfilingNS << " exists but isn't capped"); } - return NULL; - } - if ( !txn->lockState()->isDbLockedForMode( db->name(), MODE_X ) ) { - // can't create here - return NULL; + return Status::OK(); } // system.profile namespace doesn't exist; create it - log() << "creating profile collection: " << profileName << endl; + log() << "Creating profile collection: " << dbProfilingNS << endl; CollectionOptions collectionOptions; collectionOptions.capped = true; collectionOptions.cappedSize = 1024 * 1024; WriteUnitOfWork wunit(txn); - collection = db->createCollection( txn, profileName, collectionOptions ); - invariant( collection ); + invariant(db->createCollection(txn, dbProfilingNS, collectionOptions)); wunit.commit(); - return collection; + return Status::OK(); } } // namespace mongo diff --git a/src/mongo/db/introspect.h b/src/mongo/db/introspect.h index 07af4ba6653..2a6ec2a477f 100644 --- a/src/mongo/db/introspect.h +++ b/src/mongo/db/introspect.h @@ -1,63 +1,48 @@ -// introspect.h -// system management stuff. - /** -* Copyright (C) 2008 10gen 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 <http://www.gnu.org/licenses/>. -* -* 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. -*/ + * Copyright (C) 2008 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 <http://www.gnu.org/licenses/>. + * + * 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. + */ #pragma once -#include <string> - -#include "mongo/db/curop.h" -#include "mongo/db/jsobj.h" +#include "mongo/base/status.h" namespace mongo { - class Collection; class Database; class OperationContext; - /* --- profiling -------------------------------------------- - do when database->profile is set - */ - - void profile(OperationContext* txn, const Client& c, int op, CurOp& currentOp); + /** + * Invoked when database profile is enabled. + */ + void profile(OperationContext* txn, int op); /** - * Get (or create) the profile collection - * - * @param db Database in which to create the profile collection - * @param force Always create the collection if it does not exist - * @return Collection for the newly created collection, or NULL on error - **/ - Collection* getOrCreateProfileCollection(OperationContext* txn, - Database *db, - bool force = false, - std::string* errmsg = NULL); + * Pre-creates the profile collection for the specified database. + */ + Status createProfileCollection(OperationContext* txn, Database *db); } // namespace mongo diff --git a/src/mongo/db/operation_context.h b/src/mongo/db/operation_context.h index 59c190733f8..036e2be83b9 100644 --- a/src/mongo/db/operation_context.h +++ b/src/mongo/db/operation_context.h @@ -30,7 +30,6 @@ #include "mongo/base/disallow_copying.h" #include "mongo/base/status.h" -#include "mongo/base/string_data.h" #include "mongo/db/storage/recovery_unit.h" #include "mongo/db/concurrency/locker.h" #include "mongo/db/concurrency/d_concurrency.h" @@ -40,6 +39,7 @@ namespace mongo { class Client; class CurOp; class ProgressMeter; + class StringData; /** * This class encompasses the state required by an operation. |