diff options
Diffstat (limited to 'src/mongo/db/curop.h')
| -rw-r--r-- | src/mongo/db/curop.h | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/src/mongo/db/curop.h b/src/mongo/db/curop.h new file mode 100644 index 00000000000..7b4f30f7568 --- /dev/null +++ b/src/mongo/db/curop.h @@ -0,0 +1,282 @@ +// @file curop.h + +/* + * Copyright (C) 2010 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/>. + */ + + +#pragma once + +#include "namespace-inl.h" +#include "client.h" +#include "../bson/util/atomic_int.h" +#include "../util/concurrency/spin_lock.h" +#include "../util/time_support.h" +#include "../util/net/hostandport.h" +#include "../util/progress_meter.h" + +namespace mongo { + + class CurOp; + + /* lifespan is different than CurOp because of recursives with DBDirectClient */ + class OpDebug { + public: + OpDebug() : ns(""){ reset(); } + + void reset(); + + string report( const CurOp& curop ) const; + void append( const CurOp& curop, BSONObjBuilder& b ) const; + + // ------------------- + + StringBuilder extra; // weird things we need to fix later + + // basic options + int op; + bool iscommand; + Namespace ns; + BSONObj query; + BSONObj updateobj; + + // detailed options + long long cursorid; + int ntoreturn; + int ntoskip; + bool exhaust; + + // debugging/profile info + long long nscanned; + bool idhack; // indicates short circuited code path on an update to make the update faster + bool scanAndOrder; // scanandorder query plan aspect was used + long long nupdated; // number of records updated + long long nmoved; // updates resulted in a move (moves are expensive) + bool fastmod; + bool fastmodinsert; // upsert of an $operation. builds a default object + bool upsert; // true if the update actually did an insert + int keyUpdates; + + // error handling + ExceptionInfo exceptionInfo; + + // response info + int executionTime; + int nreturned; + int responseLength; + }; + + /** + * stores a copy of a bson obj in a fixed size buffer + * if its too big for the buffer, says "too big" + * useful for keeping a copy around indefinitely without wasting a lot of space or doing malloc + */ + class CachedBSONObj { + public: + enum { TOO_BIG_SENTINEL = 1 } ; + static BSONObj _tooBig; // { $msg : "query not recording (too large)" } + + CachedBSONObj() { + _size = (int*)_buf; + reset(); + } + + void reset( int sz = 0 ) { + _lock.lock(); + _reset( sz ); + _lock.unlock(); + } + + void set( const BSONObj& o ) { + scoped_spinlock lk(_lock); + int sz = o.objsize(); + if ( sz > (int) sizeof(_buf) ) { + _reset(TOO_BIG_SENTINEL); + } + else { + memcpy(_buf, o.objdata(), sz ); + } + } + + int size() const { return *_size; } + bool have() const { return size() > 0; } + + BSONObj get() const { + scoped_spinlock lk(_lock); + return _get(); + } + + void append( BSONObjBuilder& b , const StringData& name ) const { + scoped_spinlock lk(_lock); + BSONObj temp = _get(); + b.append( name , temp ); + } + + private: + /** you have to be locked when you call this */ + BSONObj _get() const { + int sz = size(); + if ( sz == 0 ) + return BSONObj(); + if ( sz == TOO_BIG_SENTINEL ) + return _tooBig; + return BSONObj( _buf ).copy(); + } + + /** you have to be locked when you call this */ + void _reset( int sz ) { _size[0] = sz; } + + mutable SpinLock _lock; + int * _size; + char _buf[512]; + }; + + /* Current operation (for the current Client). + an embedded member of Client class, and typically used from within the mutex there. + */ + class CurOp : boost::noncopyable { + public: + CurOp( Client * client , CurOp * wrapped = 0 ); + ~CurOp(); + + bool haveQuery() const { return _query.have(); } + BSONObj query() { return _query.get(); } + void appendQuery( BSONObjBuilder& b , const StringData& name ) const { _query.append( b , name ); } + + void ensureStarted(); + bool isStarted() const { return _start > 0; } + void enter( Client::Context * context ); + void leave( Client::Context * context ); + void reset(); + void reset( const HostAndPort& remote, int op ); + void markCommand() { _command = true; } + OpDebug& debug() { return _debug; } + int profileLevel() const { return _dbprofile; } + const char * getNS() const { return _ns; } + + bool shouldDBProfile( int ms ) const { + if ( _dbprofile <= 0 ) + return false; + + return _dbprofile >= 2 || ms >= cmdLine.slowMS; + } + + AtomicUInt opNum() const { return _opNum; } + + /** if this op is running */ + bool active() const { return _active; } + + bool displayInCurop() const { return _active && ! _suppressFromCurop; } + int getOp() const { return _op; } + unsigned long long startTime() { // micros + ensureStarted(); + return _start; + } + void done() { + _active = false; + _end = curTimeMicros64(); + } + unsigned long long totalTimeMicros() { + massert( 12601 , "CurOp not marked done yet" , ! _active ); + return _end - startTime(); + } + int totalTimeMillis() { return (int) (totalTimeMicros() / 1000); } + int elapsedMillis() { + unsigned long long total = curTimeMicros64() - startTime(); + return (int) (total / 1000); + } + int elapsedSeconds() { return elapsedMillis() / 1000; } + void setQuery(const BSONObj& query) { _query.set( query ); } + Client * getClient() const { return _client; } + BSONObj info(); + BSONObj infoNoauth(); + string getRemoteString( bool includePort = true ) { return _remote.toString(includePort); } + ProgressMeter& setMessage( const char * msg , unsigned long long progressMeterTotal = 0 , int secondsBetween = 3 ); + string getMessage() const { return _message.toString(); } + ProgressMeter& getProgressMeter() { return _progressMeter; } + CurOp *parent() const { return _wrapped; } + void kill() { _killed = true; } + bool killed() const { return _killed; } + void yielded() { _numYields++; } + int numYields() const { return _numYields; } + void suppressFromCurop() { _suppressFromCurop = true; } + + long long getExpectedLatencyMs() const { return _expectedLatencyMs; } + void setExpectedLatencyMs( long long latency ) { _expectedLatencyMs = latency; } + + void recordGlobalTime( long long micros ) const; + + const LockStat& lockStat() const { return _lockStat; } + LockStat& lockStat() { return _lockStat; } + private: + friend class Client; + void _reset(); + + static AtomicUInt _nextOpNum; + Client * _client; + CurOp * _wrapped; + unsigned long long _start; + unsigned long long _end; + bool _active; + bool _suppressFromCurop; // unless $all is set + int _op; + bool _command; + int _dbprofile; // 0=off, 1=slow, 2=all + AtomicUInt _opNum; // todo: simple being "unsigned" may make more sense here + char _ns[Namespace::MaxNsLen+2]; + HostAndPort _remote; // CAREFUL here with thread safety + CachedBSONObj _query; // CachedBSONObj is thread safe + OpDebug _debug; + ThreadSafeString _message; + ProgressMeter _progressMeter; + volatile bool _killed; + int _numYields; + LockStat _lockStat; + + // this is how much "extra" time a query might take + // a writebacklisten for example will block for 30s + // so this should be 30000 in that case + long long _expectedLatencyMs; + + + }; + + /* _globalKill: we are shutting down + otherwise kill attribute set on specified CurOp + this class does not handle races between interruptJs and the checkForInterrupt functions - those must be + handled by the client of this class + */ + extern class KillCurrentOp { + public: + void killAll(); + void kill(AtomicUInt i); + + /** @return true if global interrupt and should terminate the operation */ + bool globalInterruptCheck() const { return _globalKill; } + + /** + * @param heedMutex if true and have a write lock, won't kill op since it might be unsafe + */ + void checkForInterrupt( bool heedMutex = true ); + + /** @return "" if not interrupted. otherwise, you should stop. */ + const char *checkForInterruptNoAssert(); + + private: + void interruptJs( AtomicUInt *op ); + volatile bool _globalKill; + } killCurrentOp; + +} |
