diff options
-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, 225 insertions, 197 deletions
diff --git a/src/mongo/db/catalog/database.cpp b/src/mongo/db/catalog/database.cpp index 4d873eca3e2..24c0ea01821 100644 --- a/src/mongo/db/catalog/database.cpp +++ b/src/mongo/db/catalog/database.cpp @@ -249,28 +249,25 @@ namespace mongo { } } - Status Database::setProfilingLevel(OperationContext* txn, int newLevel) { - if (_profile == newLevel) { - return Status::OK(); + 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; } - if (newLevel == 0) { + if ( newLevel == 0 ) { _profile = 0; - return Status::OK(); + return true; } - 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; - } + if (!getOrCreateProfileCollection(txn, this, true, &errmsg)) + return false; _profile = newLevel; - - return Status::OK(); + return true; } 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 ea4c2e80724..23354d3e307 100644 --- a/src/mongo/db/catalog/database.h +++ b/src/mongo/db/catalog/database.h @@ -69,12 +69,9 @@ namespace mongo { void clearTmpCollections(OperationContext* txn); /** - * 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. + * @return true if success. false if bad level or error creating profile ns */ - Status setProfilingLevel(OperationContext* txn, int newLevel); + bool setProfilingLevel( OperationContext* txn, int newLevel , std::string& errmsg ); /** * @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 dc7bd5106c5..c924375ef0a 100644 --- a/src/mongo/db/catalog/database_holder.cpp +++ b/src/mongo/db/catalog/database_holder.cpp @@ -30,15 +30,13 @@ #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" @@ -46,9 +44,11 @@ #include "mongo/util/log.h" namespace mongo { -namespace { - StringData _todb(const StringData& ns) { + static DatabaseHolder _dbHolder; + +namespace { + static 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,18 +62,12 @@ 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 6ad69c5bef2..dd3afdf2470 100644 --- a/src/mongo/db/catalog/database_holder.h +++ b/src/mongo/db/catalog/database_holder.h @@ -28,17 +28,13 @@ #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 2296fd58c9b..3703d555ff2 100644 --- a/src/mongo/db/client.h +++ b/src/mongo/db/client.h @@ -55,6 +55,7 @@ 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 31a5923ef3a..956f17a9c0b 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->getCurOp()->getOp()); + if ( currentOp->shouldDBProfile( executionTime ) ) { + profile( txn, *txn->getClient(), currentOp->getOp(), *currentOp ); } } diff --git a/src/mongo/db/dbcommands.cpp b/src/mongo/db/dbcommands.cpp index 90d38d20e00..19958e19d21 100644 --- a/src/mongo/db/dbcommands.cpp +++ b/src/mongo/db/dbcommands.cpp @@ -341,16 +341,10 @@ namespace mongo { } - bool run(OperationContext* txn, - const string& dbname, - BSONObj& cmdObj, - int options, - string& errmsg, - BSONObjBuilder& result, - bool fromRepl) { - + bool run(OperationContext* txn, const string& dbname, BSONObj& cmdObj, int, 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); @@ -360,26 +354,21 @@ namespace mongo { result.append("slowms", serverGlobalParams.slowMS); int p = (int) e.number(); - Status status = Status::OK(); + bool ok = false; - if (p == -1) - status = Status::OK(); + if ( p == -1 ) + ok = true; else if ( p >= 0 && p <= 2 ) { - status = ctx.db()->setProfilingLevel(txn, p); + ok = ctx.db()->setProfilingLevel( txn, p , errmsg ); } - const BSONElement slow = cmdObj["slowms"]; + BSONElement slow = cmdObj["slowms"]; if (slow.isNumber()) { serverGlobalParams.slowMS = slow.numberInt(); } - if (!status.isOK()) { - errmsg = status.reason(); - } - - return status.isOK(); + return ok; } - } cmdProfile; class CmdDiagLogging : public Command { diff --git a/src/mongo/db/dbeval.cpp b/src/mongo/db/dbeval.cpp index b873b64b760..72d7ebcd977 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,7 +153,6 @@ 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 ccc949ed6f0..aad9da30096 100644 --- a/src/mongo/db/instance.cpp +++ b/src/mongo/db/instance.cpp @@ -489,12 +489,17 @@ namespace { } if ( currentOp.shouldDBProfile( debug.executionTime ) ) { - if (lockedForWriting()) { + // 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() ) { MONGO_LOG_COMPONENT(1, logComponentForOp(op)) << "note: not profiling because doing fsync+lock" << endl; } else { - profile(txn, op); + profile(txn, c, op, currentOp); } } diff --git a/src/mongo/db/introspect.cpp b/src/mongo/db/introspect.cpp index 65a5bee8c86..6dd6c323a99 100644 --- a/src/mongo/db/introspect.cpp +++ b/src/mongo/db/introspect.cpp @@ -1,49 +1,53 @@ +// introspect.cpp + /** - * 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. - */ +* 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. +*/ #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kDefault -#include "mongo/platform/basic.h" - -#include "mongo/db/introspect.h" +#include "mongo/pch.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 { +namespace { void _appendUserInfo(const CurOp& c, BSONObjBuilder& builder, AuthorizationSession* authSession) { @@ -71,110 +75,141 @@ namespace { builder.append("user", bestUser.getUser().empty() ? "" : bestUser.getFullName()); } - } // namespace - - void profile(OperationContext* txn, int op) { - // initialize with 1kb to start, to avoid realloc later - BufBuilder profileBufBuilder(1024); - + /** + * @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 BSONObjBuilder b(profileBufBuilder); - txn->getCurOp()->debug().append(*txn->getCurOp(), b); + currentOp.debug().append(currentOp, b); b.appendDate("ts", jsTime()); - b.append("client", txn->getClient()->clientAddress()); + b.append("client", c.clientAddress()); - AuthorizationSession * authSession = txn->getClient()->getAuthorizationSession(); - _appendUserInfo(*txn->getCurOp(), b, authSession); + AuthorizationSession * authSession = c.getAuthorizationSession(); + _appendUserInfo(currentOp, b, authSession); BSONObj p = b.done(); - const string dbName(nsToDatabase(txn->getCurOp()->getNS())); - - try { - bool acquireDbXLock = false; - while (true) { - ScopedTransaction scopedXact(txn, MODE_IX); - - boost::scoped_ptr<AutoGetDb> autoGetDb; - if (acquireDbXLock) { - autoGetDb.reset(new AutoGetDb(txn, dbName, MODE_X)); - if (autoGetDb->getDb()) { - createProfileCollection(txn, autoGetDb->getDb()); - } - } - else { - 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); + WriteUnitOfWork wunit(txn); - Collection* const coll = db->getCollection(txn, db->getProfilingNS()); - if (coll) { - WriteUnitOfWork wuow(txn); - coll->insertDocument(txn, p, false); - wuow.commit(); + // 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; + } - break; + 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) ); } - 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; + 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; + } } else { - // Cannot write the profile information - break; + mongo::log() << "note: not profiling because db went away - " + << "probably a close on: " << currentOp.getNS(); } + 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; + } - Status createProfileCollection(OperationContext* txn, Database *db) { - invariant(txn->lockState()->isDbLockedForMode(db->name(), MODE_X)); - - const std::string dbProfilingNS(db->getProfilingNS()); + // does not exist! - Collection* const collection = db->getCollection(txn, dbProfilingNS); - if (collection) { - if (!collection->isCapped()) { - return Status(ErrorCodes::NamespaceExists, - str::stream() << dbProfilingNS << " exists but isn't capped"); + 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); } + return NULL; + } - return Status::OK(); + if ( !txn->lockState()->isDbLockedForMode( db->name(), MODE_X ) ) { + // can't create here + return NULL; } // system.profile namespace doesn't exist; create it - log() << "Creating profile collection: " << dbProfilingNS << endl; + log() << "creating profile collection: " << profileName << endl; CollectionOptions collectionOptions; collectionOptions.capped = true; collectionOptions.cappedSize = 1024 * 1024; WriteUnitOfWork wunit(txn); - invariant(db->createCollection(txn, dbProfilingNS, collectionOptions)); + collection = db->createCollection( txn, profileName, collectionOptions ); + invariant( collection ); wunit.commit(); - return Status::OK(); + return collection; } } // namespace mongo diff --git a/src/mongo/db/introspect.h b/src/mongo/db/introspect.h index 2a6ec2a477f..07af4ba6653 100644 --- a/src/mongo/db/introspect.h +++ b/src/mongo/db/introspect.h @@ -1,48 +1,63 @@ +// introspect.h +// system management stuff. + /** - * 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. - */ +* 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. +*/ #pragma once -#include "mongo/base/status.h" +#include <string> + +#include "mongo/db/curop.h" +#include "mongo/db/jsobj.h" namespace mongo { + class Collection; class Database; class OperationContext; - /** - * Invoked when database profile is enabled. - */ - void profile(OperationContext* txn, int op); + /* --- profiling -------------------------------------------- + do when database->profile is set + */ + + void profile(OperationContext* txn, const Client& c, int op, CurOp& currentOp); /** - * Pre-creates the profile collection for the specified database. - */ - Status createProfileCollection(OperationContext* txn, Database *db); + * 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); } // namespace mongo diff --git a/src/mongo/db/operation_context.h b/src/mongo/db/operation_context.h index 036e2be83b9..59c190733f8 100644 --- a/src/mongo/db/operation_context.h +++ b/src/mongo/db/operation_context.h @@ -30,6 +30,7 @@ #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" @@ -39,7 +40,6 @@ namespace mongo { class Client; class CurOp; class ProgressMeter; - class StringData; /** * This class encompasses the state required by an operation. |