| assert_util.h | | assert_util.h | |
| | | | |
| skipping to change at line 20 | | skipping to change at line 20 | |
| * | | * | |
| * Unless required by applicable law or agreed to in writing, software | | * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | | * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | |
| * See the License for the specific language governing permissions and | | * See the License for the specific language governing permissions and | |
| * limitations under the License. | | * limitations under the License. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
|
| #include "../db/lasterror.h" | | #include "../bson/inline_decls.h" | |
| | | | |
| // MONGO_NORETURN undefed at end of file | | // MONGO_NORETURN undefed at end of file | |
| #ifdef __GNUC__ | | #ifdef __GNUC__ | |
| # define MONGO_NORETURN __attribute__((__noreturn__)) | | # define MONGO_NORETURN __attribute__((__noreturn__)) | |
| #else | | #else | |
| # define MONGO_NORETURN | | # define MONGO_NORETURN | |
| #endif | | #endif | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| enum CommonErrorCodes { | | enum CommonErrorCodes { | |
| DatabaseDifferCaseCode = 13297 , | | DatabaseDifferCaseCode = 13297 , | |
|
| StaleConfigInContextCode = 13388 | | SendStaleConfigCode = 13388 , | |
| | | RecvStaleConfigCode = 9996 | |
| }; | | }; | |
| | | | |
| class AssertionCount { | | class AssertionCount { | |
| public: | | public: | |
| AssertionCount(); | | AssertionCount(); | |
| void rollover(); | | void rollover(); | |
| void condrollover( int newValue ); | | void condrollover( int newValue ); | |
| | | | |
| int regular; | | int regular; | |
| int warning; | | int warning; | |
| int msg; | | int msg; | |
| int user; | | int user; | |
| int rollovers; | | int rollovers; | |
| }; | | }; | |
| | | | |
| extern AssertionCount assertionCount; | | extern AssertionCount assertionCount; | |
| | | | |
|
| | | class BSONObjBuilder; | |
| | | | |
| struct ExceptionInfo { | | struct ExceptionInfo { | |
| ExceptionInfo() : msg(""),code(-1) {} | | ExceptionInfo() : msg(""),code(-1) {} | |
| ExceptionInfo( const char * m , int c ) | | ExceptionInfo( const char * m , int c ) | |
| : msg( m ) , code( c ) { | | : msg( m ) , code( c ) { | |
| } | | } | |
|
| ExceptionInfo( const string& m , int c ) | | ExceptionInfo( const std::string& m , int c ) | |
| : msg( m ) , code( c ) { | | : msg( m ) , code( c ) { | |
| } | | } | |
| void append( BSONObjBuilder& b , const char * m = "$err" , const ch
ar * c = "code" ) const ; | | void append( BSONObjBuilder& b , const char * m = "$err" , const ch
ar * c = "code" ) const ; | |
|
| string toString() const { stringstream ss; ss << "exception: " << c
ode << " " << msg; return ss.str(); } | | std::string toString() const; | |
| bool empty() const { return msg.empty(); } | | bool empty() const { return msg.empty(); } | |
|
| | | | |
| void reset(){ msg = ""; code=-1; } | | void reset(){ msg = ""; code=-1; } | |
|
| | | std::string msg; | |
| string msg; | | | |
| int code; | | int code; | |
| }; | | }; | |
| | | | |
| /** helper class that builds error strings. lighter weight than a Stri
ngBuilder, albeit less flexible. | | /** helper class that builds error strings. lighter weight than a Stri
ngBuilder, albeit less flexible. | |
| NOINLINE_DECL used in the constructor implementations as we are ass
uming this is a cold code path when used. | | NOINLINE_DECL used in the constructor implementations as we are ass
uming this is a cold code path when used. | |
| | | | |
| example: | | example: | |
| throw UserException(123, ErrorMsg("blah", num_val)); | | throw UserException(123, ErrorMsg("blah", num_val)); | |
| */ | | */ | |
| class ErrorMsg { | | class ErrorMsg { | |
| public: | | public: | |
| ErrorMsg(const char *msg, char ch); | | ErrorMsg(const char *msg, char ch); | |
| ErrorMsg(const char *msg, unsigned val); | | ErrorMsg(const char *msg, unsigned val); | |
|
| operator string() const { return buf; } | | operator std::string() const { return buf; } | |
| private: | | private: | |
| char buf[256]; | | char buf[256]; | |
| }; | | }; | |
| | | | |
|
| | | class DBException; | |
| | | std::string causedBy( const DBException& e ); | |
| | | std::string causedBy( const std::string& e ); | |
| | | bool inShutdown(); | |
| | | | |
| | | /** Most mongo exceptions inherit from this; this is commonly caught in | |
| | | most threads */ | |
| class DBException : public std::exception { | | class DBException : public std::exception { | |
| public: | | public: | |
|
| DBException( const ExceptionInfo& ei ) : _ei(ei) {} | | DBException( const ExceptionInfo& ei ) : _ei(ei) { traceIfNeeded(*t | |
| DBException( const char * msg , int code ) : _ei(msg,code) {} | | his); } | |
| DBException( const string& msg , int code ) : _ei(msg,code) {} | | DBException( const char * msg , int code ) : _ei(msg,code) { traceI | |
| | | fNeeded(*this); } | |
| | | DBException( const std::string& msg , int code ) : _ei(msg,code) { | |
| | | traceIfNeeded(*this); } | |
| virtual ~DBException() throw() { } | | virtual ~DBException() throw() { } | |
| | | | |
| virtual const char* what() const throw() { return _ei.msg.c_str();
} | | virtual const char* what() const throw() { return _ei.msg.c_str();
} | |
| virtual int getCode() const { return _ei.code; } | | virtual int getCode() const { return _ei.code; } | |
| | | | |
|
| virtual void appendPrefix( stringstream& ss ) const { } | | virtual void appendPrefix( std::stringstream& ss ) const { } | |
| | | virtual void addContext( const std::string& str ) { | |
| virtual string toString() const { | | _ei.msg = str + causedBy( _ei.msg ); | |
| stringstream ss; ss << getCode() << " " << what(); return ss.st | | | |
| r(); | | | |
| return ss.str(); | | | |
| } | | } | |
| | | | |
|
| | | virtual std::string toString() const; | |
| | | | |
| const ExceptionInfo& getInfo() const { return _ei; } | | const ExceptionInfo& getInfo() const { return _ei; } | |
|
| | | private: | |
| | | static void traceIfNeeded( const DBException& e ); | |
| | | public: | |
| | | static bool traceExceptions; | |
| | | | |
| protected: | | protected: | |
| ExceptionInfo _ei; | | ExceptionInfo _ei; | |
| }; | | }; | |
| | | | |
| class AssertionException : public DBException { | | class AssertionException : public DBException { | |
| public: | | public: | |
| | | | |
| AssertionException( const ExceptionInfo& ei ) : DBException(ei) {} | | AssertionException( const ExceptionInfo& ei ) : DBException(ei) {} | |
| AssertionException( const char * msg , int code ) : DBException(msg
,code) {} | | AssertionException( const char * msg , int code ) : DBException(msg
,code) {} | |
|
| AssertionException( const string& msg , int code ) : DBException(ms
g,code) {} | | AssertionException( const std::string& msg , int code ) : DBExcepti
on(msg,code) {} | |
| | | | |
| virtual ~AssertionException() throw() { } | | virtual ~AssertionException() throw() { } | |
| | | | |
| virtual bool severe() { return true; } | | virtual bool severe() { return true; } | |
| virtual bool isUserAssertion() { return false; } | | virtual bool isUserAssertion() { return false; } | |
| | | | |
| /* true if an interrupted exception - see KillCurrentOp */ | | /* true if an interrupted exception - see KillCurrentOp */ | |
| bool interrupted() { | | bool interrupted() { | |
| return _ei.code == 11600 || _ei.code == 11601; | | return _ei.code == 11600 || _ei.code == 11601; | |
| } | | } | |
| }; | | }; | |
| | | | |
| /* UserExceptions are valid errors that a user can cause, like out of d
isk space or duplicate key */ | | /* UserExceptions are valid errors that a user can cause, like out of d
isk space or duplicate key */ | |
| class UserException : public AssertionException { | | class UserException : public AssertionException { | |
| public: | | public: | |
|
| UserException(int c , const string& m) : AssertionException( m , c | | UserException(int c , const std::string& m) : AssertionException( m | |
| ) {} | | , c ) {} | |
| | | | |
| virtual bool severe() { return false; } | | virtual bool severe() { return false; } | |
| virtual bool isUserAssertion() { return true; } | | virtual bool isUserAssertion() { return true; } | |
|
| virtual void appendPrefix( stringstream& ss ) const { ss << "useras
sert:"; } | | virtual void appendPrefix( std::stringstream& ss ) const; | |
| }; | | }; | |
| | | | |
| class MsgAssertionException : public AssertionException { | | class MsgAssertionException : public AssertionException { | |
| public: | | public: | |
| MsgAssertionException( const ExceptionInfo& ei ) : AssertionExcepti
on( ei ) {} | | MsgAssertionException( const ExceptionInfo& ei ) : AssertionExcepti
on( ei ) {} | |
|
| MsgAssertionException(int c, const string& m) : AssertionException(
m , c ) {} | | MsgAssertionException(int c, const std::string& m) : AssertionExcep
tion( m , c ) {} | |
| virtual bool severe() { return false; } | | virtual bool severe() { return false; } | |
|
| virtual void appendPrefix( stringstream& ss ) const { ss << "masser
t:"; } | | virtual void appendPrefix( std::stringstream& ss ) const; | |
| }; | | }; | |
| | | | |
| void asserted(const char *msg, const char *file, unsigned line) MONGO_N
ORETURN; | | void asserted(const char *msg, const char *file, unsigned line) MONGO_N
ORETURN; | |
| void wasserted(const char *msg, const char *file, unsigned line); | | void wasserted(const char *msg, const char *file, unsigned line); | |
| void verifyFailed( int msgid ); | | void verifyFailed( int msgid ); | |
|
| | | void fassertFailed( int msgid ); | |
| | | | |
| /** a "user assertion". throws UserAssertion. logs. typically used f
or errors that a user | | /** a "user assertion". throws UserAssertion. logs. typically used f
or errors that a user | |
| could cause, such as duplicate key, disk full, etc. | | could cause, such as duplicate key, disk full, etc. | |
| */ | | */ | |
| void uasserted(int msgid, const char *msg) MONGO_NORETURN; | | void uasserted(int msgid, const char *msg) MONGO_NORETURN; | |
|
| inline void uasserted(int msgid , string msg) { uasserted(msgid, msg.c_
str()); } | | void uasserted(int msgid , const std::string &msg); | |
| | | | |
| /** reported via lasterror, but don't throw exception */ | | /** reported via lasterror, but don't throw exception */ | |
| void uassert_nothrow(const char *msg); | | void uassert_nothrow(const char *msg); | |
| | | | |
|
| /** msgassert and massert are for errors that are internal but have a w
ell defined error text string. | | /** msgassert and massert are for errors that are internal but have a w
ell defined error text std::string. | |
| a stack trace is logged. | | a stack trace is logged. | |
| */ | | */ | |
| void msgassertedNoTrace(int msgid, const char *msg) MONGO_NORETURN; | | void msgassertedNoTrace(int msgid, const char *msg) MONGO_NORETURN; | |
|
| inline void msgassertedNoTrace(int msgid, const string& msg) { msgasser
tedNoTrace( msgid , msg.c_str() ); } | | inline void msgassertedNoTrace(int msgid, const std::string& msg) { msg
assertedNoTrace( msgid , msg.c_str() ); } | |
| void msgasserted(int msgid, const char *msg) MONGO_NORETURN; | | void msgasserted(int msgid, const char *msg) MONGO_NORETURN; | |
|
| inline void msgasserted(int msgid, string msg) { msgasserted(msgid, msg
.c_str()); } | | void msgasserted(int msgid, const std::string &msg); | |
| | | | |
| /* convert various types of exceptions to strings */ | | /* convert various types of exceptions to strings */ | |
|
| inline string causedBy( const char* e ){ return (string)" :: caused by | | inline std::string causedBy( const char* e ){ return (std::string)" :: | |
| :: " + e; } | | caused by :: " + e; } | |
| inline string causedBy( const DBException& e ){ return causedBy( e.toSt | | inline std::string causedBy( const DBException& e ){ return causedBy( e | |
| ring().c_str() ); } | | .toString().c_str() ); } | |
| inline string causedBy( const std::exception& e ){ return causedBy( e.w | | inline std::string causedBy( const std::exception& e ){ return causedBy | |
| hat() ); } | | ( e.what() ); } | |
| inline string causedBy( const string& e ){ return causedBy( e.c_str() ) | | inline std::string causedBy( const std::string& e ){ return causedBy( e | |
| ; } | | .c_str() ); } | |
| | | | |
| /** in the mongodb source, use verify() instead of assert(). verify is
always evaluated even in release builds. */ | | /** in the mongodb source, use verify() instead of assert(). verify is
always evaluated even in release builds. */ | |
| inline void verify( int msgid , bool testOK ) { if ( ! testOK ) verifyF
ailed( msgid ); } | | inline void verify( int msgid , bool testOK ) { if ( ! testOK ) verifyF
ailed( msgid ); } | |
| | | | |
|
| | | /** abends on condition failure */ | |
| | | inline void fassert( int msgid , bool testOK ) { if ( ! testOK ) fasser | |
| | | tFailed( msgid ); } | |
| | | | |
| | | #ifdef assert | |
| | | #undef assert | |
| | | #endif | |
| | | | |
| #define MONGO_assert(_Expression) (void)( MONGO_likely(!!(_Expression)) ||
(mongo::asserted(#_Expression, __FILE__, __LINE__), 0) ) | | #define MONGO_assert(_Expression) (void)( MONGO_likely(!!(_Expression)) ||
(mongo::asserted(#_Expression, __FILE__, __LINE__), 0) ) | |
|
| | | #define assert MONGO_assert | |
| | | | |
| /* "user assert". if asserts, user did something wrong, not our code *
/ | | /* "user assert". if asserts, user did something wrong, not our code *
/ | |
| #define MONGO_uassert(msgid, msg, expr) (void)( MONGO_likely(!!(expr)) || (
mongo::uasserted(msgid, msg), 0) ) | | #define MONGO_uassert(msgid, msg, expr) (void)( MONGO_likely(!!(expr)) || (
mongo::uasserted(msgid, msg), 0) ) | |
|
| | | #define uassert MONGO_uassert | |
| | | | |
| /* warning only - keeps going */ | | /* warning only - keeps going */ | |
| #define MONGO_wassert(_Expression) (void)( MONGO_likely(!!(_Expression)) ||
(mongo::wasserted(#_Expression, __FILE__, __LINE__), 0) ) | | #define MONGO_wassert(_Expression) (void)( MONGO_likely(!!(_Expression)) ||
(mongo::wasserted(#_Expression, __FILE__, __LINE__), 0) ) | |
|
| | | #define wassert MONGO_wassert | |
| | | | |
| /* display a message, no context, and throw assertionexception | | /* display a message, no context, and throw assertionexception | |
| | | | |
| easy way to throw an exception and log something without our stack t
race | | easy way to throw an exception and log something without our stack t
race | |
| display happening. | | display happening. | |
| */ | | */ | |
| #define MONGO_massert(msgid, msg, expr) (void)( MONGO_likely(!!(expr)) || (
mongo::msgasserted(msgid, msg), 0) ) | | #define MONGO_massert(msgid, msg, expr) (void)( MONGO_likely(!!(expr)) || (
mongo::msgasserted(msgid, msg), 0) ) | |
|
| | | #define massert MONGO_massert | |
| | | | |
| /* dassert is 'debug assert' -- might want to turn off for production a
s these | | /* dassert is 'debug assert' -- might want to turn off for production a
s these | |
| could be slow. | | could be slow. | |
| */ | | */ | |
| #if defined(_DEBUG) | | #if defined(_DEBUG) | |
| # define MONGO_dassert assert | | # define MONGO_dassert assert | |
| #else | | #else | |
| # define MONGO_dassert(x) | | # define MONGO_dassert(x) | |
| #endif | | #endif | |
|
| | | #define dassert MONGO_dassert | |
| | | | |
| // some special ids that we want to duplicate | | // some special ids that we want to duplicate | |
| | | | |
| // > 10000 asserts | | // > 10000 asserts | |
| // < 10000 UserException | | // < 10000 UserException | |
| | | | |
| enum { ASSERT_ID_DUPKEY = 11000 }; | | enum { ASSERT_ID_DUPKEY = 11000 }; | |
| | | | |
| /* throws a uassertion with an appropriate msg */ | | /* throws a uassertion with an appropriate msg */ | |
|
| void streamNotGood( int code , string msg , std::ios& myios ) MONGO_NOR
ETURN; | | void streamNotGood( int code , std::string msg , std::ios& myios ) MONG
O_NORETURN; | |
| | | | |
|
| inline void assertStreamGood(unsigned msgid, string msg, std::ios& myio
s) { | | inline void assertStreamGood(unsigned msgid, std::string msg, std::ios&
myios) { | |
| if( !myios.good() ) streamNotGood(msgid, msg, myios); | | if( !myios.good() ) streamNotGood(msgid, msg, myios); | |
| } | | } | |
| | | | |
|
| string demangleName( const type_info& typeinfo ); | | std::string demangleName( const type_info& typeinfo ); | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
| #define BOOST_CHECK_EXCEPTION MONGO_BOOST_CHECK_EXCEPTION | | #define BOOST_CHECK_EXCEPTION MONGO_BOOST_CHECK_EXCEPTION | |
| #define MONGO_BOOST_CHECK_EXCEPTION( expression ) \ | | #define MONGO_BOOST_CHECK_EXCEPTION( expression ) \ | |
| try { \ | | try { \ | |
| expression; \ | | expression; \ | |
| } catch ( const std::exception &e ) { \ | | } catch ( const std::exception &e ) { \ | |
| stringstream ss; \ | | stringstream ss; \ | |
| ss << "caught boost exception: " << e.what() << ' ' << __FILE__ <<
' ' << __LINE__; \ | | ss << "caught boost exception: " << e.what() << ' ' << __FILE__ <<
' ' << __LINE__; \ | |
| | | | |
| skipping to change at line 235 | | skipping to change at line 258 | |
| } | | } | |
| | | | |
| #define MONGO_BOOST_CHECK_EXCEPTION_WITH_MSG( expression, msg ) \ | | #define MONGO_BOOST_CHECK_EXCEPTION_WITH_MSG( expression, msg ) \ | |
| try { \ | | try { \ | |
| expression; \ | | expression; \ | |
| } catch ( const std::exception &e ) { \ | | } catch ( const std::exception &e ) { \ | |
| stringstream ss; \ | | stringstream ss; \ | |
| ss << msg << " caught boost exception: " << e.what(); \ | | ss << msg << " caught boost exception: " << e.what(); \ | |
| msgasserted( 14043 , ss.str() ); \ | | msgasserted( 14043 , ss.str() ); \ | |
| } catch ( ... ) { \ | | } catch ( ... ) { \ | |
|
| msgasserted( 14044 , string("unknown boost failed ") + msg ); \ | | msgasserted( 14044 , std::string("unknown boost failed ") + msg );
\ | |
| } | | } | |
| | | | |
| #define DESTRUCTOR_GUARD MONGO_DESTRUCTOR_GUARD | | #define DESTRUCTOR_GUARD MONGO_DESTRUCTOR_GUARD | |
| #define MONGO_DESTRUCTOR_GUARD( expression ) \ | | #define MONGO_DESTRUCTOR_GUARD( expression ) \ | |
| try { \ | | try { \ | |
| expression; \ | | expression; \ | |
| } catch ( const std::exception &e ) { \ | | } catch ( const std::exception &e ) { \ | |
| problem() << "caught exception (" << e.what() << ") in destructor (
" << __FUNCTION__ << ")" << endl; \ | | problem() << "caught exception (" << e.what() << ") in destructor (
" << __FUNCTION__ << ")" << endl; \ | |
| } catch ( ... ) { \ | | } catch ( ... ) { \ | |
| problem() << "caught unknown exception in destructor (" << __FUNCTI
ON__ << ")" << endl; \ | | problem() << "caught unknown exception in destructor (" << __FUNCTI
ON__ << ")" << endl; \ | |
| | | | |
End of changes. 34 change blocks. |
| 40 lines changed or deleted | | 67 lines changed or added | |
|
| btree.h | | btree.h | |
| | | | |
| skipping to change at line 206 | | skipping to change at line 206 | |
| }; | | }; | |
| public: | | public: | |
| template< class V > | | template< class V > | |
| const BtreeBucket<V> * btree() const { | | const BtreeBucket<V> * btree() const { | |
| return DiskLoc(*this).btree<V>(); | | return DiskLoc(*this).btree<V>(); | |
| } | | } | |
| template< class V > | | template< class V > | |
| BtreeBucket<V> * btreemod() const { | | BtreeBucket<V> * btreemod() const { | |
| return DiskLoc(*this).btreemod<V>(); | | return DiskLoc(*this).btreemod<V>(); | |
| } | | } | |
|
| operator DiskLoc() const { | | operator const DiskLoc() const { | |
| // endian | | // endian | |
| if( isNull() ) return DiskLoc(); | | if( isNull() ) return DiskLoc(); | |
| unsigned a = *((unsigned *) (_a-1)); | | unsigned a = *((unsigned *) (_a-1)); | |
| return DiskLoc(a >> 8, ofs); | | return DiskLoc(a >> 8, ofs); | |
| } | | } | |
| int& GETOFS() { return ofs; } | | int& GETOFS() { return ofs; } | |
| int getOfs() const { return ofs; } | | int getOfs() const { return ofs; } | |
| bool operator<(const DiskLoc56Bit& rhs) const { | | bool operator<(const DiskLoc56Bit& rhs) const { | |
| // the orderering of dup keys in btrees isn't too critical, but
we'd like to put items that are | | // the orderering of dup keys in btrees isn't too critical, but
we'd like to put items that are | |
| // close together on disk close together in the tree, so we do
want the file # to be the most significant | | // close together on disk close together in the tree, so we do
want the file # to be the most significant | |
| | | | |
| skipping to change at line 575 | | skipping to change at line 575 | |
| * - The bson 'key' must fit in the bucket without packing. | | * - The bson 'key' must fit in the bucket without packing. | |
| * - If 'key' and 'prevChildBucket' are set at index i, the btree | | * - If 'key' and 'prevChildBucket' are set at index i, the btree | |
| * ordering properties will be maintained. | | * ordering properties will be maintained. | |
| * Postconditions: | | * Postconditions: | |
| * - The specified key is set at index i, replacing the existing | | * - The specified key is set at index i, replacing the existing | |
| * _KeyNode data and without shifting any other _KeyNode objects
. | | * _KeyNode data and without shifting any other _KeyNode objects
. | |
| */ | | */ | |
| void setKey( int i, const DiskLoc recordLoc, const Key& key, const
DiskLoc prevChildBucket ); | | void setKey( int i, const DiskLoc recordLoc, const Key& key, const
DiskLoc prevChildBucket ); | |
| }; | | }; | |
| | | | |
|
| | | template< class V> | |
| | | struct Continuation; | |
| | | | |
| /** | | /** | |
| * This class adds functionality for manipulating buckets that are asse
mbled | | * This class adds functionality for manipulating buckets that are asse
mbled | |
| * in a tree. The requirements for const and non const functions and | | * in a tree. The requirements for const and non const functions and | |
| * arguments are generally the same as in BtreeBucket. Because this cl
ass | | * arguments are generally the same as in BtreeBucket. Because this cl
ass | |
| * deals with tree structure, some functions that are marked const may | | * deals with tree structure, some functions that are marked const may | |
| * trigger modification of another node in the btree or potentially of
the | | * trigger modification of another node in the btree or potentially of
the | |
| * current node. In such cases, the function's implementation explicit
ly | | * current node. In such cases, the function's implementation explicit
ly | |
| * casts away const when indicating an intent to write to the durabilit
y | | * casts away const when indicating an intent to write to the durabilit
y | |
| * layer. The DiskLocs provided to such functions should be passed by | | * layer. The DiskLocs provided to such functions should be passed by | |
| * value if they shadow pointers within the btree. | | * value if they shadow pointers within the btree. | |
| | | | |
| skipping to change at line 606 | | skipping to change at line 609 | |
| * | | * | |
| * TODO There are several cases in which the 'this' pointer is invalida
ted | | * TODO There are several cases in which the 'this' pointer is invalida
ted | |
| * as a result of deallocation. A seperate class representing a btree
would | | * as a result of deallocation. A seperate class representing a btree
would | |
| * alleviate some fragile cases where the implementation must currently | | * alleviate some fragile cases where the implementation must currently | |
| * behave correctly if the 'this' pointer is suddenly invalidated by a | | * behave correctly if the 'this' pointer is suddenly invalidated by a | |
| * callee. | | * callee. | |
| */ | | */ | |
| template< class V > | | template< class V > | |
| class BtreeBucket : public BucketBasics<V> { | | class BtreeBucket : public BucketBasics<V> { | |
| friend class BtreeCursor; | | friend class BtreeCursor; | |
|
| | | friend struct Continuation<V>; | |
| public: | | public: | |
| // make compiler happy: | | // make compiler happy: | |
| typedef typename V::Key Key; | | typedef typename V::Key Key; | |
| typedef typename V::KeyOwned KeyOwned; | | typedef typename V::KeyOwned KeyOwned; | |
| typedef typename BucketBasics<V>::KeyNode KeyNode; | | typedef typename BucketBasics<V>::KeyNode KeyNode; | |
| typedef typename BucketBasics<V>::_KeyNode _KeyNode; | | typedef typename BucketBasics<V>::_KeyNode _KeyNode; | |
| typedef typename BucketBasics<V>::Loc Loc; | | typedef typename BucketBasics<V>::Loc Loc; | |
| const _KeyNode& k(int i) const { return static_cast< const Buck
etBasics<V> * >(this)->k(i); } | | const _KeyNode& k(int i) const { return static_cast< const Buck
etBasics<V> * >(this)->k(i); } | |
| protected: | | protected: | |
| _KeyNode& k(int i) { return static_cast< BucketBasi
cs<V> * >(this)->_k(i); } | | _KeyNode& k(int i) { return static_cast< BucketBasi
cs<V> * >(this)->_k(i); } | |
| | | | |
| skipping to change at line 682 | | skipping to change at line 686 | |
| * - If key / recordLoc exist in the btree as a used key, @throw | | * - If key / recordLoc exist in the btree as a used key, @throw | |
| * exception 10287 and no change. | | * exception 10287 and no change. | |
| * - If key / recordLoc do not exist in the btree, they are insert
ed | | * - If key / recordLoc do not exist in the btree, they are insert
ed | |
| * and @return 0. The root of the btree may be changed, so | | * and @return 0. The root of the btree may be changed, so | |
| * 'this'/thisLoc may no longer be the root upon return. | | * 'this'/thisLoc may no longer be the root upon return. | |
| */ | | */ | |
| int bt_insert(const DiskLoc thisLoc, const DiskLoc recordLoc, | | int bt_insert(const DiskLoc thisLoc, const DiskLoc recordLoc, | |
| const BSONObj& key, const Ordering &order, bool dupsA
llowed, | | const BSONObj& key, const Ordering &order, bool dupsA
llowed, | |
| IndexDetails& idx, bool toplevel = true) const; | | IndexDetails& idx, bool toplevel = true) const; | |
| | | | |
|
| | | /** does the insert in two steps - can then use an upgradable lock | |
| | | for step 1, which | |
| | | is the part which may have page faults. also that step is most | |
| | | of the computational work. | |
| | | */ | |
| | | void twoStepInsert(DiskLoc thisLoc, Continuation<V> &c, bool dupsAl | |
| | | lowed) const; | |
| | | | |
| /** | | /** | |
| * Preconditions: | | * Preconditions: | |
| * - 'key' has a valid schema for this index, and may have objsize
() > KeyMax. | | * - 'key' has a valid schema for this index, and may have objsize
() > KeyMax. | |
| * Postconditions: | | * Postconditions: | |
| * - If key / recordLoc are in the btree, they are removed (possib
ly | | * - If key / recordLoc are in the btree, they are removed (possib
ly | |
| * by being marked as an unused key), @return true, and potentia
lly | | * by being marked as an unused key), @return true, and potentia
lly | |
| * invalidate 'this' / thisLoc and change the head. | | * invalidate 'this' / thisLoc and change the head. | |
| * - If key / recordLoc are not in the btree, @return false and do
nothing. | | * - If key / recordLoc are not in the btree, @return false and do
nothing. | |
| */ | | */ | |
| bool unindex(const DiskLoc thisLoc, IndexDetails& id, const BSONObj
& key, const DiskLoc recordLoc) const; | | bool unindex(const DiskLoc thisLoc, IndexDetails& id, const BSONObj
& key, const DiskLoc recordLoc) const; | |
| | | | |
| skipping to change at line 724 | | skipping to change at line 733 | |
| /** | | /** | |
| * Advance to next or previous key in the index. | | * Advance to next or previous key in the index. | |
| * @param direction to advance. | | * @param direction to advance. | |
| */ | | */ | |
| DiskLoc advance(const DiskLoc& thisLoc, int& keyOfs, int direction,
const char *caller) const; | | DiskLoc advance(const DiskLoc& thisLoc, int& keyOfs, int direction,
const char *caller) const; | |
| | | | |
| /** Advance in specified direction to the specified key */ | | /** Advance in specified direction to the specified key */ | |
| void advanceTo(DiskLoc &thisLoc, int &keyOfs, const BSONObj &keyBeg
in, int keyBeginLen, bool afterKey, const vector< const BSONElement * > &ke
yEnd, const vector< bool > &keyEndInclusive, const Ordering &order, int dir
ection ) const; | | void advanceTo(DiskLoc &thisLoc, int &keyOfs, const BSONObj &keyBeg
in, int keyBeginLen, bool afterKey, const vector< const BSONElement * > &ke
yEnd, const vector< bool > &keyEndInclusive, const Ordering &order, int dir
ection ) const; | |
| | | | |
| /** Locate a key with fields comprised of a combination of keyBegin
fields and keyEnd fields. */ | | /** Locate a key with fields comprised of a combination of keyBegin
fields and keyEnd fields. */ | |
|
| void customLocate(DiskLoc &thisLoc, int &keyOfs, const BSONObj &key
Begin, int keyBeginLen, bool afterKey, const vector< const BSONElement * >
&keyEnd, const vector< bool > &keyEndInclusive, const Ordering &order, int
direction, pair< DiskLoc, int > &bestParent ) const; | | static void customLocate(DiskLoc &locInOut, int &keyOfs, const BSON
Obj &keyBegin, int keyBeginLen, bool afterKey, const vector< const BSONElem
ent * > &keyEnd, const vector< bool > &keyEndInclusive, const Ordering &ord
er, int direction, pair< DiskLoc, int > &bestParent ) ; | |
| | | | |
| /** @return head of the btree by traversing from current bucket. */ | | /** @return head of the btree by traversing from current bucket. */ | |
| const DiskLoc getHead(const DiskLoc& thisLoc) const; | | const DiskLoc getHead(const DiskLoc& thisLoc) const; | |
| | | | |
| /** get tree shape */ | | /** get tree shape */ | |
| void shape(stringstream&) const; | | void shape(stringstream&) const; | |
| | | | |
| static void a_test(IndexDetails&); | | static void a_test(IndexDetails&); | |
| | | | |
| static int getKeyMax(); | | static int getKeyMax(); | |
| | | | |
| skipping to change at line 893 | | skipping to change at line 902 | |
| | | | |
| public: | | public: | |
| Key keyAt(int i) const { | | Key keyAt(int i) const { | |
| if( i >= this->n ) | | if( i >= this->n ) | |
| return Key(); | | return Key(); | |
| return Key(this->data + k(i).keyDataOfs()); | | return Key(this->data + k(i).keyDataOfs()); | |
| } | | } | |
| protected: | | protected: | |
| | | | |
| /** | | /** | |
|
| * Allocate a temporary btree bucket in ram rather than in memory m | | | |
| apped | | | |
| * storage. The caller must release this bucket with free(). | | | |
| */ | | | |
| static BtreeBucket<V> * allocTemp(); | | | |
| | | | |
| /** | | | |
| * Preconditions: | | * Preconditions: | |
| * - This bucket is packed. | | * - This bucket is packed. | |
| * - Cannot add a key of size KeyMax to this bucket. | | * - Cannot add a key of size KeyMax to this bucket. | |
| * - 0 <= keypos <= n is the position of a new key that will be in
serted | | * - 0 <= keypos <= n is the position of a new key that will be in
serted | |
| * - lchild is equal to the existing child at index keypos. | | * - lchild is equal to the existing child at index keypos. | |
| * Postconditions: | | * Postconditions: | |
| * - The thisLoc bucket is split into two packed buckets, possibly | | * - The thisLoc bucket is split into two packed buckets, possibly | |
| * invalidating the initial position of keypos, with a split key | | * invalidating the initial position of keypos, with a split key | |
| * promoted to the parent. The new key key/recordLoc will be in
serted | | * promoted to the parent. The new key key/recordLoc will be in
serted | |
| * into one of the split buckets, and lchild/rchild set appropri
ately. | | * into one of the split buckets, and lchild/rchild set appropri
ately. | |
| | | | |
| skipping to change at line 929 | | skipping to change at line 932 | |
| * lchild and rchild, the btree ordering requirements will be | | * lchild and rchild, the btree ordering requirements will be | |
| * maintained. | | * maintained. | |
| * - lchild is equal to the existing child at index keypos. | | * - lchild is equal to the existing child at index keypos. | |
| * - n == 0 is ok. | | * - n == 0 is ok. | |
| * Postconditions: | | * Postconditions: | |
| * - The key / recordLoc are inserted at position keypos, and the | | * - The key / recordLoc are inserted at position keypos, and the | |
| * bucket is split if necessary, which may change the tree head. | | * bucket is split if necessary, which may change the tree head. | |
| * - The bucket may be packed or split, invalidating the specified
value | | * - The bucket may be packed or split, invalidating the specified
value | |
| * of keypos. | | * of keypos. | |
| * This function will always modify thisLoc, but it's marked const
because | | * This function will always modify thisLoc, but it's marked const
because | |
|
| * it commonly relies on the specialized write intent mechanism of
basicInsert(). | | * it commonly relies on the specialized writ]e intent mechanism of
basicInsert(). | |
| */ | | */ | |
| void insertHere(const DiskLoc thisLoc, int keypos, | | void insertHere(const DiskLoc thisLoc, int keypos, | |
| const DiskLoc recordLoc, const Key& key, const Orde
ring &order, | | const DiskLoc recordLoc, const Key& key, const Orde
ring &order, | |
| const DiskLoc lchild, const DiskLoc rchild, IndexDe
tails &idx) const; | | const DiskLoc lchild, const DiskLoc rchild, IndexDe
tails &idx) const; | |
| | | | |
| /** bt_insert() is basically just a wrapper around this. */ | | /** bt_insert() is basically just a wrapper around this. */ | |
| int _insert(const DiskLoc thisLoc, const DiskLoc recordLoc, | | int _insert(const DiskLoc thisLoc, const DiskLoc recordLoc, | |
| const Key& key, const Ordering &order, bool dupsAllowed
, | | const Key& key, const Ordering &order, bool dupsAllowed
, | |
| const DiskLoc lChild, const DiskLoc rChild, IndexDetail
s &idx) const; | | const DiskLoc lChild, const DiskLoc rChild, IndexDetail
s &idx) const; | |
| | | | |
|
| | | void insertStepOne(DiskLoc thisLoc, Continuation<V>& c, bool dupsAl | |
| | | lowed) const; | |
| | | | |
| bool find(const IndexDetails& idx, const Key& key, const DiskLoc &r
ecordLoc, const Ordering &order, int& pos, bool assertIfDup) const; | | bool find(const IndexDetails& idx, const Key& key, const DiskLoc &r
ecordLoc, const Ordering &order, int& pos, bool assertIfDup) const; | |
|
| bool customFind( int l, int h, const BSONObj &keyBegin, int keyBegi
nLen, bool afterKey, const vector< const BSONElement * > &keyEnd, const vec
tor< bool > &keyEndInclusive, const Ordering &order, int direction, DiskLoc
&thisLoc, int &keyOfs, pair< DiskLoc, int > &bestParent ) const; | | static bool customFind( int l, int h, const BSONObj &keyBegin, int
keyBeginLen, bool afterKey, const vector< const BSONElement * > &keyEnd, co
nst vector< bool > &keyEndInclusive, const Ordering &order, int direction,
DiskLoc &thisLoc, int &keyOfs, pair< DiskLoc, int > &bestParent ) ; | |
| static void findLargestKey(const DiskLoc& thisLoc, DiskLoc& largest
Loc, int& largestKey); | | static void findLargestKey(const DiskLoc& thisLoc, DiskLoc& largest
Loc, int& largestKey); | |
| static int customBSONCmp( const BSONObj &l, const BSONObj &rBegin,
int rBeginLen, bool rSup, const vector< const BSONElement * > &rEnd, const
vector< bool > &rEndInclusive, const Ordering &o, int direction ); | | static int customBSONCmp( const BSONObj &l, const BSONObj &rBegin,
int rBeginLen, bool rSup, const vector< const BSONElement * > &rEnd, const
vector< bool > &rEndInclusive, const Ordering &o, int direction ); | |
| | | | |
| /** If child is non null, set its parent to thisLoc */ | | /** If child is non null, set its parent to thisLoc */ | |
| static void fix(const DiskLoc thisLoc, const DiskLoc child); | | static void fix(const DiskLoc thisLoc, const DiskLoc child); | |
| | | | |
| /** | | /** | |
| * Preconditions: | | * Preconditions: | |
| * - 0 <= keypos < n | | * - 0 <= keypos < n | |
| * - If the specified key and recordLoc are placed in keypos of th
isLoc, | | * - If the specified key and recordLoc are placed in keypos of th
isLoc, | |
| | | | |
| skipping to change at line 996 | | skipping to change at line 1001 | |
| class FieldRangeVector; | | class FieldRangeVector; | |
| class FieldRangeVectorIterator; | | class FieldRangeVectorIterator; | |
| | | | |
| class BtreeCursor : public Cursor { | | class BtreeCursor : public Cursor { | |
| protected: | | protected: | |
| BtreeCursor( NamespaceDetails *_d, int _idxNo, const IndexDetails&,
const BSONObj &startKey, const BSONObj &endKey, bool endKeyInclusive, int
direction ); | | BtreeCursor( NamespaceDetails *_d, int _idxNo, const IndexDetails&,
const BSONObj &startKey, const BSONObj &endKey, bool endKeyInclusive, int
direction ); | |
| BtreeCursor( NamespaceDetails *_d, int _idxNo, const IndexDetails&
_id, const shared_ptr< FieldRangeVector > &_bounds, int _direction ); | | BtreeCursor( NamespaceDetails *_d, int _idxNo, const IndexDetails&
_id, const shared_ptr< FieldRangeVector > &_bounds, int _direction ); | |
| public: | | public: | |
| virtual ~BtreeCursor(); | | virtual ~BtreeCursor(); | |
| /** makes an appropriate subclass depending on the index version */ | | /** makes an appropriate subclass depending on the index version */ | |
|
| | | static BtreeCursor* make( NamespaceDetails *_d, const IndexDetails& | |
| | | , const BSONObj &startKey, const BSONObj &endKey, bool endKeyInclusive, int | |
| | | direction ); | |
| | | static BtreeCursor* make( NamespaceDetails *_d, const IndexDetails& | |
| | | _id, const shared_ptr< FieldRangeVector > &_bounds, int _direction ); | |
| static BtreeCursor* make( NamespaceDetails *_d, int _idxNo, const I
ndexDetails&, const BSONObj &startKey, const BSONObj &endKey, bool endKeyIn
clusive, int direction ); | | static BtreeCursor* make( NamespaceDetails *_d, int _idxNo, const I
ndexDetails&, const BSONObj &startKey, const BSONObj &endKey, bool endKeyIn
clusive, int direction ); | |
| static BtreeCursor* make( NamespaceDetails *_d, int _idxNo, const I
ndexDetails& _id, const shared_ptr< FieldRangeVector > &_bounds, int _direc
tion ); | | static BtreeCursor* make( NamespaceDetails *_d, int _idxNo, const I
ndexDetails& _id, const shared_ptr< FieldRangeVector > &_bounds, int _direc
tion ); | |
| | | | |
| virtual bool ok() { return !bucket.isNull(); } | | virtual bool ok() { return !bucket.isNull(); } | |
| virtual bool advance(); | | virtual bool advance(); | |
| virtual void noteLocation(); // updates keyAtKeyOfs... | | virtual void noteLocation(); // updates keyAtKeyOfs... | |
| virtual void checkLocation() = 0; | | virtual void checkLocation() = 0; | |
| virtual bool supportGetMore() { return true; } | | virtual bool supportGetMore() { return true; } | |
| virtual bool supportYields() { return true; } | | virtual bool supportYields() { return true; } | |
| | | | |
| | | | |
| skipping to change at line 1054 | | skipping to change at line 1061 | |
| virtual Record* _current() { return currLoc().rec(); } | | virtual Record* _current() { return currLoc().rec(); } | |
| virtual BSONObj current() { return BSONObj(_current()); } | | virtual BSONObj current() { return BSONObj(_current()); } | |
| virtual string toString(); | | virtual string toString(); | |
| | | | |
| BSONObj prettyKey( const BSONObj &key ) const { | | BSONObj prettyKey( const BSONObj &key ) const { | |
| return key.replaceFieldNames( indexDetails.keyPattern() ).clien
tReadable(); | | return key.replaceFieldNames( indexDetails.keyPattern() ).clien
tReadable(); | |
| } | | } | |
| | | | |
| virtual BSONObj prettyIndexBounds() const; | | virtual BSONObj prettyIndexBounds() const; | |
| | | | |
|
| void forgetEndKey() { endKey = BSONObj(); } | | | |
| | | | |
| virtual CoveredIndexMatcher *matcher() const { return _matcher.get(
); } | | virtual CoveredIndexMatcher *matcher() const { return _matcher.get(
); } | |
| virtual shared_ptr< CoveredIndexMatcher > matcherPtr() const { retu
rn _matcher; } | | virtual shared_ptr< CoveredIndexMatcher > matcherPtr() const { retu
rn _matcher; } | |
| | | | |
| virtual void setMatcher( shared_ptr< CoveredIndexMatcher > matcher
) { _matcher = matcher; } | | virtual void setMatcher( shared_ptr< CoveredIndexMatcher > matcher
) { _matcher = matcher; } | |
| | | | |
| virtual long long nscanned() { return _nscanned; } | | virtual long long nscanned() { return _nscanned; } | |
| | | | |
| /** for debugging only */ | | /** for debugging only */ | |
| const DiskLoc getBucket() const { return bucket; } | | const DiskLoc getBucket() const { return bucket; } | |
|
| | | int getKeyOfs() const { return keyOfs; } | |
| | | | |
| // just for unit tests | | // just for unit tests | |
| virtual bool curKeyHasChild() = 0; | | virtual bool curKeyHasChild() = 0; | |
| | | | |
| protected: | | protected: | |
| /** | | /** | |
| * Our btrees may (rarely) have "unused" keys when items are delete
d. | | * Our btrees may (rarely) have "unused" keys when items are delete
d. | |
| * Skip past them. | | * Skip past them. | |
| */ | | */ | |
| virtual bool skipUnusedKeys() = 0; | | virtual bool skipUnusedKeys() = 0; | |
| | | | |
| skipping to change at line 1089 | | skipping to change at line 1095 | |
| | | | |
| /** selective audits on construction */ | | /** selective audits on construction */ | |
| void audit(); | | void audit(); | |
| | | | |
| virtual void _audit() = 0; | | virtual void _audit() = 0; | |
| virtual DiskLoc _locate(const BSONObj& key, const DiskLoc& loc) = 0
; | | virtual DiskLoc _locate(const BSONObj& key, const DiskLoc& loc) = 0
; | |
| virtual DiskLoc _advance(const DiskLoc& thisLoc, int& keyOfs, int d
irection, const char *caller) = 0; | | virtual DiskLoc _advance(const DiskLoc& thisLoc, int& keyOfs, int d
irection, const char *caller) = 0; | |
| virtual void _advanceTo(DiskLoc &thisLoc, int &keyOfs, const BSONOb
j &keyBegin, int keyBeginLen, bool afterKey, const vector< const BSONElemen
t * > &keyEnd, const vector< bool > &keyEndInclusive, const Ordering &order
, int direction ) = 0; | | virtual void _advanceTo(DiskLoc &thisLoc, int &keyOfs, const BSONOb
j &keyBegin, int keyBeginLen, bool afterKey, const vector< const BSONElemen
t * > &keyEnd, const vector< bool > &keyEndInclusive, const Ordering &order
, int direction ) = 0; | |
| | | | |
| /** set initial bucket */ | | /** set initial bucket */ | |
|
| void init(); | | void initWithoutIndependentFieldRanges(); | |
| | | | |
| /** if afterKey is true, we want the first key with values of the k
eyBegin fields greater than keyBegin */ | | /** if afterKey is true, we want the first key with values of the k
eyBegin fields greater than keyBegin */ | |
| void advanceTo( const BSONObj &keyBegin, int keyBeginLen, bool afte
rKey, const vector< const BSONElement * > &keyEnd, const vector< bool > &ke
yEndInclusive ); | | void advanceTo( const BSONObj &keyBegin, int keyBeginLen, bool afte
rKey, const vector< const BSONElement * > &keyEnd, const vector< bool > &ke
yEndInclusive ); | |
| | | | |
| set<DiskLoc> _dups; | | set<DiskLoc> _dups; | |
| NamespaceDetails * const d; | | NamespaceDetails * const d; | |
| const int idxNo; | | const int idxNo; | |
| BSONObj startKey; | | BSONObj startKey; | |
| BSONObj endKey; | | BSONObj endKey; | |
| bool _endKeyInclusive; | | bool _endKeyInclusive; | |
| | | | |
| skipping to change at line 1111 | | skipping to change at line 1117 | |
| const IndexDetails& indexDetails; | | const IndexDetails& indexDetails; | |
| const BSONObj _order; | | const BSONObj _order; | |
| const Ordering _ordering; | | const Ordering _ordering; | |
| DiskLoc bucket; | | DiskLoc bucket; | |
| int keyOfs; | | int keyOfs; | |
| const int _direction; // 1=fwd,-1=reverse | | const int _direction; // 1=fwd,-1=reverse | |
| BSONObj keyAtKeyOfs; // so we can tell if things moved around on us
between the query and the getMore call | | BSONObj keyAtKeyOfs; // so we can tell if things moved around on us
between the query and the getMore call | |
| DiskLoc locAtKeyOfs; | | DiskLoc locAtKeyOfs; | |
| const shared_ptr< FieldRangeVector > _bounds; | | const shared_ptr< FieldRangeVector > _bounds; | |
| auto_ptr< FieldRangeVectorIterator > _boundsIterator; | | auto_ptr< FieldRangeVectorIterator > _boundsIterator; | |
|
| const IndexSpec& _spec; | | | |
| shared_ptr< CoveredIndexMatcher > _matcher; | | shared_ptr< CoveredIndexMatcher > _matcher; | |
| bool _independentFieldRanges; | | bool _independentFieldRanges; | |
| long long _nscanned; | | long long _nscanned; | |
| }; | | }; | |
| | | | |
|
| | | template< class V > | |
| | | struct Continuation { | |
| | | //Continuation(const typename V::Key & k); | |
| | | Continuation(DiskLoc thisLoc, DiskLoc _recordLoc, const BSONObj &_k | |
| | | ey, | |
| | | Ordering _order, IndexDetails& _idx) : | |
| | | bLoc(thisLoc), recordLoc(_recordLoc), key(_key), order(_order), | |
| | | idx(_idx) { | |
| | | op = Nothing; | |
| | | } | |
| | | | |
| | | DiskLoc bLoc; | |
| | | DiskLoc recordLoc; | |
| | | typename V::KeyOwned key; | |
| | | const Ordering order; | |
| | | IndexDetails& idx; | |
| | | enum Op { Nothing, SetUsed, InsertHere } op; | |
| | | | |
| | | int pos; | |
| | | const BtreeBucket<V> *b; | |
| | | | |
| | | void stepTwo() { | |
| | | if( op == Nothing ) | |
| | | return; | |
| | | else if( op == SetUsed ) { | |
| | | const typename V::_KeyNode& kn = b->k(pos); | |
| | | kn.writing().setUsed(); | |
| | | } | |
| | | else { | |
| | | b->insertHere(bLoc, pos, recordLoc, key, order, DiskLoc(), | |
| | | DiskLoc(), idx); | |
| | | } | |
| | | } | |
| | | }; | |
| | | | |
| /** Renames the index namespace for this btree's index. */ | | /** Renames the index namespace for this btree's index. */ | |
| void renameIndexNamespace(const char *oldNs, const char *newNs); | | void renameIndexNamespace(const char *oldNs, const char *newNs); | |
| | | | |
| /** | | /** | |
| * give us a writable version of the btree bucket (declares write inten
t). | | * give us a writable version of the btree bucket (declares write inten
t). | |
| * note it is likely more efficient to declare write intent on somethin
g smaller when you can. | | * note it is likely more efficient to declare write intent on somethin
g smaller when you can. | |
| */ | | */ | |
| template< class V > | | template< class V > | |
| BtreeBucket<V> * DiskLoc::btreemod() const { | | BtreeBucket<V> * DiskLoc::btreemod() const { | |
| assert( _a != -1 ); | | assert( _a != -1 ); | |
| | | | |
End of changes. 15 change blocks. |
| 15 lines changed or deleted | | 61 lines changed or added | |
|
| chunk.h | | chunk.h | |
| | | | |
| skipping to change at line 37 | | skipping to change at line 37 | |
| #include "shardkey.h" | | #include "shardkey.h" | |
| #include "shard.h" | | #include "shard.h" | |
| #include "util.h" | | #include "util.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| class DBConfig; | | class DBConfig; | |
| class Chunk; | | class Chunk; | |
| class ChunkRange; | | class ChunkRange; | |
| class ChunkManager; | | class ChunkManager; | |
|
| class ChunkRangeMangager; | | | |
| class ChunkObjUnitTest; | | class ChunkObjUnitTest; | |
| | | | |
| typedef shared_ptr<const Chunk> ChunkPtr; | | typedef shared_ptr<const Chunk> ChunkPtr; | |
| | | | |
| // key is max for each Chunk or ChunkRange | | // key is max for each Chunk or ChunkRange | |
| typedef map<BSONObj,ChunkPtr,BSONObjCmp> ChunkMap; | | typedef map<BSONObj,ChunkPtr,BSONObjCmp> ChunkMap; | |
| typedef map<BSONObj,shared_ptr<ChunkRange>,BSONObjCmp> ChunkRangeMap; | | typedef map<BSONObj,shared_ptr<ChunkRange>,BSONObjCmp> ChunkRangeMap; | |
| | | | |
| typedef shared_ptr<const ChunkManager> ChunkManagerPtr; | | typedef shared_ptr<const ChunkManager> ChunkManagerPtr; | |
| | | | |
| | | | |
| skipping to change at line 156 | | skipping to change at line 155 | |
| * @return true if move was successful | | * @return true if move was successful | |
| */ | | */ | |
| bool moveAndCommit( const Shard& to , long long chunkSize , BSONObj
& res ) const; | | bool moveAndCommit( const Shard& to , long long chunkSize , BSONObj
& res ) const; | |
| | | | |
| /** | | /** | |
| * @return size of shard in bytes | | * @return size of shard in bytes | |
| * talks to mongod to do this | | * talks to mongod to do this | |
| */ | | */ | |
| long getPhysicalSize() const; | | long getPhysicalSize() const; | |
| | | | |
|
| | | /** | |
| | | * marks this chunk as a jumbo chunk | |
| | | * that means the chunk will be inelligble for migrates | |
| | | */ | |
| | | void markAsJumbo() const; | |
| | | | |
| | | bool isJumbo() const { return _jumbo; } | |
| | | | |
| | | /** | |
| | | * Attempt to refresh maximum chunk size from config. | |
| | | */ | |
| | | static void refreshChunkSize(); | |
| | | | |
| // | | // | |
| // public constants | | // public constants | |
| // | | // | |
| | | | |
| static string chunkMetadataNS; | | static string chunkMetadataNS; | |
| static int MaxChunkSize; | | static int MaxChunkSize; | |
| static int MaxObjectPerChunk; | | static int MaxObjectPerChunk; | |
|
| static bool ShouldAutoSplit; | | | |
| | | | |
| // | | // | |
| // accessors and helpers | | // accessors and helpers | |
| // | | // | |
| | | | |
| string toString() const; | | string toString() const; | |
| | | | |
| friend ostream& operator << (ostream& out, const Chunk& c) { return
(out << c.toString()); } | | friend ostream& operator << (ostream& out, const Chunk& c) { return
(out << c.toString()); } | |
| bool operator==(const Chunk& s) const; | | bool operator==(const Chunk& s) const; | |
| bool operator!=(const Chunk& s) const { return ! ( *this == s ); } | | bool operator!=(const Chunk& s) const { return ! ( *this == s ); } | |
| | | | |
| | | | |
| skipping to change at line 190 | | skipping to change at line 200 | |
| private: | | private: | |
| | | | |
| // main shard info | | // main shard info | |
| | | | |
| const ChunkManager * _manager; | | const ChunkManager * _manager; | |
| | | | |
| BSONObj _min; | | BSONObj _min; | |
| BSONObj _max; | | BSONObj _max; | |
| Shard _shard; | | Shard _shard; | |
| ShardChunkVersion _lastmod; | | ShardChunkVersion _lastmod; | |
|
| | | mutable bool _jumbo; | |
| | | | |
| // transient stuff | | // transient stuff | |
| | | | |
| mutable long _dataWritten; | | mutable long _dataWritten; | |
| | | | |
| // methods, etc.. | | // methods, etc.. | |
| | | | |
| /** | | /** | |
| * if sort 1, return lowest key | | * if sort 1, return lowest key | |
| * if sort -1, return highest key | | * if sort -1, return highest key | |
| | | | |
| skipping to change at line 225 | | skipping to change at line 236 | |
| const BSONObj& getMin() const { return _min; } | | const BSONObj& getMin() const { return _min; } | |
| const BSONObj& getMax() const { return _max; } | | const BSONObj& getMax() const { return _max; } | |
| | | | |
| // clones of Chunk methods | | // clones of Chunk methods | |
| bool contains(const BSONObj& obj) const; | | bool contains(const BSONObj& obj) const; | |
| | | | |
| ChunkRange(ChunkMap::const_iterator begin, const ChunkMap::const_it
erator end) | | ChunkRange(ChunkMap::const_iterator begin, const ChunkMap::const_it
erator end) | |
| : _manager(begin->second->getManager()) | | : _manager(begin->second->getManager()) | |
| , _shard(begin->second->getShard()) | | , _shard(begin->second->getShard()) | |
| , _min(begin->second->getMin()) | | , _min(begin->second->getMin()) | |
|
| , _max(prior(end)->second->getMax()) { | | , _max(boost::prior(end)->second->getMax()) { | |
| assert( begin != end ); | | assert( begin != end ); | |
| | | | |
| DEV while (begin != end) { | | DEV while (begin != end) { | |
| assert(begin->second->getManager() == _manager); | | assert(begin->second->getManager() == _manager); | |
| assert(begin->second->getShard() == _shard); | | assert(begin->second->getShard() == _shard); | |
| ++begin; | | ++begin; | |
| } | | } | |
| } | | } | |
| | | | |
| // Merge min and max (must be adjacent ranges) | | // Merge min and max (must be adjacent ranges) | |
| | | | |
| skipping to change at line 295 | | skipping to change at line 306 | |
| public: | | public: | |
| typedef map<Shard,ShardChunkVersion> ShardVersionMap; | | typedef map<Shard,ShardChunkVersion> ShardVersionMap; | |
| | | | |
| ChunkManager( string ns , ShardKeyPattern pattern , bool unique ); | | ChunkManager( string ns , ShardKeyPattern pattern , bool unique ); | |
| | | | |
| string getns() const { return _ns; } | | string getns() const { return _ns; } | |
| | | | |
| int numChunks() const { return _chunkMap.size(); } | | int numChunks() const { return _chunkMap.size(); } | |
| bool hasShardKey( const BSONObj& obj ) const; | | bool hasShardKey( const BSONObj& obj ) const; | |
| | | | |
|
| void createFirstChunks( const Shard& shard ) const; // only call fr
om DBConfig::shardCollection | | void createFirstChunks( const Shard& primary , vector<BSONObj>* ini
tPoints , vector<Shard>* initShards ) const; // only call from DBConfig::sh
ardCollection | |
| ChunkPtr findChunk( const BSONObj& obj ) const; | | ChunkPtr findChunk( const BSONObj& obj ) const; | |
| ChunkPtr findChunkOnServer( const Shard& shard ) const; | | ChunkPtr findChunkOnServer( const Shard& shard ) const; | |
| | | | |
| const ShardKeyPattern& getShardKey() const { return _key; } | | const ShardKeyPattern& getShardKey() const { return _key; } | |
| bool isUnique() const { return _unique; } | | bool isUnique() const { return _unique; } | |
| | | | |
| void getShardsForQuery( set<Shard>& shards , const BSONObj& query )
const; | | void getShardsForQuery( set<Shard>& shards , const BSONObj& query )
const; | |
| void getAllShards( set<Shard>& all ) const; | | void getAllShards( set<Shard>& all ) const; | |
| void getShardsForRange(set<Shard>& shards, const BSONObj& min, cons
t BSONObj& max, bool fullKeyReq = true) const; // [min, max) | | void getShardsForRange(set<Shard>& shards, const BSONObj& min, cons
t BSONObj& max, bool fullKeyReq = true) const; // [min, max) | |
| | | | |
|
| | | ChunkMap getChunkMap() const { return _chunkMap; } | |
| | | | |
| | | /** | |
| | | * Returns true if, for this shard, the chunks are identical in bot | |
| | | h chunk managers | |
| | | */ | |
| | | bool compatibleWith( const ChunkManager& other, const Shard& shard | |
| | | ) const; | |
| | | bool compatibleWith( ChunkManagerPtr other, const Shard& shard ) co | |
| | | nst { if( ! other ) return false; return compatibleWith( *other, shard ); } | |
| | | | |
| | | bool compatibleWith( const Chunk& other ) const; | |
| | | bool compatibleWith( ChunkPtr other ) const { if( ! other ) return | |
| | | false; return compatibleWith( *other ); } | |
| | | | |
| string toString() const; | | string toString() const; | |
| | | | |
| ShardChunkVersion getVersion( const Shard& shard ) const; | | ShardChunkVersion getVersion( const Shard& shard ) const; | |
| ShardChunkVersion getVersion() const; | | ShardChunkVersion getVersion() const; | |
| | | | |
| /** | | /** | |
| * this is just an increasing number of how many ChunkManagers we h
ave so we know if something has been updated | | * this is just an increasing number of how many ChunkManagers we h
ave so we know if something has been updated | |
| */ | | */ | |
| unsigned long long getSequenceNumber() const { return _sequenceNumb
er; } | | unsigned long long getSequenceNumber() const { return _sequenceNumb
er; } | |
| | | | |
| | | | |
End of changes. 7 change blocks. |
| 5 lines changed or deleted | | 31 lines changed or added | |
|
| clientcursor.h | | clientcursor.h | |
| | | | |
| skipping to change at line 117 | | skipping to change at line 117 | |
| if( _c ) { | | if( _c ) { | |
| if( _c->_pinValue >= 100 ) { | | if( _c->_pinValue >= 100 ) { | |
| _c = 0; | | _c = 0; | |
| uasserted(12051, "clientcursor already in use? driv
er problem?"); | | uasserted(12051, "clientcursor already in use? driv
er problem?"); | |
| } | | } | |
| _c->_pinValue += 100; | | _c->_pinValue += 100; | |
| } | | } | |
| } | | } | |
| }; | | }; | |
| | | | |
|
| // This object assures safe and reliable cleanup of the ClientCurso | | // This object assures safe and reliable cleanup of a ClientCursor. | |
| r. | | | |
| // The implementation assumes that there will be no duplicate ids a | | | |
| mong cursors | | | |
| // (which is assured if cursors must last longer than 1 second). | | | |
| class CleanupPointer : boost::noncopyable { | | class CleanupPointer : boost::noncopyable { | |
| public: | | public: | |
| CleanupPointer() : _c( 0 ), _id( -1 ) {} | | CleanupPointer() : _c( 0 ), _id( -1 ) {} | |
| void reset( ClientCursor *c = 0 ) { | | void reset( ClientCursor *c = 0 ) { | |
| if ( c == _c ) | | if ( c == _c ) | |
| return; | | return; | |
| if ( _c ) { | | if ( _c ) { | |
| // be careful in case cursor was deleted by someone els
e | | // be careful in case cursor was deleted by someone els
e | |
| ClientCursor::erase( _id ); | | ClientCursor::erase( _id ); | |
| } | | } | |
| | | | |
| skipping to change at line 149 | | skipping to change at line 147 | |
| ~CleanupPointer() { | | ~CleanupPointer() { | |
| DESTRUCTOR_GUARD ( reset(); ); | | DESTRUCTOR_GUARD ( reset(); ); | |
| } | | } | |
| operator bool() { return _c; } | | operator bool() { return _c; } | |
| ClientCursor * operator-> () { return _c; } | | ClientCursor * operator-> () { return _c; } | |
| private: | | private: | |
| ClientCursor *_c; | | ClientCursor *_c; | |
| CursorId _id; | | CursorId _id; | |
| }; | | }; | |
| | | | |
|
| | | /** | |
| | | * Iterates through all ClientCursors, under its own ccmutex lock. | |
| | | * Also supports deletion on the fly. | |
| | | */ | |
| | | class LockedIterator : boost::noncopyable { | |
| | | public: | |
| | | LockedIterator() : _lock( ccmutex ), _i( clientCursorsById.begi | |
| | | n() ) {} | |
| | | bool ok() const { return _i != clientCursorsById.end(); } | |
| | | ClientCursor *current() const { return _i->second; } | |
| | | void advance() { ++_i; } | |
| | | /** | |
| | | * Delete 'current' and advance. Properly handles cascading del | |
| | | etions that may occur | |
| | | * when one ClientCursor is directly deleted. | |
| | | */ | |
| | | void deleteAndAdvance(); | |
| | | private: | |
| | | recursive_scoped_lock _lock; | |
| | | CCById::const_iterator _i; | |
| | | }; | |
| | | | |
| ClientCursor(int queryOptions, const shared_ptr<Cursor>& c, const s
tring& ns, BSONObj query = BSONObj() ); | | ClientCursor(int queryOptions, const shared_ptr<Cursor>& c, const s
tring& ns, BSONObj query = BSONObj() ); | |
| | | | |
| ~ClientCursor(); | | ~ClientCursor(); | |
| | | | |
| // *************** basic accessors ******************* | | // *************** basic accessors ******************* | |
| | | | |
| CursorId cursorid() const { return _cursorid; } | | CursorId cursorid() const { return _cursorid; } | |
| string ns() const { return _ns; } | | string ns() const { return _ns; } | |
| Database * db() const { return _db; } | | Database * db() const { return _db; } | |
| const BSONObj& query() const { return _query; } | | const BSONObj& query() const { return _query; } | |
| | | | |
| skipping to change at line 194 | | skipping to change at line 212 | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * @param needRecord whether or not the next record has to be read
from disk for sure | | * @param needRecord whether or not the next record has to be read
from disk for sure | |
| * if this is true, will yield of next record isn
't in memory | | * if this is true, will yield of next record isn
't in memory | |
| * @param yielded true if a yield occurred, and potentially if a yi
eld did not occur | | * @param yielded true if a yield occurred, and potentially if a yi
eld did not occur | |
| * @return same as yield() | | * @return same as yield() | |
| */ | | */ | |
| bool yieldSometimes( RecordNeeds need, bool *yielded = 0 ); | | bool yieldSometimes( RecordNeeds need, bool *yielded = 0 ); | |
| | | | |
|
| static int yieldSuggest(); | | static int suggestYieldMicros(); | |
| static void staticYield( int micros , const StringData& ns , Record
* rec ); | | static void staticYield( int micros , const StringData& ns , Record
* rec ); | |
| | | | |
| struct YieldData { CursorId _id; bool _doingDeletes; }; | | struct YieldData { CursorId _id; bool _doingDeletes; }; | |
| bool prepareToYield( YieldData &data ); | | bool prepareToYield( YieldData &data ); | |
| static bool recoverFromYield( const YieldData &data ); | | static bool recoverFromYield( const YieldData &data ); | |
| | | | |
| struct YieldLock : boost::noncopyable { | | struct YieldLock : boost::noncopyable { | |
| explicit YieldLock( ptr<ClientCursor> cc ) | | explicit YieldLock( ptr<ClientCursor> cc ) | |
| : _canYield(cc->_c->supportYields()) { | | : _canYield(cc->_c->supportYields()) { | |
| if ( _canYield ) { | | if ( _canYield ) { | |
| | | | |
| skipping to change at line 305 | | skipping to change at line 323 | |
| public: | | public: | |
| static ClientCursor* find(CursorId id, bool warn = true) { | | static ClientCursor* find(CursorId id, bool warn = true) { | |
| recursive_scoped_lock lock(ccmutex); | | recursive_scoped_lock lock(ccmutex); | |
| ClientCursor *c = find_inlock(id, warn); | | ClientCursor *c = find_inlock(id, warn); | |
| // if this asserts, your code was not thread safe - you either
need to set no timeout | | // if this asserts, your code was not thread safe - you either
need to set no timeout | |
| // for the cursor or keep a ClientCursor::Pointer in scope for
it. | | // for the cursor or keep a ClientCursor::Pointer in scope for
it. | |
| massert( 12521, "internal error: use of an unlocked ClientCurso
r", c == 0 || c->_pinValue ); | | massert( 12521, "internal error: use of an unlocked ClientCurso
r", c == 0 || c->_pinValue ); | |
| return c; | | return c; | |
| } | | } | |
| | | | |
|
| | | /** | |
| | | * Deletes the cursor with the provided @param 'id' if one exists. | |
| | | * @throw if the cursor with the provided id is pinned. | |
| | | */ | |
| static bool erase(CursorId id) { | | static bool erase(CursorId id) { | |
| recursive_scoped_lock lock(ccmutex); | | recursive_scoped_lock lock(ccmutex); | |
| ClientCursor *cc = find_inlock(id); | | ClientCursor *cc = find_inlock(id); | |
| if ( cc ) { | | if ( cc ) { | |
| assert( cc->_pinValue < 100 ); // you can't still have an a
ctive ClientCursor::Pointer | | assert( cc->_pinValue < 100 ); // you can't still have an a
ctive ClientCursor::Pointer | |
| delete cc; | | delete cc; | |
| return true; | | return true; | |
| } | | } | |
| return false; | | return false; | |
| } | | } | |
| | | | |
| skipping to change at line 394 | | skipping to change at line 416 | |
| | | | |
| DiskLoc _lastLoc; // use getter and setter n
ot this (important) | | DiskLoc _lastLoc; // use getter and setter n
ot this (important) | |
| unsigned _idleAgeMillis; // how long has the cursor
been around, relative to server idle time | | unsigned _idleAgeMillis; // how long has the cursor
been around, relative to server idle time | |
| | | | |
| /* 0 = normal | | /* 0 = normal | |
| 1 = no timeout allowed | | 1 = no timeout allowed | |
| 100 = in use (pinned) -- see Pointer class | | 100 = in use (pinned) -- see Pointer class | |
| */ | | */ | |
| unsigned _pinValue; | | unsigned _pinValue; | |
| | | | |
|
| bool _doingDeletes; | | bool _doingDeletes; // when true we are the delete and aboutToDelet
e shouldn't manipulate us | |
| ElapsedTracker _yieldSometimesTracker; | | ElapsedTracker _yieldSometimesTracker; | |
| | | | |
| ShardChunkManagerPtr _chunkManager; | | ShardChunkManagerPtr _chunkManager; | |
| | | | |
| public: | | public: | |
| shared_ptr<ParsedQuery> pq; | | shared_ptr<ParsedQuery> pq; | |
| shared_ptr<Projection> fields; // which fields query wants returned | | shared_ptr<Projection> fields; // which fields query wants returned | |
|
| | | // TODO Maybe helper objects should manage their own memory rather | |
| | | than rely on the | |
| | | // original message being valid. | |
| Message originalMessage; // this is effectively an auto ptr for dat
a the matcher points to | | Message originalMessage; // this is effectively an auto ptr for dat
a the matcher points to | |
| | | | |
| private: // static members | | private: // static members | |
| | | | |
| static CCById clientCursorsById; | | static CCById clientCursorsById; | |
| static long long numberTimedOut; | | static long long numberTimedOut; | |
| static boost::recursive_mutex& ccmutex; // must use this for all
statics above! | | static boost::recursive_mutex& ccmutex; // must use this for all
statics above! | |
| static CursorId allocCursorId_inlock(); | | static CursorId allocCursorId_inlock(); | |
| | | | |
| }; | | }; | |
| | | | |
| class ClientCursorMonitor : public BackgroundJob { | | class ClientCursorMonitor : public BackgroundJob { | |
| public: | | public: | |
| string name() const { return "ClientCursorMonitor"; } | | string name() const { return "ClientCursorMonitor"; } | |
| void run(); | | void run(); | |
| }; | | }; | |
| | | | |
|
| extern ClientCursorMonitor clientCursorMonitor; | | | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
| // ClientCursor should only be used with auto_ptr because it needs to be | | // ClientCursor should only be used with auto_ptr because it needs to be | |
| // release()ed after a yield if stillOk() returns false and these pointer t
ypes | | // release()ed after a yield if stillOk() returns false and these pointer t
ypes | |
| // do not support releasing. This will prevent them from being used acciden
tally | | // do not support releasing. This will prevent them from being used acciden
tally | |
| namespace boost{ | | namespace boost{ | |
| template<> class scoped_ptr<mongo::ClientCursor> {}; | | template<> class scoped_ptr<mongo::ClientCursor> {}; | |
| template<> class shared_ptr<mongo::ClientCursor> {}; | | template<> class shared_ptr<mongo::ClientCursor> {}; | |
| } | | } | |
| | | | |
End of changes. 7 change blocks. |
| 9 lines changed or deleted | | 32 lines changed or added | |
|
| cmdline.h | | cmdline.h | |
| | | | |
| skipping to change at line 22 | | skipping to change at line 22 | |
| * | | * | |
| * You should have received a copy of the GNU Affero General Public Licen
se | | * You should have received a copy of the GNU Affero General Public Licen
se | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "../pch.h" | | #include "../pch.h" | |
| #include "jsobj.h" | | #include "jsobj.h" | |
| | | | |
|
| | | namespace boost { | |
| | | namespace program_options { | |
| | | class options_description; | |
| | | class positional_options_description; | |
| | | class variables_map; | |
| | | } | |
| | | } | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| #ifdef MONGO_SSL | | #ifdef MONGO_SSL | |
| class SSLManager; | | class SSLManager; | |
| #endif | | #endif | |
| | | | |
| /* command line options | | /* command line options | |
| */ | | */ | |
| /* concurrency: OK/READ */ | | /* concurrency: OK/READ */ | |
| struct CmdLine { | | struct CmdLine { | |
| | | | |
| skipping to change at line 91 | | skipping to change at line 99 | |
| | | | |
| /** --durOptions 7 dump journal and terminate without doing an
ything further | | /** --durOptions 7 dump journal and terminate without doing an
ything further | |
| --durOptions 4 recover and terminate without listening | | --durOptions 4 recover and terminate without listening | |
| */ | | */ | |
| enum { // bits to be ORed | | enum { // bits to be ORed | |
| DurDumpJournal = 1, // dump diagnostics on the journal during
recovery | | DurDumpJournal = 1, // dump diagnostics on the journal during
recovery | |
| DurScanOnly = 2, // don't do any real work, just scan and
dump if dump specified | | DurScanOnly = 2, // don't do any real work, just scan and
dump if dump specified | |
| DurRecoverOnly = 4, // terminate after recovery step | | DurRecoverOnly = 4, // terminate after recovery step | |
| DurParanoid = 8, // paranoid mode enables extra checks | | DurParanoid = 8, // paranoid mode enables extra checks | |
| DurAlwaysCommit = 16, // do a group commit every time the write
lock is released | | DurAlwaysCommit = 16, // do a group commit every time the write
lock is released | |
|
| DurAlwaysRemap = 32 // remap the private view after every gro | | DurAlwaysRemap = 32, // remap the private view after every gro | |
| up commit (may lag to the next write lock acquisition, but will do all file | | up commit (may lag to the next write lock acquisition, but will do all file | |
| s then) | | s then) | |
| | | DurNoCheckSpace = 64 // don't check that there is enough room | |
| | | for journal files before startup (for diskfull tests) | |
| }; | | }; | |
| int durOptions; // --durOptions <n> for debugging | | int durOptions; // --durOptions <n> for debugging | |
| | | | |
| bool objcheck; // --objcheck | | bool objcheck; // --objcheck | |
| | | | |
| long long oplogSize; // --oplogSize | | long long oplogSize; // --oplogSize | |
| int defaultProfile; // --profile | | int defaultProfile; // --profile | |
| int slowMS; // --time in ms that is "slow" | | int slowMS; // --time in ms that is "slow" | |
| | | | |
| int pretouch; // --pretouch for replication application (e
xperimental) | | int pretouch; // --pretouch for replication application (e
xperimental) | |
| bool moveParanoia; // for move chunk paranoia | | bool moveParanoia; // for move chunk paranoia | |
| double syncdelay; // seconds between fsyncs | | double syncdelay; // seconds between fsyncs | |
| | | | |
| bool noUnixSocket; // --nounixsocket | | bool noUnixSocket; // --nounixsocket | |
|
| | | bool doFork; // --fork | |
| string socket; // UNIX domain socket directory | | string socket; // UNIX domain socket directory | |
| | | | |
| bool keyFile; | | bool keyFile; | |
| | | | |
|
| | | #ifndef _WIN32 | |
| | | pid_t parentProc; // --fork pid of initial process | |
| | | pid_t leaderProc; // --fork pid of leader process | |
| | | #endif | |
| | | | |
| #ifdef MONGO_SSL | | #ifdef MONGO_SSL | |
| bool sslOnNormalPorts; // --sslOnNormalPorts | | bool sslOnNormalPorts; // --sslOnNormalPorts | |
| string sslPEMKeyFile; // --sslPEMKeyFile | | string sslPEMKeyFile; // --sslPEMKeyFile | |
| string sslPEMKeyPassword; // --sslPEMKeyPassword | | string sslPEMKeyPassword; // --sslPEMKeyPassword | |
| | | | |
| SSLManager* sslServerManager; // currently leaks on close | | SSLManager* sslServerManager; // currently leaks on close | |
| #endif | | #endif | |
| | | | |
|
| | | static void launchOk(); | |
| | | | |
| static void addGlobalOptions( boost::program_options::options_descr
iption& general , | | static void addGlobalOptions( boost::program_options::options_descr
iption& general , | |
| boost::program_options::options_descr
iption& hidden ); | | boost::program_options::options_descr
iption& hidden ); | |
| | | | |
| static void addWindowsOptions( boost::program_options::options_desc
ription& windows , | | static void addWindowsOptions( boost::program_options::options_desc
ription& windows , | |
| boost::program_options::options_desc
ription& hidden ); | | boost::program_options::options_desc
ription& hidden ); | |
| | | | |
| static void parseConfigFile( istream &f, stringstream &ss); | | static void parseConfigFile( istream &f, stringstream &ss); | |
| /** | | /** | |
| * @return true if should run program, false if should exit | | * @return true if should run program, false if should exit | |
| */ | | */ | |
| static bool store( int argc , char ** argv , | | static bool store( int argc , char ** argv , | |
| boost::program_options::options_description& vis
ible, | | boost::program_options::options_description& vis
ible, | |
| boost::program_options::options_description& hid
den, | | boost::program_options::options_description& hid
den, | |
| boost::program_options::positional_options_descr
iption& positional, | | boost::program_options::positional_options_descr
iption& positional, | |
| boost::program_options::variables_map &output ); | | boost::program_options::variables_map &output ); | |
|
| | | | |
| | | time_t started; | |
| }; | | }; | |
| | | | |
| // todo move to cmdline.cpp? | | // todo move to cmdline.cpp? | |
| inline CmdLine::CmdLine() : | | inline CmdLine::CmdLine() : | |
| port(DefaultDBPort), rest(false), jsonp(false), quiet(false), noTab
leScan(false), prealloc(true), preallocj(true), smallfiles(sizeof(int*) ==
4), | | port(DefaultDBPort), rest(false), jsonp(false), quiet(false), noTab
leScan(false), prealloc(true), preallocj(true), smallfiles(sizeof(int*) ==
4), | |
| configsvr(false), | | configsvr(false), | |
| quota(false), quotaFiles(8), cpu(false), durOptions(0), objcheck(fa
lse), oplogSize(0), defaultProfile(0), slowMS(100), pretouch(0), moveParano
ia( true ), | | quota(false), quotaFiles(8), cpu(false), durOptions(0), objcheck(fa
lse), oplogSize(0), defaultProfile(0), slowMS(100), pretouch(0), moveParano
ia( true ), | |
|
| syncdelay(60), noUnixSocket(false), socket("/tmp") | | syncdelay(60), noUnixSocket(false), doFork(0), socket("/tmp") | |
| { | | { | |
|
| | | started = time(0); | |
| | | | |
| journalCommitInterval = 0; // 0 means use default | | journalCommitInterval = 0; // 0 means use default | |
| dur = false; | | dur = false; | |
| #if defined(_DURABLEDEFAULTON) | | #if defined(_DURABLEDEFAULTON) | |
| dur = true; | | dur = true; | |
| #endif | | #endif | |
| if( sizeof(void*) == 8 ) | | if( sizeof(void*) == 8 ) | |
| dur = true; | | dur = true; | |
| #if defined(_DURABLEDEFAULTOFF) | | #if defined(_DURABLEDEFAULTOFF) | |
| dur = false; | | dur = false; | |
| #endif | | #endif | |
| | | | |
| #ifdef MONGO_SSL | | #ifdef MONGO_SSL | |
| sslOnNormalPorts = false; | | sslOnNormalPorts = false; | |
| sslServerManager = 0; | | sslServerManager = 0; | |
| #endif | | #endif | |
| } | | } | |
| | | | |
| extern CmdLine cmdLine; | | extern CmdLine cmdLine; | |
| | | | |
|
| | | void setupLaunchSignals(); | |
| void setupCoreSignals(); | | void setupCoreSignals(); | |
| | | | |
| string prettyHostName(); | | string prettyHostName(); | |
| | | | |
| void printCommandLineOpts(); | | void printCommandLineOpts(); | |
| | | | |
| /** | | /** | |
|
| * used for setParameter | | * used for setParameter command | |
| * so you can write validation code that lives with code using it | | * so you can write validation code that lives with code using it | |
| * rather than all in the command place | | * rather than all in the command place | |
| * also lets you have mongos or mongod specific code | | * also lets you have mongos or mongod specific code | |
| * without pulling it all sorts of things | | * without pulling it all sorts of things | |
| */ | | */ | |
| class ParameterValidator { | | class ParameterValidator { | |
| public: | | public: | |
| ParameterValidator( const string& name ); | | ParameterValidator( const string& name ); | |
| virtual ~ParameterValidator() {} | | virtual ~ParameterValidator() {} | |
| | | | |
|
| virtual bool isValid( BSONElement e , string& errmsg ) = 0; | | virtual bool isValid( BSONElement e , string& errmsg ) const = 0; | |
| | | | |
| static ParameterValidator * get( const string& name ); | | static ParameterValidator * get( const string& name ); | |
| | | | |
| private: | | private: | |
|
| string _name; | | const string _name; | |
| | | | |
| // don't need to lock since this is all done in static init | | | |
| static map<string,ParameterValidator*> * _all; | | | |
| }; | | }; | |
| | | | |
| } | | } | |
| | | | |
End of changes. 12 change blocks. |
| 10 lines changed or deleted | | 30 lines changed or added | |
|
| commands.h | | commands.h | |
| | | | |
| skipping to change at line 20 | | skipping to change at line 20 | |
| * | | * | |
| * Unless required by applicable law or agreed to in writing, software | | * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | | * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | |
| * See the License for the specific language governing permissions and | | * See the License for the specific language governing permissions and | |
| * limitations under the License. | | * limitations under the License. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
|
| #include "../pch.h" | | | |
| #include "jsobj.h" | | #include "jsobj.h" | |
|
| #include "../util/timer.h" | | #include "../util/mongoutils/str.h" | |
| #include "../client/dbclient.h" | | | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| class BSONObj; | | class BSONObj; | |
| class BSONObjBuilder; | | class BSONObjBuilder; | |
| class Client; | | class Client; | |
|
| | | class Timer; | |
| | | | |
| /** mongodb "commands" (sent via db.$cmd.findOne(...)) | | /** mongodb "commands" (sent via db.$cmd.findOne(...)) | |
| subclass to make a command. define a singleton object for it. | | subclass to make a command. define a singleton object for it. | |
| */ | | */ | |
| class Command { | | class Command { | |
|
| | | protected: | |
| | | string parseNsFullyQualified(const string& dbname, const BSONObj& c | |
| | | mdObj) const; | |
| public: | | public: | |
|
| | | // only makes sense for commands where 1st parm is the collection. | |
| | | virtual string parseNs(const string& dbname, const BSONObj& cmdObj) | |
| | | const; | |
| | | | |
| enum LockType { READ = -1 , NONE = 0 , WRITE = 1 }; | | enum LockType { READ = -1 , NONE = 0 , WRITE = 1 }; | |
| | | | |
| const string name; | | const string name; | |
| | | | |
| /* run the given command | | /* run the given command | |
| implement this... | | implement this... | |
| | | | |
| fromRepl - command is being invoked as part of replication synci
ng. In this situation you | | fromRepl - command is being invoked as part of replication synci
ng. In this situation you | |
| normally do not want to log the command to the local
oplog. | | normally do not want to log the command to the local
oplog. | |
| | | | |
| skipping to change at line 66 | | skipping to change at line 69 | |
| virtual LockType locktype() const = 0; | | virtual LockType locktype() const = 0; | |
| | | | |
| /* Return true if only the admin ns has privileges to run this comm
and. */ | | /* Return true if only the admin ns has privileges to run this comm
and. */ | |
| virtual bool adminOnly() const { | | virtual bool adminOnly() const { | |
| return false; | | return false; | |
| } | | } | |
| | | | |
| void htmlHelp(stringstream&) const; | | void htmlHelp(stringstream&) const; | |
| | | | |
| /* Like adminOnly, but even stricter: we must either be authenticat
ed for admin db, | | /* Like adminOnly, but even stricter: we must either be authenticat
ed for admin db, | |
|
| or, if running without auth, on the local interface. | | or, if running without auth, on the local interface. Used for t | |
| | | hings which | |
| | | are so major that remote invocation may not make sense (e.g., sh | |
| | | utdownServer). | |
| | | | |
| When localHostOnlyIfNoAuth() is true, adminOnly() must also be t
rue. | | When localHostOnlyIfNoAuth() is true, adminOnly() must also be t
rue. | |
| */ | | */ | |
| virtual bool localHostOnlyIfNoAuth(const BSONObj& cmdObj) { return
false; } | | virtual bool localHostOnlyIfNoAuth(const BSONObj& cmdObj) { return
false; } | |
| | | | |
| /* Return true if slaves are allowed to execute the command | | /* Return true if slaves are allowed to execute the command | |
| (the command directly from a client -- if fromRepl, always allow
ed). | | (the command directly from a client -- if fromRepl, always allow
ed). | |
| */ | | */ | |
| virtual bool slaveOk() const = 0; | | virtual bool slaveOk() const = 0; | |
| | | | |
| /* Return true if the client force a command to be run on a slave b
y | | /* Return true if the client force a command to be run on a slave b
y | |
|
| turning on the 'slaveok' option in the command query. | | turning on the 'slaveOk' option in the command query. | |
| */ | | */ | |
|
| virtual bool slaveOverrideOk() { | | virtual bool slaveOverrideOk() const { | |
| return false; | | return false; | |
| } | | } | |
| | | | |
| /* Override and return true to if true,log the operation (logOp())
to the replication log. | | /* Override and return true to if true,log the operation (logOp())
to the replication log. | |
| (not done if fromRepl of course) | | (not done if fromRepl of course) | |
| | | | |
| Note if run() returns false, we do NOT log. | | Note if run() returns false, we do NOT log. | |
| */ | | */ | |
| virtual bool logTheOp() { return false; } | | virtual bool logTheOp() { return false; } | |
| | | | |
| | | | |
| skipping to change at line 103 | | skipping to change at line 107 | |
| /* Return true if authentication and security applies to the comman
ds. Some commands | | /* Return true if authentication and security applies to the comman
ds. Some commands | |
| (e.g., getnonce, authenticate) can be done by anyone even unauth
orized. | | (e.g., getnonce, authenticate) can be done by anyone even unauth
orized. | |
| */ | | */ | |
| virtual bool requiresAuth() { return true; } | | virtual bool requiresAuth() { return true; } | |
| | | | |
| /* Return true if a replica set secondary should go into "recoverin
g" | | /* Return true if a replica set secondary should go into "recoverin
g" | |
| (unreadable) state while running this command. | | (unreadable) state while running this command. | |
| */ | | */ | |
| virtual bool maintenanceMode() const { return false; } | | virtual bool maintenanceMode() const { return false; } | |
| | | | |
|
| | | /* Return true if command should be permitted when a replica set se | |
| | | condary is in "recovering" | |
| | | (unreadable) state. | |
| | | */ | |
| | | virtual bool maintenanceOk() const { return true; /* assumed true p | |
| | | rior to commit */ } | |
| | | | |
| /** @param webUI expose the command in the web ui as localhost:2801
7/<name> | | /** @param webUI expose the command in the web ui as localhost:2801
7/<name> | |
| @param oldName an optional old, deprecated name for the command | | @param oldName an optional old, deprecated name for the command | |
| */ | | */ | |
| Command(const char *_name, bool webUI = false, const char *oldName
= 0); | | Command(const char *_name, bool webUI = false, const char *oldName
= 0); | |
| | | | |
| virtual ~Command() {} | | virtual ~Command() {} | |
| | | | |
| protected: | | protected: | |
| BSONObj getQuery( const BSONObj& cmdObj ) { | | BSONObj getQuery( const BSONObj& cmdObj ) { | |
| if ( cmdObj["query"].type() == Object ) | | if ( cmdObj["query"].type() == Object ) | |
| | | | |
End of changes. 9 change blocks. |
| 6 lines changed or deleted | | 21 lines changed or added | |
|
| curop.h | | curop.h | |
| | | | |
| skipping to change at line 61 | | skipping to change at line 61 | |
| BSONObj updateobj; | | BSONObj updateobj; | |
| | | | |
| // detailed options | | // detailed options | |
| long long cursorid; | | long long cursorid; | |
| int ntoreturn; | | int ntoreturn; | |
| int ntoskip; | | int ntoskip; | |
| bool exhaust; | | bool exhaust; | |
| | | | |
| // debugging/profile info | | // debugging/profile info | |
| int nscanned; | | int nscanned; | |
|
| bool idhack; | | bool idhack; // indicates short circuited code path on an u | |
| bool scanAndOrder; | | pdate to make the update faster | |
| bool moved; | | bool scanAndOrder; // scanandorder query plan aspect was used | |
| | | bool moved; // update resulted in a move (moves are expens | |
| | | ive) | |
| bool fastmod; | | bool fastmod; | |
|
| bool fastmodinsert; | | bool fastmodinsert; // upsert of an $operation. builds a default o | |
| bool upsert; | | bject | |
| unsigned keyUpdates; | | bool upsert; // true if the update actually did an insert | |
| | | int keyUpdates; | |
| | | | |
| // error handling | | // error handling | |
| ExceptionInfo exceptionInfo; | | ExceptionInfo exceptionInfo; | |
| | | | |
| // response info | | // response info | |
| int executionTime; | | int executionTime; | |
| int nreturned; | | int nreturned; | |
| int responseLength; | | int responseLength; | |
| }; | | }; | |
| | | | |
| | | | |
| skipping to change at line 100 | | skipping to change at line 100 | |
| reset(); | | reset(); | |
| } | | } | |
| | | | |
| void reset( int sz = 0 ) { | | void reset( int sz = 0 ) { | |
| _lock.lock(); | | _lock.lock(); | |
| _reset( sz ); | | _reset( sz ); | |
| _lock.unlock(); | | _lock.unlock(); | |
| } | | } | |
| | | | |
| void set( const BSONObj& o ) { | | void set( const BSONObj& o ) { | |
|
| _lock.lock(); | | scoped_spinlock lk(_lock); | |
| try { | | int sz = o.objsize(); | |
| int sz = o.objsize(); | | if ( sz > (int) sizeof(_buf) ) { | |
| | | _reset(TOO_BIG_SENTINEL); | |
| if ( sz > (int) sizeof(_buf) ) { | | | |
| _reset(TOO_BIG_SENTINEL); | | | |
| } | | | |
| else { | | | |
| memcpy(_buf, o.objdata(), sz ); | | | |
| } | | | |
| | | | |
| _lock.unlock(); | | | |
| } | | } | |
|
| catch ( ... ) { | | else { | |
| _lock.unlock(); | | memcpy(_buf, o.objdata(), sz ); | |
| throw; | | | |
| } | | } | |
|
| | | | |
| } | | } | |
| | | | |
| int size() const { return *_size; } | | int size() const { return *_size; } | |
| bool have() const { return size() > 0; } | | bool have() const { return size() > 0; } | |
| | | | |
| BSONObj get() const { | | BSONObj get() const { | |
|
| _lock.lock(); | | scoped_spinlock lk(_lock); | |
| BSONObj o; | | return _get(); | |
| try { | | | |
| o = _get(); | | | |
| _lock.unlock(); | | | |
| } | | | |
| catch ( ... ) { | | | |
| _lock.unlock(); | | | |
| throw; | | | |
| } | | | |
| return o; | | | |
| } | | } | |
| | | | |
| void append( BSONObjBuilder& b , const StringData& name ) const { | | void append( BSONObjBuilder& b , const StringData& name ) const { | |
| scoped_spinlock lk(_lock); | | scoped_spinlock lk(_lock); | |
| BSONObj temp = _get(); | | BSONObj temp = _get(); | |
| b.append( name , temp ); | | b.append( name , temp ); | |
| } | | } | |
| | | | |
| private: | | private: | |
| /** you have to be locked when you call this */ | | /** you have to be locked when you call this */ | |
| | | | |
| skipping to change at line 179 | | skipping to change at line 160 | |
| | | | |
| bool haveQuery() const { return _query.have(); } | | bool haveQuery() const { return _query.have(); } | |
| BSONObj query() { return _query.get(); } | | BSONObj query() { return _query.get(); } | |
| void appendQuery( BSONObjBuilder& b , const StringData& name ) cons
t { _query.append( b , name ); } | | void appendQuery( BSONObjBuilder& b , const StringData& name ) cons
t { _query.append( b , name ); } | |
| | | | |
| void ensureStarted() { | | void ensureStarted() { | |
| if ( _start == 0 ) | | if ( _start == 0 ) | |
| _start = _checkpoint = curTimeMicros64(); | | _start = _checkpoint = curTimeMicros64(); | |
| } | | } | |
| bool isStarted() const { return _start > 0; } | | bool isStarted() const { return _start > 0; } | |
|
| | | | |
| void enter( Client::Context * context ); | | void enter( Client::Context * context ); | |
|
| | | | |
| void leave( Client::Context * context ); | | void leave( Client::Context * context ); | |
|
| | | void reset(); | |
| void reset() { | | void reset( const HostAndPort& remote, int op ); | |
| _reset(); | | | |
| _start = _checkpoint = 0; | | | |
| _opNum = _nextOpNum++; | | | |
| _ns[0] = 0; | | | |
| _debug.reset(); | | | |
| _query.reset(); | | | |
| _active = true; // this should be last for ui clarity | | | |
| } | | | |
| | | | |
| void reset( const HostAndPort& remote, int op ) { | | | |
| reset(); | | | |
| _remote = remote; | | | |
| _op = op; | | | |
| } | | | |
| | | | |
| void markCommand() { _command = true; } | | void markCommand() { _command = true; } | |
| | | | |
| void waitingForLock( int type ) { | | void waitingForLock( int type ) { | |
| _waitingForLock = true; | | _waitingForLock = true; | |
| if ( type > 0 ) | | if ( type > 0 ) | |
| _lockType = 1; | | _lockType = 1; | |
| else | | else | |
| _lockType = -1; | | _lockType = -1; | |
| } | | } | |
| void gotLock() { _waitingForLock = false; } | | void gotLock() { _waitingForLock = false; } | |
| | | | |
| skipping to change at line 229 | | skipping to change at line 193 | |
| } | | } | |
| | | | |
| AtomicUInt opNum() const { return _opNum; } | | AtomicUInt opNum() const { return _opNum; } | |
| | | | |
| /** if this op is running */ | | /** if this op is running */ | |
| bool active() const { return _active; } | | bool active() const { return _active; } | |
| | | | |
| int getLockType() const { return _lockType; } | | int getLockType() const { return _lockType; } | |
| bool isWaitingForLock() const { return _waitingForLock; } | | bool isWaitingForLock() const { return _waitingForLock; } | |
| int getOp() const { return _op; } | | int getOp() const { return _op; } | |
|
| | | unsigned long long startTime() { // micros | |
| /** micros */ | | | |
| unsigned long long startTime() { | | | |
| ensureStarted(); | | ensureStarted(); | |
| return _start; | | return _start; | |
| } | | } | |
|
| | | | |
| void done() { | | void done() { | |
| _active = false; | | _active = false; | |
| _end = curTimeMicros64(); | | _end = curTimeMicros64(); | |
| } | | } | |
|
| | | | |
| unsigned long long totalTimeMicros() { | | unsigned long long totalTimeMicros() { | |
| massert( 12601 , "CurOp not marked done yet" , ! _active ); | | massert( 12601 , "CurOp not marked done yet" , ! _active ); | |
| return _end - startTime(); | | return _end - startTime(); | |
| } | | } | |
|
| | | | |
| int totalTimeMillis() { return (int) (totalTimeMicros() / 1000); } | | int totalTimeMillis() { return (int) (totalTimeMicros() / 1000); } | |
|
| | | | |
| int elapsedMillis() { | | int elapsedMillis() { | |
| unsigned long long total = curTimeMicros64() - startTime(); | | unsigned long long total = curTimeMicros64() - startTime(); | |
| return (int) (total / 1000); | | return (int) (total / 1000); | |
| } | | } | |
|
| | | | |
| int elapsedSeconds() { return elapsedMillis() / 1000; } | | int elapsedSeconds() { return elapsedMillis() / 1000; } | |
|
| | | | |
| void setQuery(const BSONObj& query) { _query.set( query ); } | | void setQuery(const BSONObj& query) { _query.set( query ); } | |
|
| | | | |
| Client * getClient() const { return _client; } | | Client * getClient() const { return _client; } | |
|
| | | BSONObj info(); | |
| BSONObj info() { | | | |
| if( ! cc().getAuthenticationInfo()->isAuthorized("admin") ) { | | | |
| BSONObjBuilder b; | | | |
| b.append("err", "unauthorized"); | | | |
| return b.obj(); | | | |
| } | | | |
| return infoNoauth(); | | | |
| } | | | |
| | | | |
| BSONObj infoNoauth(); | | BSONObj infoNoauth(); | |
|
| | | | |
| string getRemoteString( bool includePort = true ) { return _remote.
toString(includePort); } | | string getRemoteString( bool includePort = true ) { return _remote.
toString(includePort); } | |
|
| | | ProgressMeter& setMessage( const char * msg , unsigned long long pr | |
| ProgressMeter& setMessage( const char * msg , unsigned long long pr | | ogressMeterTotal = 0 , int secondsBetween = 3 ); | |
| ogressMeterTotal = 0 , int secondsBetween = 3 ) { | | | |
| if ( progressMeterTotal ) { | | | |
| if ( _progressMeter.isActive() ) { | | | |
| cout << "about to assert, old _message: " << _message < | | | |
| < " new message:" << msg << endl; | | | |
| assert( ! _progressMeter.isActive() ); | | | |
| } | | | |
| _progressMeter.reset( progressMeterTotal , secondsBetween ) | | | |
| ; | | | |
| } | | | |
| else { | | | |
| _progressMeter.finished(); | | | |
| } | | | |
| | | | |
| _message = msg; | | | |
| | | | |
| return _progressMeter; | | | |
| } | | | |
| | | | |
| string getMessage() const { return _message.toString(); } | | string getMessage() const { return _message.toString(); } | |
| ProgressMeter& getProgressMeter() { return _progressMeter; } | | ProgressMeter& getProgressMeter() { return _progressMeter; } | |
| CurOp *parent() const { return _wrapped; } | | CurOp *parent() const { return _wrapped; } | |
| void kill() { _killed = true; } | | void kill() { _killed = true; } | |
| bool killed() const { return _killed; } | | bool killed() const { return _killed; } | |
| void yielded() { _numYields++; } | | void yielded() { _numYields++; } | |
| void setNS(const char *ns) { | | void setNS(const char *ns) { | |
| strncpy(_ns, ns, Namespace::MaxNsLen); | | strncpy(_ns, ns, Namespace::MaxNsLen); | |
| _ns[Namespace::MaxNsLen] = 0; | | _ns[Namespace::MaxNsLen] = 0; | |
| } | | } | |
|
| friend class Client; | | | |
| | | | |
| private: | | private: | |
|
| | | friend class Client; | |
| | | void _reset(); | |
| | | | |
| static AtomicUInt _nextOpNum; | | static AtomicUInt _nextOpNum; | |
| Client * _client; | | Client * _client; | |
| CurOp * _wrapped; | | CurOp * _wrapped; | |
| unsigned long long _start; | | unsigned long long _start; | |
| unsigned long long _checkpoint; | | unsigned long long _checkpoint; | |
| unsigned long long _end; | | unsigned long long _end; | |
| bool _active; | | bool _active; | |
| int _op; | | int _op; | |
| bool _command; | | bool _command; | |
|
| int _lockType; // see concurrency.h for values | | int _lockType; // see concurrency.h for values | |
| bool _waitingForLock; | | bool _waitingForLock; | |
|
| int _dbprofile; // 0=off, 1=slow, 2=all | | int _dbprofile; // 0=off, 1=slow, 2=all | |
| AtomicUInt _opNum; | | AtomicUInt _opNum; // todo: simple being "unsigned" m | |
| | | ay make more sense here | |
| char _ns[Namespace::MaxNsLen+2]; | | char _ns[Namespace::MaxNsLen+2]; | |
|
| HostAndPort _remote; | | HostAndPort _remote; // CAREFUL here with thread safety | |
| CachedBSONObj _query; | | CachedBSONObj _query; // CachedBSONObj is thread safe | |
| OpDebug _debug; | | OpDebug _debug; | |
| ThreadSafeString _message; | | ThreadSafeString _message; | |
| ProgressMeter _progressMeter; | | ProgressMeter _progressMeter; | |
| volatile bool _killed; | | volatile bool _killed; | |
| int _numYields; | | int _numYields; | |
|
| | | | |
| void _reset() { | | | |
| _command = false; | | | |
| _lockType = 0; | | | |
| _dbprofile = 0; | | | |
| _end = 0; | | | |
| _waitingForLock = false; | | | |
| _message = ""; | | | |
| _progressMeter.finished(); | | | |
| _killed = false; | | | |
| _numYields = 0; | | | |
| } | | | |
| }; | | }; | |
| | | | |
| /* _globalKill: we are shutting down | | /* _globalKill: we are shutting down | |
| otherwise kill attribute set on specified CurOp | | otherwise kill attribute set on specified CurOp | |
| this class does not handle races between interruptJs and the checkFo
rInterrupt functions - those must be | | this class does not handle races between interruptJs and the checkFo
rInterrupt functions - those must be | |
| handled by the client of this class | | handled by the client of this class | |
| */ | | */ | |
| extern class KillCurrentOp { | | extern class KillCurrentOp { | |
| public: | | public: | |
| void killAll(); | | void killAll(); | |
| void kill(AtomicUInt i); | | void kill(AtomicUInt i); | |
| | | | |
| /** @return true if global interrupt and should terminate the opera
tion */ | | /** @return true if global interrupt and should terminate the opera
tion */ | |
| bool globalInterruptCheck() const { return _globalKill; } | | bool globalInterruptCheck() const { return _globalKill; } | |
| | | | |
| void checkForInterrupt( bool heedMutex = true ) { | | void checkForInterrupt( bool heedMutex = true ) { | |
|
| if ( heedMutex && dbMutex.isWriteLocked() ) | | Client& c = cc(); | |
| | | if ( heedMutex && d.dbMutex.isWriteLocked() ) | |
| return; | | return; | |
| if( _globalKill ) | | if( _globalKill ) | |
| uasserted(11600,"interrupted at shutdown"); | | uasserted(11600,"interrupted at shutdown"); | |
|
| if( cc().curop()->killed() ) | | if( c.curop()->killed() ) | |
| uasserted(11601,"interrupted"); | | uasserted(11601,"interrupted"); | |
|
| | | if( c.sometimes(1024) ) { | |
| | | AbstractMessagingPort *p = cc().port(); | |
| | | if( p ) | |
| | | p->assertStillConnected(); | |
| | | } | |
| } | | } | |
| | | | |
| /** @return "" if not interrupted. otherwise, you should stop. */ | | /** @return "" if not interrupted. otherwise, you should stop. */ | |
|
| const char *checkForInterruptNoAssert( bool heedMutex = true ) { | | const char *checkForInterruptNoAssert( /*bool heedMutex = true*/ ) | |
| if ( heedMutex && dbMutex.isWriteLocked() ) | | { | |
| return ""; | | Client& c = cc(); | |
| | | // always called withi false so commented out: | |
| | | /*if ( heedMutex && d.dbMutex.isWriteLocked() ) | |
| | | return "";*/ | |
| if( _globalKill ) | | if( _globalKill ) | |
| return "interrupted at shutdown"; | | return "interrupted at shutdown"; | |
|
| if( cc().curop()->killed() ) | | if( c.curop()->killed() ) | |
| return "interrupted"; | | return "interrupted"; | |
|
| | | if( c.sometimes(1024) ) { | |
| | | try { | |
| | | AbstractMessagingPort *p = cc().port(); | |
| | | if( p ) | |
| | | p->assertStillConnected(); | |
| | | } | |
| | | catch(...) { | |
| | | log() << "no longer connected to client"; | |
| | | return "no longer connected to client"; | |
| | | } | |
| | | } | |
| return ""; | | return ""; | |
| } | | } | |
| | | | |
| private: | | private: | |
| void interruptJs( AtomicUInt *op ); | | void interruptJs( AtomicUInt *op ); | |
| volatile bool _globalKill; | | volatile bool _globalKill; | |
| } killCurrentOp; | | } killCurrentOp; | |
| | | | |
| } | | } | |
| | | | |
End of changes. 32 change blocks. |
| 118 lines changed or deleted | | 58 lines changed or added | |
|
| cursor.h | | cursor.h | |
| | | | |
| skipping to change at line 83 | | skipping to change at line 83 | |
| virtual bool supportGetMore() = 0; | | virtual bool supportGetMore() = 0; | |
| | | | |
| /* called after every query block is iterated -- i.e. between getMo
re() blocks | | /* called after every query block is iterated -- i.e. between getMo
re() blocks | |
| so you can note where we are, if necessary. | | so you can note where we are, if necessary. | |
| */ | | */ | |
| virtual void noteLocation() { } | | virtual void noteLocation() { } | |
| | | | |
| /* called before query getmore block is iterated */ | | /* called before query getmore block is iterated */ | |
| virtual void checkLocation() { } | | virtual void checkLocation() { } | |
| | | | |
|
| | | /** | |
| | | * Called before a document pointed at by an earlier iterate of thi | |
| | | s cursor is to be | |
| | | * modified. It is ok if the current iterate also points to the do | |
| | | cument to be modified. | |
| | | */ | |
| | | virtual void prepareToTouchEarlierIterate() { noteLocation(); } | |
| | | | |
| | | /** Recover from a previous call to prepareToTouchEarlierIterate(). | |
| | | */ | |
| | | virtual void recoverFromTouchingEarlierIterate() { checkLocation(); | |
| | | } | |
| | | | |
| virtual bool supportYields() = 0; | | virtual bool supportYields() = 0; | |
| | | | |
| /** Called before a ClientCursor yield. */ | | /** Called before a ClientCursor yield. */ | |
| virtual bool prepareToYield() { noteLocation(); return supportYield
s(); } | | virtual bool prepareToYield() { noteLocation(); return supportYield
s(); } | |
| | | | |
|
| /** Called after a ClientCursor yield. */ | | /** Called after a ClientCursor yield. Recovers from a previous ca
ll to prepareToYield(). */ | |
| virtual void recoverFromYield() { checkLocation(); } | | virtual void recoverFromYield() { checkLocation(); } | |
| | | | |
| virtual string toString() { return "abstract?"; } | | virtual string toString() { return "abstract?"; } | |
| | | | |
| /* used for multikey index traversal to avoid sending back dups. se
e Matcher::matches(). | | /* used for multikey index traversal to avoid sending back dups. se
e Matcher::matches(). | |
| if a multikey index traversal: | | if a multikey index traversal: | |
| if loc has already been sent, returns true. | | if loc has already been sent, returns true. | |
| otherwise, marks loc as sent. | | otherwise, marks loc as sent. | |
| */ | | */ | |
| virtual bool getsetdup(DiskLoc loc) = 0; | | virtual bool getsetdup(DiskLoc loc) = 0; | |
| | | | |
| virtual bool isMultiKey() const = 0; | | virtual bool isMultiKey() const = 0; | |
| | | | |
|
| | | virtual bool autoDedup() const { return true; } | |
| | | | |
| /** | | /** | |
| * return true if the keys in the index have been modified from the
main doc | | * return true if the keys in the index have been modified from the
main doc | |
| * if you have { a : 1 , b : [ 1 , 2 ] } | | * if you have { a : 1 , b : [ 1 , 2 ] } | |
| * an index on { a : 1 } would not be modified | | * an index on { a : 1 } would not be modified | |
| * an index on { b : 1 } would be since the values of the array are
put in the index | | * an index on { b : 1 } would be since the values of the array are
put in the index | |
| * not the array | | * not the array | |
| */ | | */ | |
| virtual bool modifiedKeys() const = 0; | | virtual bool modifiedKeys() const = 0; | |
| | | | |
| virtual BSONObj prettyIndexBounds() const { return BSONArray(); } | | virtual BSONObj prettyIndexBounds() const { return BSONArray(); } | |
| | | | |
|
| | | /** | |
| | | * If true, this is an unindexed cursor over a capped collection. | |
| | | Currently such cursors must | |
| | | * not own a delegate ClientCursor, due to the implementation of Cl | |
| | | ientCursor::aboutToDelete(). - SERVER-4563 | |
| | | */ | |
| virtual bool capped() const { return false; } | | virtual bool capped() const { return false; } | |
| | | | |
| virtual long long nscanned() = 0; | | virtual long long nscanned() = 0; | |
| | | | |
| // The implementation may return different matchers depending on th
e | | // The implementation may return different matchers depending on th
e | |
| // position of the cursor. If matcher() is nonzero at the start, | | // position of the cursor. If matcher() is nonzero at the start, | |
| // matcher() should be checked each time advance() is called. | | // matcher() should be checked each time advance() is called. | |
| // Implementations which generate their own matcher should return t
his | | // Implementations which generate their own matcher should return t
his | |
| // to avoid a matcher being set manually. | | // to avoid a matcher being set manually. | |
| // Note that the return values differ subtly here | | // Note that the return values differ subtly here | |
| | | | |
| // Used when we want fast matcher lookup | | // Used when we want fast matcher lookup | |
| virtual CoveredIndexMatcher *matcher() const { return 0; } | | virtual CoveredIndexMatcher *matcher() const { return 0; } | |
| // Used when we need to share this matcher with someone else | | // Used when we need to share this matcher with someone else | |
| virtual shared_ptr< CoveredIndexMatcher > matcherPtr() const { retu
rn shared_ptr< CoveredIndexMatcher >(); } | | virtual shared_ptr< CoveredIndexMatcher > matcherPtr() const { retu
rn shared_ptr< CoveredIndexMatcher >(); } | |
| | | | |
|
| | | virtual bool currentMatches( MatchDetails *details = 0 ) { | |
| | | return !matcher() || matcher()->matchesCurrent( this, details ) | |
| | | ; | |
| | | } | |
| | | | |
| // A convenience function for setting the value of matcher() manual
ly | | // A convenience function for setting the value of matcher() manual
ly | |
| // so it may accessed later. Implementations which must generate | | // so it may accessed later. Implementations which must generate | |
| // their own matcher() should assert here. | | // their own matcher() should assert here. | |
| virtual void setMatcher( shared_ptr< CoveredIndexMatcher > matcher
) { | | virtual void setMatcher( shared_ptr< CoveredIndexMatcher > matcher
) { | |
| massert( 13285, "manual matcher config not allowed", false ); | | massert( 13285, "manual matcher config not allowed", false ); | |
| } | | } | |
| | | | |
| virtual void explainDetails( BSONObjBuilder& b ) { return; } | | virtual void explainDetails( BSONObjBuilder& b ) { return; } | |
| }; | | }; | |
| | | | |
| | | | |
End of changes. 5 change blocks. |
| 1 lines changed or deleted | | 27 lines changed or added | |
|
| database.h | | database.h | |
| | | | |
| skipping to change at line 22 | | skipping to change at line 22 | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Affero General Public License for more details. | | * GNU Affero General Public License for more details. | |
| * | | * | |
| * You should have received a copy of the GNU Affero General Public Licen
se | | * You should have received a copy of the GNU Affero General Public Licen
se | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "cmdline.h" | | #include "cmdline.h" | |
|
| | | #include "namespace.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| | | class Extent; | |
| | | class MongoDataFile; | |
| class ClientCursor; | | class ClientCursor; | |
| struct ByLocKey; | | struct ByLocKey; | |
| typedef map<ByLocKey, ClientCursor*> CCByLoc; | | typedef map<ByLocKey, ClientCursor*> CCByLoc; | |
| | | | |
| /** | | /** | |
| * Database represents a database database | | * Database represents a database database | |
| * Each database database has its own set of files -- dbname.ns, dbname
.0, dbname.1, ... | | * Each database database has its own set of files -- dbname.ns, dbname
.0, dbname.1, ... | |
| * NOT memory mapped | | * NOT memory mapped | |
| */ | | */ | |
| class Database { | | class Database { | |
| public: | | public: | |
| static bool _openAllFiles; | | static bool _openAllFiles; | |
| | | | |
|
| | | // you probably need to be in dbHolderMutex when constructing this | |
| Database(const char *nm, /*out*/ bool& newDb, const string& _path =
dbpath); | | Database(const char *nm, /*out*/ bool& newDb, const string& _path =
dbpath); | |
| private: | | private: | |
|
| ~Database(); | | ~Database(); // closes files and other cleanup see below. | |
| public: | | public: | |
| /* you must use this to close - there is essential code in this met
hod that is not in the ~Database destructor. | | /* you must use this to close - there is essential code in this met
hod that is not in the ~Database destructor. | |
| thus the destructor is private. this could be cleaned up one da
y... | | thus the destructor is private. this could be cleaned up one da
y... | |
| */ | | */ | |
| static void closeDatabase( const char *db, const string& path ); | | static void closeDatabase( const char *db, const string& path ); | |
| | | | |
| void openAllFiles(); | | void openAllFiles(); | |
| | | | |
| /** | | /** | |
| * tries to make sure that this hasn't been deleted | | * tries to make sure that this hasn't been deleted | |
| */ | | */ | |
| bool isOk() const { return magic == 781231; } | | bool isOk() const { return magic == 781231; } | |
| | | | |
| bool isEmpty() { return ! namespaceIndex.allocated(); } | | bool isEmpty() { return ! namespaceIndex.allocated(); } | |
| | | | |
| /** | | /** | |
| * total file size of Database in bytes | | * total file size of Database in bytes | |
| */ | | */ | |
| long long fileSize() const; | | long long fileSize() const; | |
| | | | |
|
| int numFiles() const { return (int)files.size(); } | | int numFiles() const; | |
| | | | |
| /** | | /** | |
| * returns file valid for file number n | | * returns file valid for file number n | |
| */ | | */ | |
| boost::filesystem::path fileName( int n ) const; | | boost::filesystem::path fileName( int n ) const; | |
| | | | |
|
| bool exists(int n) const { return boost::filesystem::exists( fileNa | | private: | |
| me( n ) ); } | | bool exists(int n) const; | |
| | | bool openExistingFile( int n ); | |
| | | | |
|
| | | public: | |
| /** | | /** | |
| * return file n. if it doesn't exist, create it | | * return file n. if it doesn't exist, create it | |
| */ | | */ | |
| MongoDataFile* getFile( int n, int sizeNeeded = 0, bool preallocate
Only = false ); | | MongoDataFile* getFile( int n, int sizeNeeded = 0, bool preallocate
Only = false ); | |
| | | | |
| MongoDataFile* addAFile( int sizeNeeded, bool preallocateNextFile )
; | | MongoDataFile* addAFile( int sizeNeeded, bool preallocateNextFile )
; | |
| | | | |
| /** | | /** | |
| * makes sure we have an extra file at the end that is empty | | * makes sure we have an extra file at the end that is empty | |
| * safe to call this multiple times - the implementation will only
preallocate one file | | * safe to call this multiple times - the implementation will only
preallocate one file | |
| | | | |
| skipping to change at line 94 | | skipping to change at line 101 | |
| | | | |
| Extent* allocExtent( const char *ns, int size, bool capped, bool en
forceQuota ); | | Extent* allocExtent( const char *ns, int size, bool capped, bool en
forceQuota ); | |
| | | | |
| MongoDataFile* newestFile(); | | MongoDataFile* newestFile(); | |
| | | | |
| /** | | /** | |
| * @return true if success. false if bad level or error creating p
rofile ns | | * @return true if success. false if bad level or error creating p
rofile ns | |
| */ | | */ | |
| bool setProfilingLevel( int newLevel , string& errmsg ); | | bool setProfilingLevel( int newLevel , string& errmsg ); | |
| | | | |
|
| void flushFiles( bool sync ) const; | | void flushFiles( bool sync ); | |
| | | | |
| /** | | /** | |
| * @return true if ns is part of the database | | * @return true if ns is part of the database | |
| * ns=foo.bar, db=foo returns true | | * ns=foo.bar, db=foo returns true | |
| */ | | */ | |
| bool ownsNS( const string& ns ) const { | | bool ownsNS( const string& ns ) const { | |
| if ( ! startsWith( ns , name ) ) | | if ( ! startsWith( ns , name ) ) | |
| return false; | | return false; | |
| return ns[name.size()] == '.'; | | return ns[name.size()] == '.'; | |
| } | | } | |
|
| | | private: | |
| static bool validDBName( const string& ns ); | | | |
| | | | |
| /** | | /** | |
| * @throws DatabaseDifferCaseCode if the name is a duplicate based
on | | * @throws DatabaseDifferCaseCode if the name is a duplicate based
on | |
| * case insensitive matching. | | * case insensitive matching. | |
| */ | | */ | |
|
| void checkDuplicateUncasedNames() const; | | void checkDuplicateUncasedNames(bool inholderlockalready) const; | |
| | | public: | |
| /** | | /** | |
| * @return name of an existing database with same text name but dif
ferent | | * @return name of an existing database with same text name but dif
ferent | |
| * casing, if one exists. Otherwise the empty string is returned.
If | | * casing, if one exists. Otherwise the empty string is returned.
If | |
| * 'duplicates' is specified, it is filled with all duplicate names
. | | * 'duplicates' is specified, it is filled with all duplicate names
. | |
| */ | | */ | |
|
| static string duplicateUncasedName( const string &name, const strin
g &path, set< string > *duplicates = 0 ); | | static string duplicateUncasedName( bool inholderlockalready, const
string &name, const string &path, set< string > *duplicates = 0 ); | |
| | | | |
|
| public: // this should be private later | | | |
| | | | |
| vector<MongoDataFile*> files; | | | |
| const string name; // "alleyinsider" | | const string name; // "alleyinsider" | |
| const string path; | | const string path; | |
|
| | | | |
| | | private: | |
| | | | |
| | | // must be in the dbLock when touching this (and write locked when | |
| | | writing to of course) | |
| | | // however during Database object construction we aren't, which is | |
| | | ok as it isn't yet visible | |
| | | // to others and we are in the dbholder lock then. | |
| | | vector<MongoDataFile*> _files; | |
| | | | |
| | | public: // this should be private later | |
| | | | |
| NamespaceIndex namespaceIndex; | | NamespaceIndex namespaceIndex; | |
| int profile; // 0=off. | | int profile; // 0=off. | |
| const string profileName; // "alleyinsider.system.profile" | | const string profileName; // "alleyinsider.system.profile" | |
| CCByLoc ccByLoc; | | CCByLoc ccByLoc; | |
| int magic; // used for making sure the object is still loaded in me
mory | | int magic; // used for making sure the object is still loaded in me
mory | |
| }; | | }; | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 13 change blocks. |
| 14 lines changed or deleted | | 27 lines changed or added | |
|
| db.h | | db.h | |
| | | | |
| skipping to change at line 21 | | skipping to change at line 21 | |
| * GNU Affero General Public License for more details. | | * GNU Affero General Public License for more details. | |
| * | | * | |
| * You should have received a copy of the GNU Affero General Public Licen
se | | * You should have received a copy of the GNU Affero General Public Licen
se | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "../pch.h" | | #include "../pch.h" | |
| #include "../util/net/message.h" | | #include "../util/net/message.h" | |
|
| #include "concurrency.h" | | #include "mongomutex.h" | |
| #include "pdfile.h" | | #include "pdfile.h" | |
| #include "curop.h" | | #include "curop.h" | |
| #include "client.h" | | #include "client.h" | |
|
| | | #include "databaseholder.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| // void jniCallback(Message& m, Message& out); | | | |
| | | | |
| /** | | | |
| * class to hold path + dbname -> Database | | | |
| * might be able to optimizer further | | | |
| */ | | | |
| class DatabaseHolder { | | | |
| public: | | | |
| typedef map<string,Database*> DBs; | | | |
| typedef map<string,DBs> Paths; | | | |
| | | | |
| DatabaseHolder() : _size(0) { } | | | |
| | | | |
| bool isLoaded( const string& ns , const string& path ) const { | | | |
| dbMutex.assertAtLeastReadLocked(); | | | |
| Paths::const_iterator x = _paths.find( path ); | | | |
| if ( x == _paths.end() ) | | | |
| return false; | | | |
| const DBs& m = x->second; | | | |
| | | | |
| string db = _todb( ns ); | | | |
| | | | |
| DBs::const_iterator it = m.find(db); | | | |
| return it != m.end(); | | | |
| } | | | |
| | | | |
| Database * get( const string& ns , const string& path ) const { | | | |
| dbMutex.assertAtLeastReadLocked(); | | | |
| Paths::const_iterator x = _paths.find( path ); | | | |
| if ( x == _paths.end() ) | | | |
| return 0; | | | |
| const DBs& m = x->second; | | | |
| | | | |
| string db = _todb( ns ); | | | |
| | | | |
| DBs::const_iterator it = m.find(db); | | | |
| if ( it != m.end() ) | | | |
| return it->second; | | | |
| return 0; | | | |
| } | | | |
| | | | |
| void put( const string& ns , const string& path , Database * db ) { | | | |
| dbMutex.assertWriteLocked(); | | | |
| DBs& m = _paths[path]; | | | |
| Database*& d = m[_todb(ns)]; | | | |
| if ( ! d ) | | | |
| _size++; | | | |
| d = db; | | | |
| } | | | |
| | | | |
| Database* getOrCreate( const string& ns , const string& path , bool | | | |
| & justCreated ); | | | |
| | | | |
| void erase( const string& ns , const string& path ) { | | | |
| dbMutex.assertWriteLocked(); | | | |
| DBs& m = _paths[path]; | | | |
| _size -= (int)m.erase( _todb( ns ) ); | | | |
| } | | | |
| | | | |
| /* force - force close even if something underway - use at shutdown | | | |
| */ | | | |
| bool closeAll( const string& path , BSONObjBuilder& result, bool fo | | | |
| rce ); | | | |
| | | | |
| int size() { | | | |
| return _size; | | | |
| } | | | |
| | | | |
| void forEach(boost::function<void(Database *)> f) const { | | | |
| dbMutex.assertAtLeastReadLocked(); | | | |
| for ( Paths::const_iterator i=_paths.begin(); i!=_paths.end(); | | | |
| i++ ) { | | | |
| DBs m = i->second; | | | |
| for( DBs::const_iterator j=m.begin(); j!=m.end(); j++ ) { | | | |
| f(j->second); | | | |
| } | | | |
| } | | | |
| } | | | |
| | | | |
| /** | | | |
| * gets all unique db names, ignoring paths | | | |
| */ | | | |
| void getAllShortNames( set<string>& all ) const { | | | |
| dbMutex.assertAtLeastReadLocked(); | | | |
| for ( Paths::const_iterator i=_paths.begin(); i!=_paths.end(); | | | |
| i++ ) { | | | |
| DBs m = i->second; | | | |
| for( DBs::const_iterator j=m.begin(); j!=m.end(); j++ ) { | | | |
| all.insert( j->first ); | | | |
| } | | | |
| } | | | |
| } | | | |
| | | | |
| private: | | | |
| | | | |
| string _todb( const string& ns ) const { | | | |
| string d = __todb( ns ); | | | |
| uassert( 13280 , (string)"invalid db name: " + ns , Database::v | | | |
| alidDBName( d ) ); | | | |
| return d; | | | |
| } | | | |
| | | | |
| string __todb( const string& ns ) const { | | | |
| size_t i = ns.find( '.' ); | | | |
| if ( i == string::npos ) { | | | |
| uassert( 13074 , "db name can't be empty" , ns.size() ); | | | |
| return ns; | | | |
| } | | | |
| uassert( 13075 , "db name can't be empty" , i > 0 ); | | | |
| return ns.substr( 0 , i ); | | | |
| } | | | |
| | | | |
| Paths _paths; | | | |
| int _size; | | | |
| | | | |
| }; | | | |
| | | | |
| extern DatabaseHolder dbHolder; | | | |
| | | | |
| struct dbtemprelease { | | struct dbtemprelease { | |
| Client::Context * _context; | | Client::Context * _context; | |
| int _locktype; | | int _locktype; | |
| | | | |
| dbtemprelease() { | | dbtemprelease() { | |
| const Client& c = cc(); | | const Client& c = cc(); | |
| _context = c.getContext(); | | _context = c.getContext(); | |
|
| _locktype = dbMutex.getState(); | | _locktype = d.dbMutex.getState(); | |
| assert( _locktype ); | | assert( _locktype ); | |
| | | | |
| if ( _locktype > 0 ) { | | if ( _locktype > 0 ) { | |
| massert( 10298 , "can't temprelease nested write lock", _lo
cktype == 1); | | massert( 10298 , "can't temprelease nested write lock", _lo
cktype == 1); | |
| if ( _context ) _context->unlocked(); | | if ( _context ) _context->unlocked(); | |
|
| dbMutex.unlock(); | | d.dbMutex.unlock(); | |
| } | | } | |
| else { | | else { | |
| massert( 10299 , "can't temprelease nested read lock", _loc
ktype == -1); | | massert( 10299 , "can't temprelease nested read lock", _loc
ktype == -1); | |
| if ( _context ) _context->unlocked(); | | if ( _context ) _context->unlocked(); | |
|
| dbMutex.unlock_shared(); | | d.dbMutex.unlock_shared(); | |
| } | | } | |
| | | | |
| verify( 14814 , c.curop() ); | | verify( 14814 , c.curop() ); | |
| c.curop()->yielded(); | | c.curop()->yielded(); | |
| | | | |
| } | | } | |
| ~dbtemprelease() { | | ~dbtemprelease() { | |
| if ( _locktype > 0 ) | | if ( _locktype > 0 ) | |
|
| dbMutex.lock(); | | d.dbMutex.lock(); | |
| else | | else | |
|
| dbMutex.lock_shared(); | | d.dbMutex.lock_shared(); | |
| | | | |
| if ( _context ) _context->relocked(); | | if ( _context ) _context->relocked(); | |
| } | | } | |
| }; | | }; | |
| | | | |
| /** must be write locked | | /** must be write locked | |
| no assert (and no release) if nested write lock | | no assert (and no release) if nested write lock | |
| a lot like dbtempreleasecond but no malloc so should be a tiny bit
faster | | a lot like dbtempreleasecond but no malloc so should be a tiny bit
faster | |
| */ | | */ | |
| struct dbtempreleasewritelock { | | struct dbtempreleasewritelock { | |
| Client::Context * _context; | | Client::Context * _context; | |
| int _locktype; | | int _locktype; | |
| dbtempreleasewritelock() { | | dbtempreleasewritelock() { | |
| const Client& c = cc(); | | const Client& c = cc(); | |
| _context = c.getContext(); | | _context = c.getContext(); | |
|
| _locktype = dbMutex.getState(); | | _locktype = d.dbMutex.getState(); | |
| assert( _locktype >= 1 ); | | assert( _locktype >= 1 ); | |
| if( _locktype > 1 ) | | if( _locktype > 1 ) | |
| return; // nested | | return; // nested | |
| if ( _context ) | | if ( _context ) | |
| _context->unlocked(); | | _context->unlocked(); | |
|
| dbMutex.unlock(); | | d.dbMutex.unlock(); | |
| verify( 14845 , c.curop() ); | | verify( 14845 , c.curop() ); | |
| c.curop()->yielded(); | | c.curop()->yielded(); | |
| } | | } | |
| ~dbtempreleasewritelock() { | | ~dbtempreleasewritelock() { | |
| if ( _locktype == 1 ) | | if ( _locktype == 1 ) | |
|
| dbMutex.lock(); | | d.dbMutex.lock(); | |
| if ( _context ) | | if ( _context ) | |
| _context->relocked(); | | _context->relocked(); | |
| } | | } | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| only does a temp release if we're not nested and have a lock | | only does a temp release if we're not nested and have a lock | |
| */ | | */ | |
| struct dbtempreleasecond { | | struct dbtempreleasecond { | |
| dbtemprelease * real; | | dbtemprelease * real; | |
| int locktype; | | int locktype; | |
| | | | |
| dbtempreleasecond() { | | dbtempreleasecond() { | |
| real = 0; | | real = 0; | |
|
| locktype = dbMutex.getState(); | | locktype = d.dbMutex.getState(); | |
| if ( locktype == 1 || locktype == -1 ) | | if ( locktype == 1 || locktype == -1 ) | |
| real = new dbtemprelease(); | | real = new dbtemprelease(); | |
| } | | } | |
| | | | |
| ~dbtempreleasecond() { | | ~dbtempreleasecond() { | |
| if ( real ) { | | if ( real ) { | |
| delete real; | | delete real; | |
| real = 0; | | real = 0; | |
| } | | } | |
| } | | } | |
| | | | |
| bool unlocked() { | | bool unlocked() { | |
|
| return real > 0; | | return real != 0; | |
| } | | } | |
| }; | | }; | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
|
| | | | |
| //#include "dbinfo.h" | | | |
| #include "concurrency.h" | | | |
| | | | |
End of changes. 14 change blocks. |
| 130 lines changed or deleted | | 12 lines changed or added | |
|
| dbclient.h | | dbclient.h | |
| | | | |
| skipping to change at line 28 | | skipping to change at line 28 | |
| * limitations under the License. | | * limitations under the License. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "../pch.h" | | #include "../pch.h" | |
| #include "../util/net/message.h" | | #include "../util/net/message.h" | |
| #include "../util/net/message_port.h" | | #include "../util/net/message_port.h" | |
| #include "../db/jsobj.h" | | #include "../db/jsobj.h" | |
| #include "../db/json.h" | | #include "../db/json.h" | |
|
| | | #include "../db/security.h" | |
| #include <stack> | | #include <stack> | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| /** the query field 'options' can have these bits set: */ | | /** the query field 'options' can have these bits set: */ | |
| enum QueryOptions { | | enum QueryOptions { | |
| /** Tailable means cursor is not closed when the last data is retri
eved. rather, the cursor marks | | /** Tailable means cursor is not closed when the last data is retri
eved. rather, the cursor marks | |
| the final object's position. you can resume using the cursor la
ter, from where it was located, | | the final object's position. you can resume using the cursor la
ter, from where it was located, | |
| if more data were received. Set on dbQuery and dbGetMore. | | if more data were received. Set on dbQuery and dbGetMore. | |
| | | | |
| | | | |
| skipping to change at line 334 | | skipping to change at line 335 | |
| template< class T > | | template< class T > | |
| void appendComplex( const char *fieldName, const T& val ) { | | void appendComplex( const char *fieldName, const T& val ) { | |
| makeComplex(); | | makeComplex(); | |
| BSONObjBuilder b; | | BSONObjBuilder b; | |
| b.appendElements(obj); | | b.appendElements(obj); | |
| b.append(fieldName, val); | | b.append(fieldName, val); | |
| obj = b.obj(); | | obj = b.obj(); | |
| } | | } | |
| }; | | }; | |
| | | | |
|
| | | /** | |
| | | * Represents a full query description, including all options required | |
| | | for the query to be passed on | |
| | | * to other hosts | |
| | | */ | |
| | | class QuerySpec { | |
| | | | |
| | | string _ns; | |
| | | int _ntoskip; | |
| | | int _ntoreturn; | |
| | | int _options; | |
| | | BSONObj _query; | |
| | | BSONObj _fields; | |
| | | Query _queryObj; | |
| | | | |
| | | public: | |
| | | | |
| | | QuerySpec( const string& ns, | |
| | | const BSONObj& query, const BSONObj& fields, | |
| | | int ntoskip, int ntoreturn, int options ) | |
| | | : _ns( ns ), _ntoskip( ntoskip ), _ntoreturn( ntoreturn ), _opt | |
| | | ions( options ), | |
| | | _query( query.getOwned() ), _fields( fields.getOwned() ) , _q | |
| | | ueryObj( _query ) { | |
| | | } | |
| | | | |
| | | QuerySpec() {} | |
| | | | |
| | | bool isEmpty() const { return _ns.size() == 0; } | |
| | | | |
| | | bool isExplain() const { return _queryObj.isExplain(); } | |
| | | BSONObj filter() const { return _queryObj.getFilter(); } | |
| | | | |
| | | BSONObj hint() const { return _queryObj.getHint(); } | |
| | | BSONObj sort() const { return _queryObj.getSort(); } | |
| | | BSONObj query() const { return _query; } | |
| | | BSONObj fields() const { return _fields; } | |
| | | BSONObj* fieldsData() { return &_fields; } | |
| | | | |
| | | // don't love this, but needed downstrem | |
| | | const BSONObj* fieldsPtr() const { return &_fields; } | |
| | | | |
| | | string ns() const { return _ns; } | |
| | | int ntoskip() const { return _ntoskip; } | |
| | | int ntoreturn() const { return _ntoreturn; } | |
| | | int options() const { return _options; } | |
| | | | |
| | | void setFields( BSONObj& o ) { _fields = o.getOwned(); } | |
| | | | |
| | | string toString() const { | |
| | | return str::stream() << "QSpec " << | |
| | | BSON( "ns" << _ns << "n2skip" << _ntoskip << "n2return" << | |
| | | _ntoreturn << "options" << _options | |
| | | << "query" << _query << "fields" << _fields ); | |
| | | } | |
| | | | |
| | | }; | |
| | | | |
| /** Typically one uses the QUERY(...) macro to construct a Query object
. | | /** Typically one uses the QUERY(...) macro to construct a Query object
. | |
| Example: QUERY( "age" << 33 << "school" << "UCLA" ) | | Example: QUERY( "age" << 33 << "school" << "UCLA" ) | |
| */ | | */ | |
| #define QUERY(x) mongo::Query( BSON(x) ) | | #define QUERY(x) mongo::Query( BSON(x) ) | |
| | | | |
|
| | | // Useful utilities for namespaces | |
| | | /** @return the database name portion of an ns string */ | |
| | | string nsGetDB( const string &ns ); | |
| | | | |
| | | /** @return the collection name portion of an ns string */ | |
| | | string nsGetCollection( const string &ns ); | |
| | | | |
| /** | | /** | |
| interface that handles communication with the db | | interface that handles communication with the db | |
| */ | | */ | |
| class DBConnector { | | class DBConnector { | |
| public: | | public: | |
| virtual ~DBConnector() {} | | virtual ~DBConnector() {} | |
| /** actualServer is set to the actual server where they call went i
f there was a choice (SlaveOk) */ | | /** actualServer is set to the actual server where they call went i
f there was a choice (SlaveOk) */ | |
| virtual bool call( Message &toSend, Message &response, bool assertO
k=true , string * actualServer = 0 ) = 0; | | virtual bool call( Message &toSend, Message &response, bool assertO
k=true , string * actualServer = 0 ) = 0; | |
|
| virtual void say( Message &toSend, bool isRetry = false ) = 0; | | virtual void say( Message &toSend, bool isRetry = false , string *
actualServer = 0 ) = 0; | |
| virtual void sayPiggyBack( Message &toSend ) = 0; | | virtual void sayPiggyBack( Message &toSend ) = 0; | |
| /* used by QueryOption_Exhaust. To use that your subclass must imp
lement this. */ | | /* used by QueryOption_Exhaust. To use that your subclass must imp
lement this. */ | |
| virtual bool recv( Message& m ) { assert(false); return false; } | | virtual bool recv( Message& m ) { assert(false); return false; } | |
| // In general, for lazy queries, we'll need to say, recv, then chec
kResponse | | // In general, for lazy queries, we'll need to say, recv, then chec
kResponse | |
| virtual void checkResponse( const char* data, int nReturned, bool*
retry = NULL, string* targetHost = NULL ) { | | virtual void checkResponse( const char* data, int nReturned, bool*
retry = NULL, string* targetHost = NULL ) { | |
| if( retry ) *retry = false; if( targetHost ) *targetHost = ""; | | if( retry ) *retry = false; if( targetHost ) *targetHost = ""; | |
| } | | } | |
| virtual bool lazySupported() const = 0; | | virtual bool lazySupported() const = 0; | |
| }; | | }; | |
| | | | |
| | | | |
| skipping to change at line 431 | | skipping to change at line 493 | |
| @param options see enum QueryOptions - normally not needed to r
un a command | | @param options see enum QueryOptions - normally not needed to r
un a command | |
| @return true if the command returned "ok". | | @return true if the command returned "ok". | |
| */ | | */ | |
| virtual bool runCommand(const string &dbname, const BSONObj& cmd, B
SONObj &info, int options=0); | | virtual bool runCommand(const string &dbname, const BSONObj& cmd, B
SONObj &info, int options=0); | |
| | | | |
| /** Authorize access to a particular database. | | /** Authorize access to a particular database. | |
| Authentication is separate for each database on the server -- y
ou may authenticate for any | | Authentication is separate for each database on the server -- y
ou may authenticate for any | |
| number of databases on a single connection. | | number of databases on a single connection. | |
| The "admin" database is special and once authenticated provides
access to all databases on the | | The "admin" database is special and once authenticated provides
access to all databases on the | |
| server. | | server. | |
|
| @param digestPassword if password is plain text, set this to tr | | @param digestPassword if password is plain text, set this | |
| ue. otherwise assumed to be pre-digested | | to true. otherwise assumed to be pre-digested | |
| | | @param[out] authLevel level of authentication for the giv | |
| | | en user | |
| @return true if successful | | @return true if successful | |
| */ | | */ | |
|
| virtual bool auth(const string &dbname, const string &username, con
st string &pwd, string& errmsg, bool digestPassword = true); | | virtual bool auth(const string &dbname, const string &username, con
st string &pwd, string& errmsg, bool digestPassword = true, Auth::Level * l
evel = NULL); | |
| | | | |
| /** count number of objects in collection ns that match the query c
riteria specified | | /** count number of objects in collection ns that match the query c
riteria specified | |
| throws UserAssertion if database returns an error | | throws UserAssertion if database returns an error | |
| */ | | */ | |
| virtual unsigned long long count(const string &ns, const BSONObj& q
uery = BSONObj(), int options=0, int limit=0, int skip=0 ); | | virtual unsigned long long count(const string &ns, const BSONObj& q
uery = BSONObj(), int options=0, int limit=0, int skip=0 ); | |
| | | | |
| string createPasswordDigest( const string &username , const string
&clearTextPassword ); | | string createPasswordDigest( const string &username , const string
&clearTextPassword ); | |
| | | | |
| /** returns true in isMaster parm if this db is the current master | | /** returns true in isMaster parm if this db is the current master | |
| of a replica pair. | | of a replica pair. | |
| | | | |
| skipping to change at line 474 | | skipping to change at line 537 | |
| @param capped if true, this is a fixed size collection (where ol
d data rolls out). | | @param capped if true, this is a fixed size collection (where ol
d data rolls out). | |
| @param max maximum number of objects if capped (optional). | | @param max maximum number of objects if capped (optional). | |
| | | | |
| returns true if successful. | | returns true if successful. | |
| */ | | */ | |
| bool createCollection(const string &ns, long long size = 0, bool ca
pped = false, int max = 0, BSONObj *info = 0); | | bool createCollection(const string &ns, long long size = 0, bool ca
pped = false, int max = 0, BSONObj *info = 0); | |
| | | | |
| /** Get error result from the last write operation (insert/update/d
elete) on this connection. | | /** Get error result from the last write operation (insert/update/d
elete) on this connection. | |
| @return error message text, or empty string if no error. | | @return error message text, or empty string if no error. | |
| */ | | */ | |
|
| string getLastError(); | | string getLastError(bool fsync = false, bool j = false, int w = 0,
int wtimeout = 0); | |
| | | | |
| /** Get error result from the last write operation (insert/update/d
elete) on this connection. | | /** Get error result from the last write operation (insert/update/d
elete) on this connection. | |
| @return full error object. | | @return full error object. | |
|
| | | | |
| | | If "w" is -1, wait for propagation to majority of nodes. | |
| | | If "wtimeout" is 0, the operation will block indefinitely if ne | |
| | | eded. | |
| */ | | */ | |
|
| virtual BSONObj getLastErrorDetailed(); | | virtual BSONObj getLastErrorDetailed(bool fsync = false, bool j = f
alse, int w = 0, int wtimeout = 0); | |
| | | | |
| /** Can be called with the returned value from getLastErrorDetailed
to extract an error string. | | /** Can be called with the returned value from getLastErrorDetailed
to extract an error string. | |
| If all you need is the string, just call getLastError() instead
. | | If all you need is the string, just call getLastError() instead
. | |
| */ | | */ | |
| static string getLastErrorString( const BSONObj& res ); | | static string getLastErrorString( const BSONObj& res ); | |
| | | | |
| /** Return the last error which has occurred, even if not the very
last operation. | | /** Return the last error which has occurred, even if not the very
last operation. | |
| | | | |
| @return { err : <error message>, nPrev : <how_many_ops_back_occu
rred>, ok : 1 } | | @return { err : <error message>, nPrev : <how_many_ops_back_occu
rred>, ok : 1 } | |
| | | | |
| | | | |
| skipping to change at line 703 | | skipping to change at line 769 | |
| | | | |
| /** Erase / drop an entire database */ | | /** Erase / drop an entire database */ | |
| virtual bool dropDatabase(const string &dbname, BSONObj *info = 0)
{ | | virtual bool dropDatabase(const string &dbname, BSONObj *info = 0)
{ | |
| bool ret = simpleCommand(dbname, info, "dropDatabase"); | | bool ret = simpleCommand(dbname, info, "dropDatabase"); | |
| resetIndexCache(); | | resetIndexCache(); | |
| return ret; | | return ret; | |
| } | | } | |
| | | | |
| virtual string toString() = 0; | | virtual string toString() = 0; | |
| | | | |
|
| /** @return the database name portion of an ns string */ | | | |
| string nsGetDB( const string &ns ) { | | | |
| string::size_type pos = ns.find( "." ); | | | |
| if ( pos == string::npos ) | | | |
| return ns; | | | |
| | | | |
| return ns.substr( 0 , pos ); | | | |
| } | | | |
| | | | |
| /** @return the collection name portion of an ns string */ | | | |
| string nsGetCollection( const string &ns ) { | | | |
| string::size_type pos = ns.find( "." ); | | | |
| if ( pos == string::npos ) | | | |
| return ""; | | | |
| | | | |
| return ns.substr( pos + 1 ); | | | |
| } | | | |
| | | | |
| protected: | | protected: | |
| /** if the result of a command is ok*/ | | /** if the result of a command is ok*/ | |
| bool isOk(const BSONObj&); | | bool isOk(const BSONObj&); | |
| | | | |
| /** if the element contains a not master error */ | | /** if the element contains a not master error */ | |
| bool isNotMasterErrorString( const BSONElement& e ); | | bool isNotMasterErrorString( const BSONElement& e ); | |
| | | | |
| BSONObj _countCmd(const string &ns, const BSONObj& query, int optio
ns, int limit, int skip ); | | BSONObj _countCmd(const string &ns, const BSONObj& query, int optio
ns, int limit, int skip ); | |
| | | | |
| enum QueryOptions availableOptions(); | | enum QueryOptions availableOptions(); | |
| | | | |
| skipping to change at line 880 | | skipping to change at line 928 | |
| false was returned -- it will try to connect again. | | false was returned -- it will try to connect again. | |
| | | | |
| @param serverHostname host to connect to. can include port numb
er ( 127.0.0.1 , 127.0.0.1:5555 ) | | @param serverHostname host to connect to. can include port numb
er ( 127.0.0.1 , 127.0.0.1:5555 ) | |
| */ | | */ | |
| void connect(const string& serverHostname) { | | void connect(const string& serverHostname) { | |
| string errmsg; | | string errmsg; | |
| if( !connect(HostAndPort(serverHostname), errmsg) ) | | if( !connect(HostAndPort(serverHostname), errmsg) ) | |
| throw ConnectException(string("can't connect ") + errmsg); | | throw ConnectException(string("can't connect ") + errmsg); | |
| } | | } | |
| | | | |
|
| virtual bool auth(const string &dbname, const string &username, con
st string &pwd, string& errmsg, bool digestPassword = true); | | virtual bool auth(const string &dbname, const string &username, con
st string &pwd, string& errmsg, bool digestPassword = true, Auth::Level* le
vel=NULL); | |
| | | | |
| virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer
y=Query(), int nToReturn = 0, int nToSkip = 0, | | virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer
y=Query(), int nToReturn = 0, int nToSkip = 0, | |
| const BSONObj *fieldsToRetur
n = 0, int queryOptions = 0 , int batchSize = 0 ) { | | const BSONObj *fieldsToRetur
n = 0, int queryOptions = 0 , int batchSize = 0 ) { | |
| checkConnection(); | | checkConnection(); | |
| return DBClientBase::query( ns, query, nToReturn, nToSkip, fiel
dsToReturn, queryOptions , batchSize ); | | return DBClientBase::query( ns, query, nToReturn, nToSkip, fiel
dsToReturn, queryOptions , batchSize ); | |
| } | | } | |
| | | | |
| /** Uses QueryOption_Exhaust | | /** Uses QueryOption_Exhaust | |
| Exhaust mode sends back all data queries as fast as possible, w
ith no back-and-for for OP_GETMORE. If you are certain | | Exhaust mode sends back all data queries as fast as possible, w
ith no back-and-for for OP_GETMORE. If you are certain | |
| you will exhaust the query, it could be useful. | | you will exhaust the query, it could be useful. | |
| | | | |
| skipping to change at line 921 | | skipping to change at line 969 | |
| return ss.str(); | | return ss.str(); | |
| } | | } | |
| | | | |
| /** Returns the address of the server */ | | /** Returns the address of the server */ | |
| string toString() { return _serverString; } | | string toString() { return _serverString; } | |
| | | | |
| string getServerAddress() const { return _serverString; } | | string getServerAddress() const { return _serverString; } | |
| | | | |
| virtual void killCursor( long long cursorID ); | | virtual void killCursor( long long cursorID ); | |
| virtual bool callRead( Message& toSend , Message& response ) { retu
rn call( toSend , response ); } | | virtual bool callRead( Message& toSend , Message& response ) { retu
rn call( toSend , response ); } | |
|
| virtual void say( Message &toSend, bool isRetry = false ); | | virtual void say( Message &toSend, bool isRetry = false , string *
actualServer = 0 ); | |
| virtual bool recv( Message& m ); | | virtual bool recv( Message& m ); | |
| virtual void checkResponse( const char *data, int nReturned, bool*
retry = NULL, string* host = NULL ); | | virtual void checkResponse( const char *data, int nReturned, bool*
retry = NULL, string* host = NULL ); | |
| virtual bool call( Message &toSend, Message &response, bool assertO
k = true , string * actualServer = 0 ); | | virtual bool call( Message &toSend, Message &response, bool assertO
k = true , string * actualServer = 0 ); | |
| virtual ConnectionString::ConnectionType type() const { return Conn
ectionString::MASTER; } | | virtual ConnectionString::ConnectionType type() const { return Conn
ectionString::MASTER; } | |
| void setSoTimeout(double to) { _so_timeout = to; } | | void setSoTimeout(double to) { _so_timeout = to; } | |
| double getSoTimeout() const { return _so_timeout; } | | double getSoTimeout() const { return _so_timeout; } | |
| | | | |
| virtual bool lazySupported() const { return true; } | | virtual bool lazySupported() const { return true; } | |
| | | | |
| static int getNumConnections() { | | static int getNumConnections() { | |
| | | | |
| skipping to change at line 977 | | skipping to change at line 1025 | |
| | | | |
| /** pings server to check if it's up | | /** pings server to check if it's up | |
| */ | | */ | |
| bool serverAlive( const string &uri ); | | bool serverAlive( const string &uri ); | |
| | | | |
| DBClientBase * createDirectClient(); | | DBClientBase * createDirectClient(); | |
| | | | |
| BSONElement getErrField( const BSONObj& result ); | | BSONElement getErrField( const BSONObj& result ); | |
| bool hasErrField( const BSONObj& result ); | | bool hasErrField( const BSONObj& result ); | |
| | | | |
|
| | | inline std::ostream& operator<<( std::ostream &s, const Query &q ) { | |
| | | return s << q.toString(); | |
| | | } | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
| #include "dbclientcursor.h" | | #include "dbclientcursor.h" | |
| #include "dbclient_rs.h" | | #include "dbclient_rs.h" | |
| #include "undef_macros.h" | | #include "undef_macros.h" | |
| | | | |
End of changes. 13 change blocks. |
| 26 lines changed or deleted | | 84 lines changed or added | |
|
| dbclient_rs.h | | dbclient_rs.h | |
| | | | |
| skipping to change at line 108 | | skipping to change at line 108 | |
| | | | |
| private: | | private: | |
| /** | | /** | |
| * This populates a list of hosts from the list of seeds (discardin
g the | | * This populates a list of hosts from the list of seeds (discardin
g the | |
| * seed list). | | * seed list). | |
| * @param name set name | | * @param name set name | |
| * @param servers seeds | | * @param servers seeds | |
| */ | | */ | |
| ReplicaSetMonitor( const string& name , const vector<HostAndPort>&
servers ); | | ReplicaSetMonitor( const string& name , const vector<HostAndPort>&
servers ); | |
| | | | |
|
| /** | | | |
| * Checks all connections from the host list and sets the current | | | |
| * master. | | | |
| * | | | |
| * @param checkAllSecondaries if set to false, stop immediately whe | | | |
| n | | | |
| * the master is found or when _master is not -1. | | | |
| */ | | | |
| void _check( bool checkAllSecondaries ); | | void _check( bool checkAllSecondaries ); | |
| | | | |
| /** | | /** | |
| * Use replSetGetStatus command to make sure hosts in host list are
up | | * Use replSetGetStatus command to make sure hosts in host list are
up | |
| * and readable. Sets Node::ok appropriately. | | * and readable. Sets Node::ok appropriately. | |
| */ | | */ | |
|
| void _checkStatus( const string& hostAddr ); | | void _checkStatus(DBClientConnection *conn); | |
| | | | |
| /** | | /** | |
| * Add array of hosts to host list. Doesn't do anything if hosts ar
e | | * Add array of hosts to host list. Doesn't do anything if hosts ar
e | |
| * already in host list. | | * already in host list. | |
| * @param hostList the list of hosts to add | | * @param hostList the list of hosts to add | |
| * @param changed if new hosts were added | | * @param changed if new hosts were added | |
| */ | | */ | |
| void _checkHosts(const BSONObj& hostList, bool& changed); | | void _checkHosts(const BSONObj& hostList, bool& changed); | |
| | | | |
| /** | | /** | |
| * Updates host list. | | * Updates host list. | |
|
| * Invariant: if nodesOffset is >= 0, _nodes[nodesOffset].conn shou | | * @param c the connection to check | |
| ld be | | | |
| * equal to conn. | | | |
| * | | | |
| * @param conn the connection to check | | | |
| * @param maybePrimary OUT | | * @param maybePrimary OUT | |
| * @param verbose | | * @param verbose | |
| * @param nodesOffset - offset into _nodes array, -1 for not in it | | * @param nodesOffset - offset into _nodes array, -1 for not in it | |
|
| * | | * @return if the connection is good | |
| * @return true if the connection is good or false if invariant | | | |
| * is broken | | | |
| */ | | */ | |
|
| bool _checkConnection( DBClientConnection* conn, string& maybePrima | | bool _checkConnection( DBClientConnection * c , string& maybePrimar | |
| ry, | | y , bool verbose , int nodesOffset ); | |
| bool verbose, int nodesOffset ); | | | |
| | | | |
| string _getServerAddress_inlock() const; | | string _getServerAddress_inlock() const; | |
| | | | |
| NodeDiff _getHostDiff_inlock( const BSONObj& hostList ); | | NodeDiff _getHostDiff_inlock( const BSONObj& hostList ); | |
| bool _shouldChangeHosts( const BSONObj& hostList, bool inlock ); | | bool _shouldChangeHosts( const BSONObj& hostList, bool inlock ); | |
| | | | |
|
| /** | | | |
| * @return the index to _nodes corresponding to the server address. | | | |
| */ | | | |
| int _find( const string& server ) const ; | | int _find( const string& server ) const ; | |
| int _find_inlock( const string& server ) const ; | | int _find_inlock( const string& server ) const ; | |
|
| | | int _find( const HostAndPort& server ) const ; | |
| | | | |
|
| /** | | mutable mongo::mutex _lock; // protects _nodes | |
| * Checks whether the given connection matches the connection store | | | |
| d in _nodes. | | | |
| * Mainly used for sanity checking to confirm that nodeOffset still | | | |
| * refers to the right connection after releasing and reacquiring | | | |
| * a mutex. | | | |
| */ | | | |
| bool _checkConnMatch_inlock( DBClientConnection* conn, size_t nodeO | | | |
| ffset ) const; | | | |
| | | | |
| // protects _nodes and indices pointing to it (_master & _nextSlave | | | |
| ) | | | |
| mutable mongo::mutex _lock; | | | |
| | | | |
| /** | | | |
| * "Synchronizes" the _checkConnection method. Should ideally be on | | | |
| e mutex per | | | |
| * connection object being used. The purpose of this lock is to mak | | | |
| e sure that | | | |
| * the reply from the connection the lock holder got is the actual | | | |
| response | | | |
| * to what it sent. | | | |
| * | | | |
| * Deadlock WARNING: never acquire this while holding _lock | | | |
| */ | | | |
| mutable mongo::mutex _checkConnectionLock; | | mutable mongo::mutex _checkConnectionLock; | |
| | | | |
| string _name; | | string _name; | |
|
| | | | |
| | | // note these get copied around in the nodes vector so be sure to m | |
| | | aintain copyable semantics here | |
| struct Node { | | struct Node { | |
| Node( const HostAndPort& a , DBClientConnection* c ) | | Node( const HostAndPort& a , DBClientConnection* c ) | |
|
| : addr( a ) , conn(c) , ok( c != NULL ), | | : addr( a ) , conn(c) , ok(true) , | |
| ismaster(false), secondary( false ) , hidden( false ) , p
ingTimeMillis(0) { | | ismaster(false), secondary( false ) , hidden( false ) , p
ingTimeMillis(0) { | |
|
| | | ok = conn.get() == NULL; | |
| } | | } | |
| | | | |
| bool okForSecondaryQueries() const { | | bool okForSecondaryQueries() const { | |
| return ok && secondary && ! hidden; | | return ok && secondary && ! hidden; | |
| } | | } | |
| | | | |
| BSONObj toBSON() const { | | BSONObj toBSON() const { | |
| return BSON( "addr" << addr.toString() << | | return BSON( "addr" << addr.toString() << | |
| "isMaster" << ismaster << | | "isMaster" << ismaster << | |
| "secondary" << secondary << | | "secondary" << secondary << | |
| | | | |
| skipping to change at line 259 | | skipping to change at line 229 | |
| | | | |
| /** Returns false if nomember of the set were reachable, or neither
is | | /** Returns false if nomember of the set were reachable, or neither
is | |
| * master, although, | | * master, although, | |
| * when false returned, you can still try to use this connection ob
ject, it will | | * when false returned, you can still try to use this connection ob
ject, it will | |
| * try reconnects. | | * try reconnects. | |
| */ | | */ | |
| bool connect(); | | bool connect(); | |
| | | | |
| /** Authorize. Authorizes all nodes as needed | | /** Authorize. Authorizes all nodes as needed | |
| */ | | */ | |
|
| virtual bool auth(const string &dbname, const string &username, con
st string &pwd, string& errmsg, bool digestPassword = true ); | | virtual bool auth(const string &dbname, const string &username, con
st string &pwd, string& errmsg, bool digestPassword = true, Auth::Level * l
evel = NULL); | |
| | | | |
| // ----------- simple functions -------------- | | // ----------- simple functions -------------- | |
| | | | |
| /** throws userassertion "no master found" */ | | /** throws userassertion "no master found" */ | |
| virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer
y, int nToReturn = 0, int nToSkip = 0, | | virtual auto_ptr<DBClientCursor> query(const string &ns, Query quer
y, int nToReturn = 0, int nToSkip = 0, | |
| const BSONObj *fieldsToRetur
n = 0, int queryOptions = 0 , int batchSize = 0 ); | | const BSONObj *fieldsToRetur
n = 0, int queryOptions = 0 , int batchSize = 0 ); | |
| | | | |
| /** throws userassertion "no master found" */ | | /** throws userassertion "no master found" */ | |
| virtual BSONObj findOne(const string &ns, const Query& query, const
BSONObj *fieldsToReturn = 0, int queryOptions = 0); | | virtual BSONObj findOne(const string &ns, const Query& query, const
BSONObj *fieldsToReturn = 0, int queryOptions = 0); | |
| | | | |
| | | | |
| skipping to change at line 289 | | skipping to change at line 259 | |
| | | | |
| virtual void killCursor( long long cursorID ); | | virtual void killCursor( long long cursorID ); | |
| | | | |
| // ---- access raw connections ---- | | // ---- access raw connections ---- | |
| | | | |
| DBClientConnection& masterConn(); | | DBClientConnection& masterConn(); | |
| DBClientConnection& slaveConn(); | | DBClientConnection& slaveConn(); | |
| | | | |
| // ---- callback pieces ------- | | // ---- callback pieces ------- | |
| | | | |
|
| virtual void say( Message &toSend, bool isRetry = false ); | | virtual void say( Message &toSend, bool isRetry = false , string* a
ctualServer = 0); | |
| virtual bool recv( Message &toRecv ); | | virtual bool recv( Message &toRecv ); | |
| virtual void checkResponse( const char* data, int nReturned, bool*
retry = NULL, string* targetHost = NULL ); | | virtual void checkResponse( const char* data, int nReturned, bool*
retry = NULL, string* targetHost = NULL ); | |
| | | | |
| /* this is the callback from our underlying connections to notify u
s that we got a "not master" error. | | /* this is the callback from our underlying connections to notify u
s that we got a "not master" error. | |
| */ | | */ | |
| void isntMaster(); | | void isntMaster(); | |
| | | | |
| /* this is used to indicate we got a "not master or secondary" erro
r from a secondary. | | /* this is used to indicate we got a "not master or secondary" erro
r from a secondary. | |
| */ | | */ | |
| void isntSecondary(); | | void isntSecondary(); | |
| | | | |
End of changes. 13 change blocks. |
| 51 lines changed or deleted | | 14 lines changed or added | |
|
| dbclientcursor.h | | dbclientcursor.h | |
| | | | |
| skipping to change at line 30 | | skipping to change at line 30 | |
| #include "../pch.h" | | #include "../pch.h" | |
| #include "../util/net/message.h" | | #include "../util/net/message.h" | |
| #include "../db/jsobj.h" | | #include "../db/jsobj.h" | |
| #include "../db/json.h" | | #include "../db/json.h" | |
| #include <stack> | | #include <stack> | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| class AScopedConnection; | | class AScopedConnection; | |
| | | | |
|
| /** for mock purposes only -- do not create variants of DBClientCursor, | | /** for mock purposes only -- do not create variants of DBClientCursor, | |
| nor hang code here */ | | nor hang code here | |
| class DBClientCursorInterface { | | @see DBClientMockCursor | |
| | | */ | |
| | | class DBClientCursorInterface : boost::noncopyable { | |
| public: | | public: | |
| virtual ~DBClientCursorInterface() {} | | virtual ~DBClientCursorInterface() {} | |
|
| | | | |
| virtual bool more() = 0; | | virtual bool more() = 0; | |
| virtual BSONObj next() = 0; | | virtual BSONObj next() = 0; | |
|
| | | | |
| // TODO bring more of the DBClientCursor interface to here | | // TODO bring more of the DBClientCursor interface to here | |
|
| | | | |
| protected: | | protected: | |
| DBClientCursorInterface() {} | | DBClientCursorInterface() {} | |
| }; | | }; | |
| | | | |
| /** Queries return a cursor object */ | | /** Queries return a cursor object */ | |
| class DBClientCursor : public DBClientCursorInterface { | | class DBClientCursor : public DBClientCursorInterface { | |
| public: | | public: | |
| /** If true, safe to call next(). Requests more from server if nec
essary. */ | | /** If true, safe to call next(). Requests more from server if nec
essary. */ | |
| bool more(); | | bool more(); | |
| | | | |
| /** If true, there is more in our local buffers to be fetched via n
ext(). Returns | | /** If true, there is more in our local buffers to be fetched via n
ext(). Returns | |
| false when a getMore request back to server would be required.
You can use this | | false when a getMore request back to server would be required.
You can use this | |
| if you want to exhaust whatever data has been fetched to the cl
ient already but | | if you want to exhaust whatever data has been fetched to the cl
ient already but | |
| then perhaps stop. | | then perhaps stop. | |
| */ | | */ | |
|
| int objsLeftInBatch() const { _assertIfNull(); return _putBack.size
() + b.nReturned - b.pos; } | | int objsLeftInBatch() const { _assertIfNull(); return _putBack.size
() + batch.nReturned - batch.pos; } | |
| bool moreInCurrentBatch() { return objsLeftInBatch() > 0; } | | bool moreInCurrentBatch() { return objsLeftInBatch() > 0; } | |
| | | | |
| /** next | | /** next | |
| @return next object in the result cursor. | | @return next object in the result cursor. | |
| on an error at the remote server, you will get back: | | on an error at the remote server, you will get back: | |
| { $err: <string> } | | { $err: <string> } | |
| if you do not want to handle that yourself, call nextSafe(). | | if you do not want to handle that yourself, call nextSafe(). | |
| */ | | */ | |
| BSONObj next(); | | BSONObj next(); | |
| | | | |
| | | | |
| skipping to change at line 90 | | skipping to change at line 89 | |
| return o; | | return o; | |
| } | | } | |
| | | | |
| /** peek ahead at items buffered for future next() calls. | | /** peek ahead at items buffered for future next() calls. | |
| never requests new data from the server. so peek only effectiv
e | | never requests new data from the server. so peek only effectiv
e | |
| with what is already buffered. | | with what is already buffered. | |
| WARNING: no support for _putBack yet! | | WARNING: no support for _putBack yet! | |
| */ | | */ | |
| void peek(vector<BSONObj>&, int atMost); | | void peek(vector<BSONObj>&, int atMost); | |
| | | | |
|
| | | // Peeks at first element, if exists | |
| | | BSONObj peekFirst(); | |
| | | | |
| /** | | /** | |
| * peek ahead and see if an error occurred, and get the error if so
. | | * peek ahead and see if an error occurred, and get the error if so
. | |
| */ | | */ | |
| bool peekError(BSONObj* error = NULL); | | bool peekError(BSONObj* error = NULL); | |
| | | | |
| /** | | /** | |
| iterate the rest of the cursor and return the number if items | | iterate the rest of the cursor and return the number if items | |
| */ | | */ | |
| int itcount() { | | int itcount() { | |
| int c = 0; | | int c = 0; | |
| | | | |
| skipping to change at line 139 | | skipping to change at line 141 | |
| query(_query), | | query(_query), | |
| nToReturn(_nToReturn), | | nToReturn(_nToReturn), | |
| haveLimit( _nToReturn > 0 && !(queryOptions & QueryOption_Curso
rTailable)), | | haveLimit( _nToReturn > 0 && !(queryOptions & QueryOption_Curso
rTailable)), | |
| nToSkip(_nToSkip), | | nToSkip(_nToSkip), | |
| fieldsToReturn(_fieldsToReturn), | | fieldsToReturn(_fieldsToReturn), | |
| opts(queryOptions), | | opts(queryOptions), | |
| batchSize(bs==1?2:bs), | | batchSize(bs==1?2:bs), | |
| cursorId(), | | cursorId(), | |
| _ownCursor( true ), | | _ownCursor( true ), | |
| wasError( false ) { | | wasError( false ) { | |
|
| | | _finishConsInit(); | |
| } | | } | |
| | | | |
| DBClientCursor( DBClientBase* client, const string &_ns, long long
_cursorId, int _nToReturn, int options ) : | | DBClientCursor( DBClientBase* client, const string &_ns, long long
_cursorId, int _nToReturn, int options ) : | |
| _client(client), | | _client(client), | |
| ns(_ns), | | ns(_ns), | |
| nToReturn( _nToReturn ), | | nToReturn( _nToReturn ), | |
| haveLimit( _nToReturn > 0 && !(options & QueryOption_CursorTail
able)), | | haveLimit( _nToReturn > 0 && !(options & QueryOption_CursorTail
able)), | |
| opts( options ), | | opts( options ), | |
| cursorId(_cursorId), | | cursorId(_cursorId), | |
| _ownCursor( true ) { | | _ownCursor( true ) { | |
|
| | | _finishConsInit(); | |
| } | | } | |
| | | | |
| virtual ~DBClientCursor(); | | virtual ~DBClientCursor(); | |
| | | | |
| long long getCursorId() const { return cursorId; } | | long long getCursorId() const { return cursorId; } | |
| | | | |
| /** by default we "own" the cursor and will send the server a KillC
ursor | | /** by default we "own" the cursor and will send the server a KillC
ursor | |
| message when ~DBClientCursor() is called. This function overrid
es that. | | message when ~DBClientCursor() is called. This function overrid
es that. | |
| */ | | */ | |
| void decouple() { _ownCursor = false; } | | void decouple() { _ownCursor = false; } | |
| | | | |
| void attach( AScopedConnection * conn ); | | void attach( AScopedConnection * conn ); | |
| | | | |
|
| | | string originalHost() const { return _originalHost; } | |
| | | | |
| | | Message* getMessage(){ return batch.m.get(); } | |
| | | | |
| /** | | /** | |
| * actually does the query | | * actually does the query | |
| */ | | */ | |
| bool init(); | | bool init(); | |
| | | | |
| void initLazy( bool isRetry = false ); | | void initLazy( bool isRetry = false ); | |
| bool initLazyFinish( bool& retry ); | | bool initLazyFinish( bool& retry ); | |
| | | | |
| class Batch : boost::noncopyable { | | class Batch : boost::noncopyable { | |
| friend class DBClientCursor; | | friend class DBClientCursor; | |
| | | | |
| skipping to change at line 185 | | skipping to change at line 193 | |
| const char *data; | | const char *data; | |
| public: | | public: | |
| Batch() : m( new Message() ), nReturned(), pos(), data() { } | | Batch() : m( new Message() ), nReturned(), pos(), data() { } | |
| }; | | }; | |
| | | | |
| private: | | private: | |
| friend class DBClientBase; | | friend class DBClientBase; | |
| friend class DBClientConnection; | | friend class DBClientConnection; | |
| | | | |
| int nextBatchSize(); | | int nextBatchSize(); | |
|
| | | void _finishConsInit(); | |
| | | | |
|
| Batch b; | | Batch batch; | |
| DBClientBase* _client; | | DBClientBase* _client; | |
|
| | | string _originalHost; | |
| string ns; | | string ns; | |
| BSONObj query; | | BSONObj query; | |
| int nToReturn; | | int nToReturn; | |
| bool haveLimit; | | bool haveLimit; | |
| int nToSkip; | | int nToSkip; | |
| const BSONObj *fieldsToReturn; | | const BSONObj *fieldsToReturn; | |
| int opts; | | int opts; | |
| int batchSize; | | int batchSize; | |
| stack< BSONObj > _putBack; | | stack< BSONObj > _putBack; | |
| int resultFlags; | | int resultFlags; | |
| | | | |
End of changes. 12 change blocks. |
| 8 lines changed or deleted | | 18 lines changed or added | |
|
| engine_v8.h | | engine_v8.h | |
| | | | |
| skipping to change at line 38 | | skipping to change at line 38 | |
| class V8ScriptEngine; | | class V8ScriptEngine; | |
| class V8Scope; | | class V8Scope; | |
| | | | |
| typedef Handle< Value > (*v8Function) ( V8Scope* scope, const v8::Argum
ents& args ); | | typedef Handle< Value > (*v8Function) ( V8Scope* scope, const v8::Argum
ents& args ); | |
| | | | |
| // Preemption is going to be allowed for the v8 mutex, and some of our
v8 | | // Preemption is going to be allowed for the v8 mutex, and some of our
v8 | |
| // usage is not preemption safe. So we are using an additional mutex t
hat | | // usage is not preemption safe. So we are using an additional mutex t
hat | |
| // will not be preempted. The V8Lock should be used in place of v8::Lo
cker | | // will not be preempted. The V8Lock should be used in place of v8::Lo
cker | |
| // except in certain special cases involving interrupts. | | // except in certain special cases involving interrupts. | |
| namespace v8Locks { | | namespace v8Locks { | |
|
| | | struct InterruptLock { | |
| | | InterruptLock(); | |
| | | ~InterruptLock(); | |
| | | }; | |
| | | | |
| // the implementations are quite simple - objects must be destroyed
in | | // the implementations are quite simple - objects must be destroyed
in | |
| // reverse of the order created, and should not be shared between t
hreads | | // reverse of the order created, and should not be shared between t
hreads | |
| struct RecursiveLock { | | struct RecursiveLock { | |
| RecursiveLock(); | | RecursiveLock(); | |
| ~RecursiveLock(); | | ~RecursiveLock(); | |
| bool _unlock; | | bool _unlock; | |
| }; | | }; | |
| struct RecursiveUnlock { | | struct RecursiveUnlock { | |
| RecursiveUnlock(); | | RecursiveUnlock(); | |
| ~RecursiveUnlock(); | | ~RecursiveUnlock(); | |
| bool _lock; | | bool _lock; | |
| }; | | }; | |
| } // namespace v8Locks | | } // namespace v8Locks | |
|
| | | | |
| class V8Lock { | | class V8Lock { | |
|
| | | public: | |
| | | V8Lock() : _preemptionLock(Isolate::GetCurrent()){} | |
| | | | |
| | | private: | |
| v8Locks::RecursiveLock _noPreemptionLock; | | v8Locks::RecursiveLock _noPreemptionLock; | |
| v8::Locker _preemptionLock; | | v8::Locker _preemptionLock; | |
| }; | | }; | |
|
| | | | |
| struct V8Unlock { | | struct V8Unlock { | |
|
| | | public: | |
| | | V8Unlock() : _preemptionUnlock(Isolate::GetCurrent()){} | |
| | | | |
| | | private: | |
| v8::Unlocker _preemptionUnlock; | | v8::Unlocker _preemptionUnlock; | |
| v8Locks::RecursiveUnlock _noPreemptionUnlock; | | v8Locks::RecursiveUnlock _noPreemptionUnlock; | |
| }; | | }; | |
| | | | |
|
| | | class BSONHolder { | |
| | | public: | |
| | | | |
| | | BSONHolder( BSONObj obj ) { | |
| | | _obj = obj.getOwned(); | |
| | | _modified = false; | |
| | | } | |
| | | | |
| | | ~BSONHolder() { | |
| | | } | |
| | | | |
| | | BSONObj _obj; | |
| | | bool _modified; | |
| | | }; | |
| | | | |
| class V8Scope : public Scope { | | class V8Scope : public Scope { | |
| public: | | public: | |
| | | | |
| V8Scope( V8ScriptEngine * engine ); | | V8Scope( V8ScriptEngine * engine ); | |
| ~V8Scope(); | | ~V8Scope(); | |
| | | | |
| virtual void reset(); | | virtual void reset(); | |
| virtual void init( const BSONObj * data ); | | virtual void init( const BSONObj * data ); | |
| | | | |
| virtual void localConnect( const char * dbName ); | | virtual void localConnect( const char * dbName ); | |
| | | | |
| skipping to change at line 98 | | skipping to change at line 128 | |
| virtual void setFunction( const char *field , const char * code ); | | virtual void setFunction( const char *field , const char * code ); | |
| // virtual void setThis( const BSONObj * obj ); | | // virtual void setThis( const BSONObj * obj ); | |
| | | | |
| virtual void rename( const char * from , const char * to ); | | virtual void rename( const char * from , const char * to ); | |
| | | | |
| virtual ScriptingFunction _createFunction( const char * code ); | | virtual ScriptingFunction _createFunction( const char * code ); | |
| Local< v8::Function > __createFunction( const char * code ); | | Local< v8::Function > __createFunction( const char * code ); | |
| virtual int invoke( ScriptingFunction func , const BSONObj* args, c
onst BSONObj* recv, int timeoutMs = 0 , bool ignoreReturn = false, bool rea
dOnlyArgs = false, bool readOnlyRecv = false ); | | virtual int invoke( ScriptingFunction func , const BSONObj* args, c
onst BSONObj* recv, int timeoutMs = 0 , bool ignoreReturn = false, bool rea
dOnlyArgs = false, bool readOnlyRecv = false ); | |
| virtual bool exec( const StringData& code , const string& name , bo
ol printResult , bool reportError , bool assertOnError, int timeoutMs ); | | virtual bool exec( const StringData& code , const string& name , bo
ol printResult , bool reportError , bool assertOnError, int timeoutMs ); | |
| virtual string getError() { return _error; } | | virtual string getError() { return _error; } | |
|
| | | virtual bool hasOutOfMemoryException(); | |
| | | | |
| virtual void injectNative( const char *field, NativeFunction func,
void* data = 0 ); | | virtual void injectNative( const char *field, NativeFunction func,
void* data = 0 ); | |
| void injectNative( const char *field, NativeFunction func, Handle<v
8::Object>& obj, void* data = 0 ); | | void injectNative( const char *field, NativeFunction func, Handle<v
8::Object>& obj, void* data = 0 ); | |
| void injectV8Function( const char *field, v8Function func ); | | void injectV8Function( const char *field, v8Function func ); | |
| void injectV8Function( const char *field, v8Function func, Handle<v
8::Object>& obj ); | | void injectV8Function( const char *field, v8Function func, Handle<v
8::Object>& obj ); | |
| void injectV8Function( const char *field, v8Function func, Handle<v
8::Template>& t ); | | void injectV8Function( const char *field, v8Function func, Handle<v
8::Template>& t ); | |
| Handle<v8::FunctionTemplate> createV8Function( v8Function func ); | | Handle<v8::FunctionTemplate> createV8Function( v8Function func ); | |
| | | | |
| void gc(); | | void gc(); | |
| | | | |
| Handle< Context > context() const { return _context; } | | Handle< Context > context() const { return _context; } | |
| | | | |
| v8::Local<v8::Object> mongoToV8( const mongo::BSONObj & m , bool ar
ray = 0 , bool readOnly = false ); | | v8::Local<v8::Object> mongoToV8( const mongo::BSONObj & m , bool ar
ray = 0 , bool readOnly = false ); | |
| v8::Handle<v8::Object> mongoToLZV8( const mongo::BSONObj & m , bool
array = 0 , bool readOnly = false ); | | v8::Handle<v8::Object> mongoToLZV8( const mongo::BSONObj & m , bool
array = 0 , bool readOnly = false ); | |
| mongo::BSONObj v8ToMongo( v8::Handle<v8::Object> o , int depth = 0
); | | mongo::BSONObj v8ToMongo( v8::Handle<v8::Object> o , int depth = 0
); | |
| | | | |
|
| void v8ToMongoElement( BSONObjBuilder & b , v8::Handle<v8::String> | | void v8ToMongoElement( BSONObjBuilder & b , const string sname , v8 | |
| name , | | ::Handle<v8::Value> value , int depth = 0, BSONObj* originalParent=0 ); | |
| const string sname , v8::Handle<v8::Value> v | | | |
| alue , int depth = 0, BSONObj* originalParent=0 ); | | | |
| v8::Handle<v8::Value> mongoToV8Element( const BSONElement &f, bool
readOnly = false ); | | v8::Handle<v8::Value> mongoToV8Element( const BSONElement &f, bool
readOnly = false ); | |
| virtual void append( BSONObjBuilder & builder , const char * fieldN
ame , const char * scopeName ); | | virtual void append( BSONObjBuilder & builder , const char * fieldN
ame , const char * scopeName ); | |
| | | | |
| v8::Function * getNamedCons( const char * name ); | | v8::Function * getNamedCons( const char * name ); | |
| v8::Function * getObjectIdCons(); | | v8::Function * getObjectIdCons(); | |
| Local< v8::Value > newId( const OID &id ); | | Local< v8::Value > newId( const OID &id ); | |
| | | | |
|
| Persistent<v8::Object> wrapBSONObject(Local<v8::Object> obj, BSONOb
j* data); | | Persistent<v8::Object> wrapBSONObject(Local<v8::Object> obj, BSONHo
lder* data); | |
| Persistent<v8::Object> wrapArrayObject(Local<v8::Object> obj, char*
data); | | Persistent<v8::Object> wrapArrayObject(Local<v8::Object> obj, char*
data); | |
| | | | |
| v8::Handle<v8::String> getV8Str(string str); | | v8::Handle<v8::String> getV8Str(string str); | |
| // inline v8::Handle<v8::String> getV8Str(string str) { return v8::S
tring::New(str.c_str()); } | | // inline v8::Handle<v8::String> getV8Str(string str) { return v8::S
tring::New(str.c_str()); } | |
| inline v8::Handle<v8::String> getLocalV8Str(string str) { return v8
::String::New(str.c_str()); } | | inline v8::Handle<v8::String> getLocalV8Str(string str) { return v8
::String::New(str.c_str()); } | |
| | | | |
|
| | | v8::Isolate* getIsolate() { return _isolate; } | |
| | | Persistent<Context> getContext() { return _context; } | |
| | | | |
| | | // call with v8 mutex: | |
| | | void enableV8Interrupt(); | |
| | | void disableV8Interrupt(); | |
| | | bool pauseV8Interrupt(); | |
| | | bool resumeV8Interrupt(); | |
| | | | |
| Handle<v8::String> V8STR_CONN; | | Handle<v8::String> V8STR_CONN; | |
| Handle<v8::String> V8STR_ID; | | Handle<v8::String> V8STR_ID; | |
| Handle<v8::String> V8STR_LENGTH; | | Handle<v8::String> V8STR_LENGTH; | |
| Handle<v8::String> V8STR_LEN; | | Handle<v8::String> V8STR_LEN; | |
| Handle<v8::String> V8STR_TYPE; | | Handle<v8::String> V8STR_TYPE; | |
| Handle<v8::String> V8STR_ISOBJECTID; | | Handle<v8::String> V8STR_ISOBJECTID; | |
| Handle<v8::String> V8STR_NATIVE_FUNC; | | Handle<v8::String> V8STR_NATIVE_FUNC; | |
| Handle<v8::String> V8STR_NATIVE_DATA; | | Handle<v8::String> V8STR_NATIVE_DATA; | |
| Handle<v8::String> V8STR_V8_FUNC; | | Handle<v8::String> V8STR_V8_FUNC; | |
| Handle<v8::String> V8STR_RETURN; | | Handle<v8::String> V8STR_RETURN; | |
| | | | |
| skipping to change at line 152 | | skipping to change at line 191 | |
| Handle<v8::String> V8STR_I; | | Handle<v8::String> V8STR_I; | |
| Handle<v8::String> V8STR_EMPTY; | | Handle<v8::String> V8STR_EMPTY; | |
| Handle<v8::String> V8STR_MINKEY; | | Handle<v8::String> V8STR_MINKEY; | |
| Handle<v8::String> V8STR_MAXKEY; | | Handle<v8::String> V8STR_MAXKEY; | |
| Handle<v8::String> V8STR_NUMBERLONG; | | Handle<v8::String> V8STR_NUMBERLONG; | |
| Handle<v8::String> V8STR_NUMBERINT; | | Handle<v8::String> V8STR_NUMBERINT; | |
| Handle<v8::String> V8STR_DBPTR; | | Handle<v8::String> V8STR_DBPTR; | |
| Handle<v8::String> V8STR_BINDATA; | | Handle<v8::String> V8STR_BINDATA; | |
| Handle<v8::String> V8STR_WRAPPER; | | Handle<v8::String> V8STR_WRAPPER; | |
| Handle<v8::String> V8STR_RO; | | Handle<v8::String> V8STR_RO; | |
|
| Handle<v8::String> V8STR_MODIFIED; | | Handle<v8::String> V8STR_FULLNAME; | |
| | | Handle<v8::String> V8STR_BSON; | |
| | | | |
| private: | | private: | |
| void _startCall(); | | void _startCall(); | |
| | | | |
| static Handle< Value > nativeCallback( V8Scope* scope, const Argume
nts &args ); | | static Handle< Value > nativeCallback( V8Scope* scope, const Argume
nts &args ); | |
| static v8::Handle< v8::Value > v8Callback( const v8::Arguments &arg
s ); | | static v8::Handle< v8::Value > v8Callback( const v8::Arguments &arg
s ); | |
| static Handle< Value > load( V8Scope* scope, const Arguments &args
); | | static Handle< Value > load( V8Scope* scope, const Arguments &args
); | |
| static Handle< Value > Print(V8Scope* scope, const v8::Arguments& a
rgs); | | static Handle< Value > Print(V8Scope* scope, const v8::Arguments& a
rgs); | |
| static Handle< Value > Version(V8Scope* scope, const v8::Arguments&
args); | | static Handle< Value > Version(V8Scope* scope, const v8::Arguments&
args); | |
| static Handle< Value > GCV8(V8Scope* scope, const v8::Arguments& ar
gs); | | static Handle< Value > GCV8(V8Scope* scope, const v8::Arguments& ar
gs); | |
| | | | |
| skipping to change at line 180 | | skipping to change at line 220 | |
| vector< Persistent<Value> > _funcs; | | vector< Persistent<Value> > _funcs; | |
| v8::Persistent<v8::Object> _emptyObj; | | v8::Persistent<v8::Object> _emptyObj; | |
| | | | |
| v8::Persistent<v8::Function> _wrapper; | | v8::Persistent<v8::Function> _wrapper; | |
| | | | |
| enum ConnectState { NOT , LOCAL , EXTERNAL }; | | enum ConnectState { NOT , LOCAL , EXTERNAL }; | |
| ConnectState _connectState; | | ConnectState _connectState; | |
| | | | |
| std::map <string, v8::Persistent <v8::String> > _strCache; | | std::map <string, v8::Persistent <v8::String> > _strCache; | |
| | | | |
|
| | | Persistent<v8::FunctionTemplate> lzFunctionTemplate; | |
| Persistent<v8::ObjectTemplate> lzObjectTemplate; | | Persistent<v8::ObjectTemplate> lzObjectTemplate; | |
| Persistent<v8::ObjectTemplate> roObjectTemplate; | | Persistent<v8::ObjectTemplate> roObjectTemplate; | |
| Persistent<v8::ObjectTemplate> lzArrayTemplate; | | Persistent<v8::ObjectTemplate> lzArrayTemplate; | |
| Persistent<v8::ObjectTemplate> internalFieldObjects; | | Persistent<v8::ObjectTemplate> internalFieldObjects; | |
|
| | | v8::Isolate* _isolate; | |
| }; | | }; | |
| | | | |
| class V8ScriptEngine : public ScriptEngine { | | class V8ScriptEngine : public ScriptEngine { | |
| public: | | public: | |
| V8ScriptEngine(); | | V8ScriptEngine(); | |
| virtual ~V8ScriptEngine(); | | virtual ~V8ScriptEngine(); | |
| | | | |
| virtual Scope * createScope() { return new V8Scope( this ); } | | virtual Scope * createScope() { return new V8Scope( this ); } | |
| | | | |
| virtual void runTest() {} | | virtual void runTest() {} | |
| | | | |
| bool utf8Ok() const { return true; } | | bool utf8Ok() const { return true; } | |
| | | | |
| class V8UnlockForClient : public Unlocker { | | class V8UnlockForClient : public Unlocker { | |
|
| V8Unlock u_; | | // V8Unlock u_; | |
| }; | | }; | |
| | | | |
| virtual auto_ptr<Unlocker> newThreadUnlocker() { return auto_ptr< U
nlocker >( new V8UnlockForClient ); } | | virtual auto_ptr<Unlocker> newThreadUnlocker() { return auto_ptr< U
nlocker >( new V8UnlockForClient ); } | |
| | | | |
| virtual void interrupt( unsigned opSpec ); | | virtual void interrupt( unsigned opSpec ); | |
| virtual void interruptAll(); | | virtual void interruptAll(); | |
| | | | |
| private: | | private: | |
| friend class V8Scope; | | friend class V8Scope; | |
| }; | | }; | |
| | | | |
| skipping to change at line 228 | | skipping to change at line 270 | |
| const char* data () const { return _data.c_str(); } | | const char* data () const { return _data.c_str(); } | |
| size_t length () const { return _data.length(); } | | size_t length () const { return _data.length(); } | |
| private: | | private: | |
| // string _str; | | // string _str; | |
| // const char* _data; | | // const char* _data; | |
| std::string _data; | | std::string _data; | |
| // size_t _len; | | // size_t _len; | |
| }; | | }; | |
| | | | |
| extern ScriptEngine * globalScriptEngine; | | extern ScriptEngine * globalScriptEngine; | |
|
| extern map< unsigned, int > __interruptSpecToThreadId; | | | |
| | | | |
| } | | } | |
| | | | |
End of changes. 15 change blocks. |
| 8 lines changed or deleted | | 48 lines changed or added | |
|
| goodies.h | | goodies.h | |
| | | | |
| skipping to change at line 57 | | skipping to change at line 57 | |
| #include <pthread.h> | | #include <pthread.h> | |
| #include <execinfo.h> | | #include <execinfo.h> | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| inline pthread_t GetCurrentThreadId() { | | inline pthread_t GetCurrentThreadId() { | |
| return pthread_self(); | | return pthread_self(); | |
| } | | } | |
| | | | |
| /* use "addr2line -CFe <exe>" to parse. */ | | /* use "addr2line -CFe <exe>" to parse. */ | |
|
| inline void printStackTrace( ostream &o = cout ) { | | inline void printStackTrace(ostream &o = cout) { | |
| void *b[20]; | | void *b[20]; | |
| | | | |
| int size = backtrace(b, 20); | | int size = backtrace(b, 20); | |
| for (int i = 0; i < size; i++) | | for (int i = 0; i < size; i++) | |
| o << hex << b[i] << dec << ' '; | | o << hex << b[i] << dec << ' '; | |
| o << endl; | | o << endl; | |
| | | | |
| char **strings; | | char **strings; | |
| | | | |
| strings = backtrace_symbols(b, size); | | strings = backtrace_symbols(b, size); | |
| for (int i = 0; i < size; i++) | | for (int i = 0; i < size; i++) | |
| o << ' ' << strings[i] << '\n'; | | o << ' ' << strings[i] << '\n'; | |
| o.flush(); | | o.flush(); | |
| free (strings); | | free (strings); | |
| } | | } | |
| #else | | #else | |
|
| inline void printStackTrace( ostream &o = cout ) { } | | inline void printStackTrace(ostream &o = cout) { } | |
| #endif | | #endif | |
| | | | |
| bool isPrime(int n); | | bool isPrime(int n); | |
| int nextPrime(int n); | | int nextPrime(int n); | |
| | | | |
| inline void dumpmemory(const char *data, int len) { | | inline void dumpmemory(const char *data, int len) { | |
| if ( len > 1024 ) | | if ( len > 1024 ) | |
| len = 1024; | | len = 1024; | |
| try { | | try { | |
| const char *q = data; | | const char *q = data; | |
| | | | |
| skipping to change at line 160 | | skipping to change at line 160 | |
| #if !defined(_WIN32) | | #if !defined(_WIN32) | |
| typedef int HANDLE; | | typedef int HANDLE; | |
| inline void strcpy_s(char *dst, unsigned len, const char *src) { | | inline void strcpy_s(char *dst, unsigned len, const char *src) { | |
| assert( strlen(src) < len ); | | assert( strlen(src) < len ); | |
| strcpy(dst, src); | | strcpy(dst, src); | |
| } | | } | |
| #else | | #else | |
| typedef void *HANDLE; | | typedef void *HANDLE; | |
| #endif | | #endif | |
| | | | |
|
| /* thread local "value" rather than a pointer | | | |
| good for things which have copy constructors (and the copy construct | | | |
| or is fast enough) | | | |
| e.g. | | | |
| ThreadLocalValue<int> myint; | | | |
| */ | | | |
| template<class T> | | | |
| class ThreadLocalValue { | | | |
| public: | | | |
| ThreadLocalValue( T def = 0 ) : _default( def ) { } | | | |
| | | | |
| T get() const { | | | |
| T * val = _val.get(); | | | |
| if ( val ) | | | |
| return *val; | | | |
| return _default; | | | |
| } | | | |
| | | | |
| void set( const T& i ) { | | | |
| T *v = _val.get(); | | | |
| if( v ) { | | | |
| *v = i; | | | |
| return; | | | |
| } | | | |
| v = new T(i); | | | |
| _val.reset( v ); | | | |
| } | | | |
| | | | |
| private: | | | |
| boost::thread_specific_ptr<T> _val; | | | |
| const T _default; | | | |
| }; | | | |
| | | | |
| class ProgressMeter : boost::noncopyable { | | class ProgressMeter : boost::noncopyable { | |
| public: | | public: | |
|
| ProgressMeter( unsigned long long total , int secondsBetween = 3 ,
int checkInterval = 100 ) { | | ProgressMeter( unsigned long long total , int secondsBetween = 3 ,
int checkInterval = 100 , string units = "" ) : _units(units) { | |
| reset( total , secondsBetween , checkInterval ); | | reset( total , secondsBetween , checkInterval ); | |
| } | | } | |
| | | | |
| ProgressMeter() { | | ProgressMeter() { | |
| _active = 0; | | _active = 0; | |
|
| | | _units = ""; | |
| } | | } | |
| | | | |
| // typically you do ProgressMeterHolder | | // typically you do ProgressMeterHolder | |
| void reset( unsigned long long total , int secondsBetween = 3 , int
checkInterval = 100 ) { | | void reset( unsigned long long total , int secondsBetween = 3 , int
checkInterval = 100 ) { | |
| _total = total; | | _total = total; | |
| _secondsBetween = secondsBetween; | | _secondsBetween = secondsBetween; | |
| _checkInterval = checkInterval; | | _checkInterval = checkInterval; | |
| | | | |
| _done = 0; | | _done = 0; | |
| _hits = 0; | | _hits = 0; | |
| | | | |
| skipping to change at line 229 | | skipping to change at line 198 | |
| bool isActive() { | | bool isActive() { | |
| return _active; | | return _active; | |
| } | | } | |
| | | | |
| /** | | /** | |
| * @param n how far along we are relative to the total # we set in
CurOp::setMessage | | * @param n how far along we are relative to the total # we set in
CurOp::setMessage | |
| * @return if row was printed | | * @return if row was printed | |
| */ | | */ | |
| bool hit( int n = 1 ) { | | bool hit( int n = 1 ) { | |
| if ( ! _active ) { | | if ( ! _active ) { | |
|
| cout << "warning: hit on in-active ProgressMeter" << endl; | | cout << "warning: hit an inactive ProgressMeter" << endl; | |
| return false; | | return false; | |
| } | | } | |
| | | | |
| _done += n; | | _done += n; | |
| _hits++; | | _hits++; | |
| if ( _hits % _checkInterval ) | | if ( _hits % _checkInterval ) | |
| return false; | | return false; | |
| | | | |
| int t = (int) time(0); | | int t = (int) time(0); | |
| if ( t - _lastTime < _secondsBetween ) | | if ( t - _lastTime < _secondsBetween ) | |
| return false; | | return false; | |
| | | | |
| if ( _total > 0 ) { | | if ( _total > 0 ) { | |
| int per = (int)( ( (double)_done * 100.0 ) / (double)_total
); | | int per = (int)( ( (double)_done * 100.0 ) / (double)_total
); | |
|
| cout << "\t\t" << _done << "/" << _total << "\t" << per << | | cout << "\t\t" << _done << "/" << _total << "\t" << per << | |
| "%" << endl; | | "%"; | |
| | | | |
| | | if ( ! _units.empty() ) { | |
| | | cout << "\t(" << _units << ")" << endl; | |
| | | } | |
| | | else { | |
| | | cout << endl; | |
| | | } | |
| } | | } | |
| _lastTime = t; | | _lastTime = t; | |
| return true; | | return true; | |
| } | | } | |
| | | | |
|
| | | void setUnits( string units ) { | |
| | | _units = units; | |
| | | } | |
| | | | |
| void setTotalWhileRunning( unsigned long long total ) { | | void setTotalWhileRunning( unsigned long long total ) { | |
| _total = total; | | _total = total; | |
| } | | } | |
| | | | |
| unsigned long long done() const { return _done; } | | unsigned long long done() const { return _done; } | |
| | | | |
| unsigned long long hits() const { return _hits; } | | unsigned long long hits() const { return _hits; } | |
| | | | |
| unsigned long long total() const { return _total; } | | unsigned long long total() const { return _total; } | |
| | | | |
| string toString() const { | | string toString() const { | |
| if ( ! _active ) | | if ( ! _active ) | |
| return ""; | | return ""; | |
| stringstream buf; | | stringstream buf; | |
| buf << _done << "/" << _total << " " << (_done*100)/_total << "
%"; | | buf << _done << "/" << _total << " " << (_done*100)/_total << "
%"; | |
|
| | | | |
| | | if ( ! _units.empty() ) { | |
| | | buf << "\t(" << _units << ")" << endl; | |
| | | } | |
| | | | |
| return buf.str(); | | return buf.str(); | |
| } | | } | |
| | | | |
| bool operator==( const ProgressMeter& other ) const { | | bool operator==( const ProgressMeter& other ) const { | |
| return this == &other; | | return this == &other; | |
| } | | } | |
| private: | | private: | |
| | | | |
| bool _active; | | bool _active; | |
| | | | |
| unsigned long long _total; | | unsigned long long _total; | |
| int _secondsBetween; | | int _secondsBetween; | |
| int _checkInterval; | | int _checkInterval; | |
| | | | |
| unsigned long long _done; | | unsigned long long _done; | |
| unsigned long long _hits; | | unsigned long long _hits; | |
| int _lastTime; | | int _lastTime; | |
|
| | | | |
| | | string _units; | |
| }; | | }; | |
| | | | |
| // e.g.: | | // e.g.: | |
| // CurOp * op = cc().curop(); | | // CurOp * op = cc().curop(); | |
| // ProgressMeterHolder pm( op->setMessage( "index: (1/3) external sort"
, d->stats.nrecords , 10 ) ); | | // ProgressMeterHolder pm( op->setMessage( "index: (1/3) external sort"
, d->stats.nrecords , 10 ) ); | |
| // loop { pm.hit(); } | | // loop { pm.hit(); } | |
| class ProgressMeterHolder : boost::noncopyable { | | class ProgressMeterHolder : boost::noncopyable { | |
| public: | | public: | |
| ProgressMeterHolder( ProgressMeter& pm ) | | ProgressMeterHolder( ProgressMeter& pm ) | |
| : _pm( pm ) { | | : _pm( pm ) { | |
| | | | |
| skipping to change at line 327 | | skipping to change at line 314 | |
| | | | |
| class TicketHolder { | | class TicketHolder { | |
| public: | | public: | |
| TicketHolder( int num ) : _mutex("TicketHolder") { | | TicketHolder( int num ) : _mutex("TicketHolder") { | |
| _outof = num; | | _outof = num; | |
| _num = num; | | _num = num; | |
| } | | } | |
| | | | |
| bool tryAcquire() { | | bool tryAcquire() { | |
| scoped_lock lk( _mutex ); | | scoped_lock lk( _mutex ); | |
|
| return _tryAcquire(); | | if ( _num <= 0 ) { | |
| } | | if ( _num < 0 ) { | |
| | | cerr << "DISASTER! in TicketHolder" << endl; | |
| void waitForTicket() { | | } | |
| scoped_lock lk( _mutex ); | | return false; | |
| | | | |
| while( ! _tryAcquire() ) { | | | |
| _newTicket.wait( lk.boost() ); | | | |
| } | | } | |
|
| | | _num--; | |
| | | return true; | |
| } | | } | |
| | | | |
| void release() { | | void release() { | |
|
| { | | scoped_lock lk( _mutex ); | |
| scoped_lock lk( _mutex ); | | _num++; | |
| _num++; | | | |
| } | | | |
| _newTicket.notify_one(); | | | |
| } | | } | |
| | | | |
| void resize( int newSize ) { | | void resize( int newSize ) { | |
|
| { | | scoped_lock lk( _mutex ); | |
| scoped_lock lk( _mutex ); | | int used = _outof - _num; | |
| | | if ( used > newSize ) { | |
| int used = _outof - _num; | | cout << "ERROR: can't resize since we're using (" << used < | |
| if ( used > newSize ) { | | < ") more than newSize(" << newSize << ")" << endl; | |
| cout << "ERROR: can't resize since we're using (" << us | | return; | |
| ed << ") more than newSize(" << newSize << ")" << endl; | | | |
| return; | | | |
| } | | | |
| | | | |
| _outof = newSize; | | | |
| _num = _outof - used; | | | |
| } | | } | |
| | | | |
|
| // Potentially wasteful, but easier to see is correct | | _outof = newSize; | |
| _newTicket.notify_all(); | | _num = _outof - used; | |
| } | | } | |
| | | | |
| int available() const { | | int available() const { | |
| return _num; | | return _num; | |
| } | | } | |
| | | | |
| int used() const { | | int used() const { | |
| return _outof - _num; | | return _outof - _num; | |
| } | | } | |
| | | | |
| int outof() const { return _outof; } | | int outof() const { return _outof; } | |
| | | | |
| private: | | private: | |
|
| | | | |
| bool _tryAcquire(){ | | | |
| if ( _num <= 0 ) { | | | |
| if ( _num < 0 ) { | | | |
| cerr << "DISASTER! in TicketHolder" << endl; | | | |
| } | | | |
| return false; | | | |
| } | | | |
| _num--; | | | |
| return true; | | | |
| } | | | |
| | | | |
| int _outof; | | int _outof; | |
| int _num; | | int _num; | |
| mongo::mutex _mutex; | | mongo::mutex _mutex; | |
|
| boost::condition_variable_any _newTicket; | | | |
| }; | | }; | |
| | | | |
| class TicketHolderReleaser { | | class TicketHolderReleaser { | |
| public: | | public: | |
| TicketHolderReleaser( TicketHolder * holder ) { | | TicketHolderReleaser( TicketHolder * holder ) { | |
| _holder = holder; | | _holder = holder; | |
| } | | } | |
| | | | |
| ~TicketHolderReleaser() { | | ~TicketHolderReleaser() { | |
| _holder->release(); | | _holder->release(); | |
| } | | } | |
| private: | | private: | |
| TicketHolder * _holder; | | TicketHolder * _holder; | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * this is a thread safe string | | * this is a thread safe string | |
| * you will never get a bad pointer, though data may be mungedd | | * you will never get a bad pointer, though data may be mungedd | |
| */ | | */ | |
|
| class ThreadSafeString { | | class ThreadSafeString : boost::noncopyable { | |
| public: | | public: | |
| ThreadSafeString( size_t size=256 ) | | ThreadSafeString( size_t size=256 ) | |
| : _size( size ) , _buf( new char[size] ) { | | : _size( size ) , _buf( new char[size] ) { | |
| memset( _buf , 0 , _size ); | | memset( _buf , 0 , _size ); | |
| } | | } | |
| | | | |
| ThreadSafeString( const ThreadSafeString& other ) | | ThreadSafeString( const ThreadSafeString& other ) | |
| : _size( other._size ) , _buf( new char[_size] ) { | | : _size( other._size ) , _buf( new char[_size] ) { | |
| strncpy( _buf , other._buf , _size ); | | strncpy( _buf , other._buf , _size ); | |
| } | | } | |
| | | | |
| skipping to change at line 502 | | skipping to change at line 466 | |
| T* operator->() const { return _p; } | | T* operator->() const { return _p; } | |
| T& operator*() const { return *_p; } | | T& operator*() const { return *_p; } | |
| | | | |
| // convert from ptr<T> | | // convert from ptr<T> | |
| operator T* () const { return _p; } | | operator T* () const { return _p; } | |
| | | | |
| private: | | private: | |
| T* _p; | | T* _p; | |
| }; | | }; | |
| | | | |
|
| /** Hmmmm */ | | using boost::shared_ptr; | |
| using namespace boost; | | using boost::scoped_ptr; | |
| | | using boost::scoped_array; | |
| | | using boost::intrusive_ptr; | |
| | | using boost::bad_lexical_cast; | |
| | | using boost::dynamic_pointer_cast; | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 19 change blocks. |
| 83 lines changed or deleted | | 49 lines changed or added | |
|
| hostandport.h | | hostandport.h | |
| | | | |
| skipping to change at line 28 | | skipping to change at line 28 | |
| #pragma once | | #pragma once | |
| | | | |
| #include "sock.h" | | #include "sock.h" | |
| #include "../../db/cmdline.h" | | #include "../../db/cmdline.h" | |
| #include "../mongoutils/str.h" | | #include "../mongoutils/str.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| using namespace mongoutils; | | using namespace mongoutils; | |
| | | | |
|
| | | void dynHostResolve(string& name, int& port); | |
| | | string dynHostMyName(); | |
| | | | |
| /** helper for manipulating host:port connection endpoints. | | /** helper for manipulating host:port connection endpoints. | |
| */ | | */ | |
| struct HostAndPort { | | struct HostAndPort { | |
| HostAndPort() : _port(-1) { } | | HostAndPort() : _port(-1) { } | |
| | | | |
|
| /** From a string hostname[:portnumber] | | /** From a string hostname[:portnumber] or a #dynname | |
| Throws user assertion if bad config string or bad port #. | | Throws user assertion if bad config string or bad port #. | |
|
| */ | | */ | |
| HostAndPort(string s); | | HostAndPort(string s); | |
| | | | |
| /** @param p port number. -1 is ok to use default. */ | | /** @param p port number. -1 is ok to use default. */ | |
|
| HostAndPort(string h, int p /*= -1*/) : _host(h), _port(p) { } | | HostAndPort(string h, int p /*= -1*/) : _host(h), _port(p) { | |
| | | assert( !str::startsWith(h, '#') ); | |
| HostAndPort(const SockAddr& sock ) | | | |
| : _host( sock.getAddr() ) , _port( sock.getPort() ) { | | | |
| } | | } | |
| | | | |
|
| static HostAndPort me() { | | HostAndPort(const SockAddr& sock ) : _host( sock.getAddr() ) , _por | |
| return HostAndPort("localhost", cmdLine.port); | | t( sock.getPort() ) { } | |
| } | | | |
| | | static HostAndPort me() { return HostAndPort("localhost", cmdLine.p | |
| | | ort); } | |
| | | | |
| /* uses real hostname instead of localhost */ | | /* uses real hostname instead of localhost */ | |
| static HostAndPort Me(); | | static HostAndPort Me(); | |
| | | | |
| bool operator<(const HostAndPort& r) const { | | bool operator<(const HostAndPort& r) const { | |
|
| if( _host < r._host ) | | string h = host(); | |
| | | string rh = r.host(); | |
| | | if( h < rh ) | |
| return true; | | return true; | |
|
| if( _host == r._host ) | | if( h == rh ) | |
| return port() < r.port(); | | return port() < r.port(); | |
| return false; | | return false; | |
| } | | } | |
| | | | |
| bool operator==(const HostAndPort& r) const { | | bool operator==(const HostAndPort& r) const { | |
|
| return _host == r._host && port() == r.port(); | | return host() == r.host() && port() == r.port(); | |
| } | | } | |
| | | | |
|
| bool operator!=(const HostAndPort& r) const { | | bool operator!=(const HostAndPort& r) const { return !(*this == r); | |
| return _host != r._host || port() != r.port(); | | } | |
| } | | | |
| | | | |
| /* returns true if the host/port combo identifies this process inst
ance. */ | | /* returns true if the host/port combo identifies this process inst
ance. */ | |
|
| bool isSelf() const; // defined in message.cpp | | bool isSelf() const; // defined in isself.cpp | |
| | | | |
| bool isLocalHost() const; | | bool isLocalHost() const; | |
| | | | |
| /** | | /** | |
| * @param includePort host:port if true, host otherwise | | * @param includePort host:port if true, host otherwise | |
| */ | | */ | |
| string toString( bool includePort=true ) const; | | string toString( bool includePort=true ) const; | |
| | | | |
|
| operator string() const { return toString(); } | | // get the logical name if using a #dynhostname instead of resolvin | |
| | | g to current actual name | |
| | | string dynString() const; | |
| | | string toStringLong() const; | |
| | | | |
|
| string host() const { return _host; } | | operator string() const { return toString(); } | |
| | | | |
|
| int port() const { return _port >= 0 ? _port : CmdLine::DefaultDBPo | | bool empty() const { | |
| rt; } | | return _dynName.empty() && _host.empty() && _port < 0; | |
| bool hasPort() const { return _port >= 0; } | | } | |
| void setPort( int port ) { _port = port; } | | string host() const { | |
| | | if( !dyn() ) | |
| | | return _host; | |
| | | string h = _dynName; | |
| | | int p; | |
| | | dynHostResolve(h, p); | |
| | | return h; | |
| | | } | |
| | | int port() const { | |
| | | int p = -2; | |
| | | if( dyn() ) { | |
| | | string h = _dynName; | |
| | | dynHostResolve(h,p); | |
| | | } | |
| | | else { | |
| | | p = _port; | |
| | | } | |
| | | return p >= 0 ? p : CmdLine::DefaultDBPort; | |
| | | } | |
| | | bool hasPort() const { | |
| | | int p = -2; | |
| | | if( dyn() ) { | |
| | | string h = _dynName; | |
| | | dynHostResolve(h,p); | |
| | | } | |
| | | else { | |
| | | p = _port; | |
| | | } | |
| | | return p >= 0; | |
| | | } | |
| | | void setPort( int port ) { | |
| | | if( dyn() ) { | |
| | | log() << "INFO skipping setPort() HostAndPort dyn()=true" < | |
| | | < endl; | |
| | | return; | |
| | | } | |
| | | _port = port; | |
| | | } | |
| | | | |
| private: | | private: | |
|
| | | bool dyn() const { return !_dynName.empty(); } | |
| | | void init(const char *); | |
| // invariant (except full obj assignment): | | // invariant (except full obj assignment): | |
|
| | | string _dynName; // when this is set, _host and _port aren't used,
rather, we look up the dyn info every time. | |
| string _host; | | string _host; | |
| int _port; // -1 indicates unspecified | | int _port; // -1 indicates unspecified | |
| }; | | }; | |
| | | | |
| inline HostAndPort HostAndPort::Me() { | | inline HostAndPort HostAndPort::Me() { | |
|
| | | { | |
| | | string s = dynHostMyName(); | |
| | | if( !s.empty() ) | |
| | | return HostAndPort(s); | |
| | | } | |
| | | | |
| const char* ips = cmdLine.bind_ip.c_str(); | | const char* ips = cmdLine.bind_ip.c_str(); | |
| while(*ips) { | | while(*ips) { | |
| string ip; | | string ip; | |
| const char * comma = strchr(ips, ','); | | const char * comma = strchr(ips, ','); | |
| if (comma) { | | if (comma) { | |
| ip = string(ips, comma - ips); | | ip = string(ips, comma - ips); | |
| ips = comma + 1; | | ips = comma + 1; | |
| } | | } | |
| else { | | else { | |
| ip = string(ips); | | ip = string(ips); | |
| | | | |
| skipping to change at line 117 | | skipping to change at line 166 | |
| return h; | | return h; | |
| } | | } | |
| } | | } | |
| | | | |
| string h = getHostName(); | | string h = getHostName(); | |
| assert( !h.empty() ); | | assert( !h.empty() ); | |
| assert( h != "localhost" ); | | assert( h != "localhost" ); | |
| return HostAndPort(h, cmdLine.port); | | return HostAndPort(h, cmdLine.port); | |
| } | | } | |
| | | | |
|
| | | inline string HostAndPort::dynString() const { | |
| | | return dyn() ? _dynName : toString(); | |
| | | } | |
| | | | |
| | | inline string HostAndPort::toStringLong() const { | |
| | | return _dynName + ':' + toString(); | |
| | | } | |
| | | | |
| inline string HostAndPort::toString( bool includePort ) const { | | inline string HostAndPort::toString( bool includePort ) const { | |
|
| | | string h = host(); | |
| | | int p = port(); | |
| | | | |
| if ( ! includePort ) | | if ( ! includePort ) | |
|
| return _host; | | return h; | |
| | | | |
| stringstream ss; | | stringstream ss; | |
|
| ss << _host; | | ss << h; | |
| if ( _port != -1 ) { | | if ( p != -1 ) { | |
| ss << ':'; | | ss << ':'; | |
| #if defined(_DEBUG) | | #if defined(_DEBUG) | |
|
| if( _port >= 44000 && _port < 44100 ) { | | if( p >= 44000 && p < 44100 ) { | |
| log() << "warning: special debug port 44xxx used" << endl; | | log() << "warning: special debug port 44xxx used" << endl; | |
|
| ss << _port+1; | | ss << p+1; | |
| } | | } | |
| else | | else | |
|
| ss << _port; | | ss << p; | |
| #else | | #else | |
|
| ss << _port; | | ss << p; | |
| #endif | | #endif | |
| } | | } | |
| return ss.str(); | | return ss.str(); | |
| } | | } | |
| | | | |
| inline bool HostAndPort::isLocalHost() const { | | inline bool HostAndPort::isLocalHost() const { | |
|
| | | string _host = host(); | |
| return ( _host == "localhost" | | return ( _host == "localhost" | |
| || startsWith(_host.c_str(), "127.") | | || startsWith(_host.c_str(), "127.") | |
| || _host == "::1" | | || _host == "::1" | |
| || _host == "anonymous unix socket" | | || _host == "anonymous unix socket" | |
| || _host.c_str()[0] == '/' // unix socket | | || _host.c_str()[0] == '/' // unix socket | |
| ); | | ); | |
| } | | } | |
| | | | |
|
| inline HostAndPort::HostAndPort(string s) { | | inline void HostAndPort::init(const char *p) { | |
| const char *p = s.c_str(); | | uassert(13110, "HostAndPort: bad host:port config string", *p); | |
| uassert(13110, "HostAndPort: bad config string", *p); | | assert( *p != '#' ); | |
| | | assert( _dynName.empty() ); | |
| const char *colon = strrchr(p, ':'); | | const char *colon = strrchr(p, ':'); | |
| if( colon ) { | | if( colon ) { | |
| int port = atoi(colon+1); | | int port = atoi(colon+1); | |
| uassert(13095, "HostAndPort: bad port #", port > 0); | | uassert(13095, "HostAndPort: bad port #", port > 0); | |
| _host = string(p,colon-p); | | _host = string(p,colon-p); | |
| _port = port; | | _port = port; | |
| } | | } | |
| else { | | else { | |
| // no port specified. | | // no port specified. | |
| _host = p; | | _host = p; | |
| _port = -1; | | _port = -1; | |
| } | | } | |
| } | | } | |
| | | | |
|
| | | inline HostAndPort::HostAndPort(string s) { | |
| | | const char *p = s.c_str(); | |
| | | if( *p == '#' ) { | |
| | | _dynName = s; | |
| | | _port = -2; | |
| | | _host = "invalid_hostname_dyn_in_use"; | |
| | | } | |
| | | else { | |
| | | init(p); | |
| | | } | |
| | | } | |
| | | | |
| } | | } | |
| | | | |
End of changes. 27 change blocks. |
| 32 lines changed or deleted | | 110 lines changed or added | |
|
| log.h | | log.h | |
| | | | |
| skipping to change at line 21 | | skipping to change at line 21 | |
| * Unless required by applicable law or agreed to in writing, software | | * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | | * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | |
| * See the License for the specific language governing permissions and | | * See the License for the specific language governing permissions and | |
| * limitations under the License. | | * limitations under the License. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include <string.h> | | #include <string.h> | |
|
| | | #include <sstream> | |
| #include <errno.h> | | #include <errno.h> | |
|
| | | #include <vector> | |
| | | #include <boost/shared_ptr.hpp> | |
| | | #include <boost/scoped_ptr.hpp> | |
| | | #include <boost/thread/tss.hpp> | |
| #include "../bson/util/builder.h" | | #include "../bson/util/builder.h" | |
|
| | | #include "debug_util.h" | |
| | | | |
| #ifndef _WIN32 | | #ifndef _WIN32 | |
|
| //#include <syslog.h> | | #include <syslog.h> | |
| #endif | | #endif | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| | | enum ExitCode; | |
| | | | |
| enum LogLevel { LL_DEBUG , LL_INFO , LL_NOTICE , LL_WARNING , LL_ERROR
, LL_SEVERE }; | | enum LogLevel { LL_DEBUG , LL_INFO , LL_NOTICE , LL_WARNING , LL_ERROR
, LL_SEVERE }; | |
| | | | |
| inline const char * logLevelToString( LogLevel l ) { | | inline const char * logLevelToString( LogLevel l ) { | |
| switch ( l ) { | | switch ( l ) { | |
| case LL_DEBUG: | | case LL_DEBUG: | |
| case LL_INFO: | | case LL_INFO: | |
| case LL_NOTICE: | | case LL_NOTICE: | |
| return ""; | | return ""; | |
| case LL_WARNING: | | case LL_WARNING: | |
| return "warning" ; | | return "warning" ; | |
| case LL_ERROR: | | case LL_ERROR: | |
| return "ERROR"; | | return "ERROR"; | |
| case LL_SEVERE: | | case LL_SEVERE: | |
| return "SEVERE"; | | return "SEVERE"; | |
| default: | | default: | |
| return "UNKNOWN"; | | return "UNKNOWN"; | |
| } | | } | |
| } | | } | |
| | | | |
|
| | | #ifndef _WIN32 | |
| | | inline const int logLevelToSysLogLevel( LogLevel l) { | |
| | | switch ( l ) { | |
| | | case LL_DEBUG: | |
| | | return LOG_DEBUG; | |
| | | case LL_INFO: | |
| | | return LOG_INFO; | |
| | | case LL_NOTICE: | |
| | | return LOG_NOTICE; | |
| | | case LL_WARNING: | |
| | | return LOG_WARNING; | |
| | | case LL_ERROR: | |
| | | return LOG_ERR; | |
| | | case LL_SEVERE: | |
| | | return LOG_CRIT; | |
| | | default: | |
| | | return LL_INFO; | |
| | | } | |
| | | } | |
| | | #endif | |
| | | | |
| class LabeledLevel { | | class LabeledLevel { | |
| public: | | public: | |
| | | | |
| LabeledLevel( int level ) : _level( level ) {} | | LabeledLevel( int level ) : _level( level ) {} | |
| LabeledLevel( const char* label, int level ) : _label( label ), _lev
el( level ) {} | | LabeledLevel( const char* label, int level ) : _label( label ), _lev
el( level ) {} | |
| LabeledLevel( const string& label, int level ) : _label( label ), _l
evel( level ) {} | | LabeledLevel( const string& label, int level ) : _label( label ), _l
evel( level ) {} | |
| | | | |
| LabeledLevel operator+( int i ) const { | | LabeledLevel operator+( int i ) const { | |
| return LabeledLevel( _label, _level + i ); | | return LabeledLevel( _label, _level + i ); | |
| } | | } | |
| | | | |
| skipping to change at line 197 | | skipping to change at line 226 | |
| return *this; | | return *this; | |
| } | | } | |
| virtual Nullstream& operator<< (ios_base& (*hex)(ios_base&)) { | | virtual Nullstream& operator<< (ios_base& (*hex)(ios_base&)) { | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| virtual void flush(Tee *t = 0) {} | | virtual void flush(Tee *t = 0) {} | |
| }; | | }; | |
| extern Nullstream nullstream; | | extern Nullstream nullstream; | |
| | | | |
|
| | | class mutex; | |
| | | | |
| class Logstream : public Nullstream { | | class Logstream : public Nullstream { | |
| static mongo::mutex mutex; | | static mongo::mutex mutex; | |
| static int doneSetup; | | static int doneSetup; | |
| stringstream ss; | | stringstream ss; | |
| int indent; | | int indent; | |
| LogLevel logLevel; | | LogLevel logLevel; | |
| static FILE* logfile; | | static FILE* logfile; | |
| static boost::scoped_ptr<ostream> stream; | | static boost::scoped_ptr<ostream> stream; | |
| static vector<Tee*> * globalTees; | | static vector<Tee*> * globalTees; | |
|
| | | static bool isSyslog; | |
| public: | | public: | |
|
| inline static void logLockless( const StringData& s ); | | static void logLockless( const StringData& s ); | |
| | | | |
|
| static void setLogFile(FILE* f) { | | static void setLogFile(FILE* f); | |
| scoped_lock lk(mutex); | | #ifndef _WIN32 | |
| logfile = f; | | static void useSyslog(const char * name) { | |
| } | | cout << "using syslog ident: " << name << endl; | |
| | | | |
| | | // openlog requires heap allocated non changing pointer | |
| | | // this should only be called once per pragram execution | |
| | | | |
|
| static int magicNumber() { | | char * newName = (char *) malloc( strlen(name) + 1 ); | |
| return 1717; | | strcpy( newName , name); | |
| | | openlog( newName , LOG_ODELAY , LOG_USER ); | |
| | | isSyslog = true; | |
| } | | } | |
|
| | | #endif | |
| | | | |
| | | static int magicNumber() { return 1717; } | |
| | | | |
| static int getLogDesc() { | | static int getLogDesc() { | |
| int fd = -1; | | int fd = -1; | |
| if (logfile != NULL) | | if (logfile != NULL) | |
| #if defined(_WIN32) | | #if defined(_WIN32) | |
| // the ISO C++ conformant name is _fileno | | // the ISO C++ conformant name is _fileno | |
| fd = _fileno( logfile ); | | fd = _fileno( logfile ); | |
| #else | | #else | |
| fd = fileno( logfile ); | | fd = fileno( logfile ); | |
| #endif | | #endif | |
| return fd; | | return fd; | |
| } | | } | |
| | | | |
|
| inline void flush(Tee *t = 0); | | void flush(Tee *t = 0); | |
| | | | |
| inline Nullstream& setLogLevel(LogLevel l) { | | inline Nullstream& setLogLevel(LogLevel l) { | |
| logLevel = l; | | logLevel = l; | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| /** note these are virtual */ | | /** note these are virtual */ | |
| Logstream& operator<<(const char *x) { ss << x; return *this; } | | Logstream& operator<<(const char *x) { ss << x; return *this; } | |
| Logstream& operator<<(const string& x) { ss << x; return *this; } | | Logstream& operator<<(const string& x) { ss << x; return *this; } | |
| Logstream& operator<<(const StringData& x) { ss << x.data(); return
*this; } | | Logstream& operator<<(const StringData& x) { ss << x.data(); return
*this; } | |
| | | | |
| skipping to change at line 290 | | skipping to change at line 330 | |
| if ( ! globalTees ) | | if ( ! globalTees ) | |
| globalTees = new vector<Tee*>(); | | globalTees = new vector<Tee*>(); | |
| globalTees->push_back( t ); | | globalTees->push_back( t ); | |
| } | | } | |
| | | | |
| void indentInc(){ indent++; } | | void indentInc(){ indent++; } | |
| void indentDec(){ indent--; } | | void indentDec(){ indent--; } | |
| int getIndent() const { return indent; } | | int getIndent() const { return indent; } | |
| | | | |
| private: | | private: | |
|
| static thread_specific_ptr<Logstream> tsp; | | static boost::thread_specific_ptr<Logstream> tsp; | |
| Logstream() { | | Logstream() { | |
| indent = 0; | | indent = 0; | |
| _init(); | | _init(); | |
| } | | } | |
| void _init() { | | void _init() { | |
| ss.str(""); | | ss.str(""); | |
| logLevel = LL_INFO; | | logLevel = LL_INFO; | |
| } | | } | |
| public: | | public: | |
|
| static Logstream& get() { | | static Logstream& get(); | |
| if ( StaticObserver::_destroyingStatics ) { | | | |
| cout << "Logstream::get called in uninitialized state" << e | | | |
| ndl; | | | |
| } | | | |
| Logstream *p = tsp.get(); | | | |
| if( p == 0 ) | | | |
| tsp.reset( p = new Logstream() ); | | | |
| return *p; | | | |
| } | | | |
| }; | | }; | |
| | | | |
| extern int logLevel; | | extern int logLevel; | |
| extern int tlogLevel; | | extern int tlogLevel; | |
| | | | |
| inline Nullstream& out( int level = 0 ) { | | inline Nullstream& out( int level = 0 ) { | |
| if ( level > logLevel ) | | if ( level > logLevel ) | |
| return nullstream; | | return nullstream; | |
| return Logstream::get(); | | return Logstream::get(); | |
| } | | } | |
| | | | |
| skipping to change at line 342 | | skipping to change at line 374 | |
| } | | } | |
| | | | |
| /** logging which we may not want during unit tests (dbtests) runs. | | /** logging which we may not want during unit tests (dbtests) runs. | |
| set tlogLevel to -1 to suppress tlog() output in a test program. */ | | set tlogLevel to -1 to suppress tlog() output in a test program. */ | |
| inline Nullstream& tlog( int level = 0 ) { | | inline Nullstream& tlog( int level = 0 ) { | |
| if ( level > tlogLevel || level > logLevel ) | | if ( level > tlogLevel || level > logLevel ) | |
| return nullstream; | | return nullstream; | |
| return Logstream::get().prolog(); | | return Logstream::get().prolog(); | |
| } | | } | |
| | | | |
|
| | | // log if debug build or if at a certain level | |
| | | inline Nullstream& dlog( int level ) { | |
| | | if ( level <= logLevel || DEBUG_BUILD ) | |
| | | return Logstream::get().prolog(); | |
| | | return nullstream; | |
| | | } | |
| | | | |
| inline Nullstream& log( int level ) { | | inline Nullstream& log( int level ) { | |
| if ( level > logLevel ) | | if ( level > logLevel ) | |
| return nullstream; | | return nullstream; | |
| return Logstream::get().prolog(); | | return Logstream::get().prolog(); | |
| } | | } | |
| | | | |
|
| #define MONGO_LOG(level) if ( MONGO_unlikely(logLevel >= (level)) ) log( le
vel ) | | #define MONGO_LOG(level) if ( MONGO_likely(logLevel < (level)) ) { } else l
og( level ) | |
| #define LOG MONGO_LOG | | #define LOG MONGO_LOG | |
| | | | |
| inline Nullstream& log( LogLevel l ) { | | inline Nullstream& log( LogLevel l ) { | |
| return Logstream::get().prolog().setLogLevel( l ); | | return Logstream::get().prolog().setLogLevel( l ); | |
| } | | } | |
| | | | |
| inline Nullstream& log( const LabeledLevel& ll ) { | | inline Nullstream& log( const LabeledLevel& ll ) { | |
| Nullstream& stream = log( ll.getLevel() ); | | Nullstream& stream = log( ll.getLevel() ); | |
| if( ll.getLabel() != "" ) | | if( ll.getLabel() != "" ) | |
| stream << "[" << ll.getLabel() << "] "; | | stream << "[" << ll.getLabel() << "] "; | |
| | | | |
| skipping to change at line 394 | | skipping to change at line 433 | |
| | | | |
| /** | | /** | |
| log to a file rather than stdout | | log to a file rather than stdout | |
| defined in assert_util.cpp | | defined in assert_util.cpp | |
| */ | | */ | |
| void initLogging( const string& logpath , bool append ); | | void initLogging( const string& logpath , bool append ); | |
| void rotateLogs( int signal = 0 ); | | void rotateLogs( int signal = 0 ); | |
| | | | |
| std::string toUtf8String(const std::wstring& wide); | | std::string toUtf8String(const std::wstring& wide); | |
| | | | |
|
| inline string errnoWithDescription(int x = errno) { | | | |
| stringstream s; | | | |
| s << "errno:" << x << ' '; | | | |
| | | | |
| #if defined(_WIN32) | | | |
| LPTSTR errorText = NULL; | | | |
| FormatMessage( | | | |
| FORMAT_MESSAGE_FROM_SYSTEM | | | |
| |FORMAT_MESSAGE_ALLOCATE_BUFFER | | | |
| |FORMAT_MESSAGE_IGNORE_INSERTS, | | | |
| NULL, | | | |
| x, 0, | | | |
| (LPTSTR) &errorText, // output | | | |
| 0, // minimum size for output buffer | | | |
| NULL); | | | |
| if( errorText ) { | | | |
| string x = toUtf8String(errorText); | | | |
| for( string::iterator i = x.begin(); i != x.end(); i++ ) { | | | |
| if( *i == '\n' || *i == '\r' ) | | | |
| break; | | | |
| s << *i; | | | |
| } | | | |
| LocalFree(errorText); | | | |
| } | | | |
| else | | | |
| s << strerror(x); | | | |
| /* | | | |
| DWORD n = FormatMessage( | | | |
| FORMAT_MESSAGE_ALLOCATE_BUFFER | | | | |
| FORMAT_MESSAGE_FROM_SYSTEM | | | | |
| FORMAT_MESSAGE_IGNORE_INSERTS, | | | |
| NULL, x, | | | |
| MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | | | |
| (LPTSTR) &lpMsgBuf, 0, NULL); | | | |
| */ | | | |
| #else | | | |
| s << strerror(x); | | | |
| #endif | | | |
| return s.str(); | | | |
| } | | | |
| | | | |
| /** output the error # and error message with prefix. | | /** output the error # and error message with prefix. | |
| handy for use as parm in uassert/massert. | | handy for use as parm in uassert/massert. | |
| */ | | */ | |
| string errnoWithPrefix( const char * prefix ); | | string errnoWithPrefix( const char * prefix ); | |
| | | | |
|
| void Logstream::logLockless( const StringData& s ) { | | | |
| if ( s.size() == 0 ) | | | |
| return; | | | |
| | | | |
| if ( doneSetup == 1717 ) { | | | |
| if (fwrite(s.data(), s.size(), 1, logfile)) { | | | |
| fflush(logfile); | | | |
| } | | | |
| else { | | | |
| int x = errno; | | | |
| cout << "Failed to write to logfile: " << errnoWithDescript | | | |
| ion(x) << endl; | | | |
| } | | | |
| } | | | |
| else { | | | |
| cout << s.data(); | | | |
| cout.flush(); | | | |
| } | | | |
| } | | | |
| | | | |
| void Logstream::flush(Tee *t) { | | | |
| // this ensures things are sane | | | |
| if ( doneSetup == 1717 ) { | | | |
| string msg = ss.str(); | | | |
| string threadName = getThreadName(); | | | |
| const char * type = logLevelToString(logLevel); | | | |
| | | | |
| int spaceNeeded = (int)(msg.size() + 64 + threadName.size()); | | | |
| int bufSize = 128; | | | |
| while ( bufSize < spaceNeeded ) | | | |
| bufSize += 128; | | | |
| | | | |
| BufBuilder b(bufSize); | | | |
| time_t_to_String( time(0) , b.grow(20) ); | | | |
| if (!threadName.empty()) { | | | |
| b.appendChar( '[' ); | | | |
| b.appendStr( threadName , false ); | | | |
| b.appendChar( ']' ); | | | |
| b.appendChar( ' ' ); | | | |
| } | | | |
| | | | |
| for ( int i=0; i<indent; i++ ) | | | |
| b.appendChar( '\t' ); | | | |
| | | | |
| if ( type[0] ) { | | | |
| b.appendStr( type , false ); | | | |
| b.appendStr( ": " , false ); | | | |
| } | | | |
| | | | |
| b.appendStr( msg ); | | | |
| | | | |
| string out( b.buf() , b.len() - 1); | | | |
| | | | |
| scoped_lock lk(mutex); | | | |
| | | | |
| if( t ) t->write(logLevel,out); | | | |
| if ( globalTees ) { | | | |
| for ( unsigned i=0; i<globalTees->size(); i++ ) | | | |
| (*globalTees)[i]->write(logLevel,out); | | | |
| } | | | |
| | | | |
| #ifndef _WIN32 | | | |
| //syslog( LOG_INFO , "%s" , cc ); | | | |
| #endif | | | |
| if(fwrite(out.data(), out.size(), 1, logfile)) { | | | |
| fflush(logfile); | | | |
| } | | | |
| else { | | | |
| int x = errno; | | | |
| cout << "Failed to write to logfile: " << errnoWithDescript | | | |
| ion(x) << ": " << out << endl; | | | |
| } | | | |
| | | | |
| #ifdef POSIX_FADV_DONTNEED | | | |
| // This only applies to pages that have already been flushed | | | |
| RARELY posix_fadvise(fileno(logfile), 0, 0, POSIX_FADV_DONTNEED | | | |
| ); | | | |
| #endif | | | |
| | | | |
| } | | | |
| _init(); | | | |
| } | | | |
| | | | |
| struct LogIndentLevel { | | struct LogIndentLevel { | |
| LogIndentLevel(){ | | LogIndentLevel(){ | |
| Logstream::get().indentInc(); | | Logstream::get().indentInc(); | |
| } | | } | |
| ~LogIndentLevel(){ | | ~LogIndentLevel(){ | |
| Logstream::get().indentDec(); | | Logstream::get().indentDec(); | |
| } | | } | |
| }; | | }; | |
| | | | |
| extern Tee* const warnings; // Things put here go in serverStatus | | extern Tee* const warnings; // Things put here go in serverStatus | |
| | | | |
|
| | | string errnoWithDescription(int errorcode = -1); | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 20 change blocks. |
| 145 lines changed or deleted | | 61 lines changed or added | |
|
| matcher.h | | matcher.h | |
| | | | |
| skipping to change at line 65 | | skipping to change at line 65 | |
| | | | |
| ElementMatcher() { | | ElementMatcher() { | |
| } | | } | |
| | | | |
| ElementMatcher( BSONElement e , int op, bool isNot ); | | ElementMatcher( BSONElement e , int op, bool isNot ); | |
| | | | |
| ElementMatcher( BSONElement e , int op , const BSONObj& array, bool
isNot ); | | ElementMatcher( BSONElement e , int op , const BSONObj& array, bool
isNot ); | |
| | | | |
| ~ElementMatcher() { } | | ~ElementMatcher() { } | |
| | | | |
|
| | | bool negativeCompareOp() const { return _compareOp == BSONObj::NE | | |
| | | | _compareOp == BSONObj::NIN; } | |
| | | int inverseOfNegativeCompareOp() const; | |
| | | bool negativeCompareOpContainsNull() const; | |
| | | | |
| BSONElement _toMatch; | | BSONElement _toMatch; | |
| int _compareOp; | | int _compareOp; | |
| bool _isNot; | | bool _isNot; | |
| shared_ptr< set<BSONElement,element_lt> > _myset; | | shared_ptr< set<BSONElement,element_lt> > _myset; | |
| shared_ptr< vector<RegexMatcher> > _myregex; | | shared_ptr< vector<RegexMatcher> > _myregex; | |
| | | | |
| // these are for specific operators | | // these are for specific operators | |
| int _mod; | | int _mod; | |
| int _modm; | | int _modm; | |
| BSONType _type; | | BSONType _type; | |
| | | | |
| skipping to change at line 124 | | skipping to change at line 128 | |
| { a : { $gt : 3 } } | | { a : { $gt : 3 } } | |
| Not equal: | | Not equal: | |
| { a : { $ne : 3 } } | | { a : { $ne : 3 } } | |
| | | | |
| TODO: we should rewrite the matcher to be more an AST style. | | TODO: we should rewrite the matcher to be more an AST style. | |
| */ | | */ | |
| class Matcher : boost::noncopyable { | | class Matcher : boost::noncopyable { | |
| int matchesDotted( | | int matchesDotted( | |
| const char *fieldName, | | const char *fieldName, | |
| const BSONElement& toMatch, const BSONObj& obj, | | const BSONElement& toMatch, const BSONObj& obj, | |
|
| int compareOp, const ElementMatcher& bm, bool isArr , MatchDeta
ils * details ); | | int compareOp, const ElementMatcher& bm, bool isArr , MatchDeta
ils * details ) const; | |
| | | | |
|
| int matchesNe( | | /** | |
| | | * Perform a NE or NIN match by returning the inverse of the opposi | |
| | | te matching operation. | |
| | | * Missing values are considered matches unless the match must not | |
| | | equal null. | |
| | | */ | |
| | | int inverseMatch( | |
| const char *fieldName, | | const char *fieldName, | |
| const BSONElement &toMatch, const BSONObj &obj, | | const BSONElement &toMatch, const BSONObj &obj, | |
|
| const ElementMatcher&bm, MatchDetails * details ); | | const ElementMatcher&bm, MatchDetails * details ) const; | |
| | | | |
| public: | | public: | |
| static int opDirection(int op) { | | static int opDirection(int op) { | |
| return op <= BSONObj::LTE ? -1 : 1; | | return op <= BSONObj::LTE ? -1 : 1; | |
| } | | } | |
| | | | |
| Matcher(const BSONObj &pattern, bool nested=false); | | Matcher(const BSONObj &pattern, bool nested=false); | |
| | | | |
| ~Matcher(); | | ~Matcher(); | |
| | | | |
|
| bool matches(const BSONObj& j, MatchDetails * details = 0 ); | | bool matches(const BSONObj& j, MatchDetails * details = 0 ) const; | |
| | | | |
| bool atomic() const { return _atomic; } | | bool atomic() const { return _atomic; } | |
| | | | |
| string toString() const { | | string toString() const { | |
| return _jsobj.toString(); | | return _jsobj.toString(); | |
| } | | } | |
| | | | |
| void addOrDedupConstraint( const shared_ptr< FieldRangeVector > &fr
v ) { | | void addOrDedupConstraint( const shared_ptr< FieldRangeVector > &fr
v ) { | |
| _orDedupConstraints.push_back( frv ); | | _orDedupConstraints.push_back( frv ); | |
| } | | } | |
| | | | |
| skipping to change at line 162 | | skipping to change at line 170 | |
| void popOrClause() { | | void popOrClause() { | |
| _orMatchers.pop_front(); | | _orMatchers.pop_front(); | |
| } | | } | |
| | | | |
| /** | | /** | |
| * @return true if this key matcher will return the same true/false | | * @return true if this key matcher will return the same true/false | |
| * value as the provided doc matcher. | | * value as the provided doc matcher. | |
| */ | | */ | |
| bool keyMatch( const Matcher &docMatcher ) const; | | bool keyMatch( const Matcher &docMatcher ) const; | |
| | | | |
|
| | | bool singleSimpleCriterion() const { | |
| | | return false; // TODO SERVER-958 | |
| | | // // TODO Really check, especially if all basics are ok. | |
| | | // // $all, etc | |
| | | // // _orConstraints? | |
| | | // return ( ( basics.size() + nRegex ) < 2 ) && !where && !_orMa | |
| | | tchers.size() && !_norMatchers.size(); | |
| | | } | |
| | | | |
| | | const BSONObj *getQuery() const { return &_jsobj; }; | |
| | | | |
| private: | | private: | |
| /** | | /** | |
| * Generate a matcher for the provided index key format using the | | * Generate a matcher for the provided index key format using the | |
| * provided full doc matcher. | | * provided full doc matcher. | |
| */ | | */ | |
| Matcher( const Matcher &docMatcher, const BSONObj &constrainIndexKe
y ); | | Matcher( const Matcher &docMatcher, const BSONObj &constrainIndexKe
y ); | |
| | | | |
| void addBasic(const BSONElement &e, int c, bool isNot) { | | void addBasic(const BSONElement &e, int c, bool isNot) { | |
| // TODO May want to selectively ignore these element types base
d on op type. | | // TODO May want to selectively ignore these element types base
d on op type. | |
| if ( e.type() == MinKey || e.type() == MaxKey ) | | if ( e.type() == MinKey || e.type() == MaxKey ) | |
| return; | | return; | |
| _basics.push_back( ElementMatcher( e , c, isNot ) ); | | _basics.push_back( ElementMatcher( e , c, isNot ) ); | |
| } | | } | |
| | | | |
| void addRegex(const char *fieldName, const char *regex, const char
*flags, bool isNot = false); | | void addRegex(const char *fieldName, const char *regex, const char
*flags, bool isNot = false); | |
| bool addOp( const BSONElement &e, const BSONElement &fe, bool isNot
, const char *& regex, const char *&flags ); | | bool addOp( const BSONElement &e, const BSONElement &fe, bool isNot
, const char *& regex, const char *&flags ); | |
| | | | |
|
| int valuesMatch(const BSONElement& l, const BSONElement& r, int op,
const ElementMatcher& bm); | | int valuesMatch(const BSONElement& l, const BSONElement& r, int op,
const ElementMatcher& bm) const; | |
| | | | |
| bool parseClause( const BSONElement &e ); | | bool parseClause( const BSONElement &e ); | |
| void parseExtractedClause( const BSONElement &e, list< shared_ptr<
Matcher > > &matchers ); | | void parseExtractedClause( const BSONElement &e, list< shared_ptr<
Matcher > > &matchers ); | |
| | | | |
|
| | | void parseWhere( const BSONElement &e ); | |
| void parseMatchExpressionElement( const BSONElement &e, bool nested
); | | void parseMatchExpressionElement( const BSONElement &e, bool nested
); | |
| | | | |
| Where *_where; // set if query uses $where | | Where *_where; // set if query uses $where | |
| BSONObj _jsobj; // the query pattern. e.g., { nam
e: "joe" } | | BSONObj _jsobj; // the query pattern. e.g., { nam
e: "joe" } | |
| BSONObj _constrainIndexKey; | | BSONObj _constrainIndexKey; | |
| vector<ElementMatcher> _basics; | | vector<ElementMatcher> _basics; | |
| bool _haveSize; | | bool _haveSize; | |
| bool _all; | | bool _all; | |
| bool _hasArray; | | bool _hasArray; | |
| bool _haveNeg; | | bool _haveNeg; | |
| | | | |
| /* $atomic - if true, a multi document operation (some removes, upd
ates) | | /* $atomic - if true, a multi document operation (some removes, upd
ates) | |
| should be done atomically. in that case, we do not yi
eld - | | should be done atomically. in that case, we do not yi
eld - | |
| i.e. we stay locked the whole time. | | i.e. we stay locked the whole time. | |
| http://www.mongodb.org/display/DOCS/Removing[ | | http://www.mongodb.org/display/DOCS/Removing[ | |
| */ | | */ | |
| bool _atomic; | | bool _atomic; | |
| | | | |
|
| RegexMatcher _regexs[4]; | | vector<RegexMatcher> _regexs; | |
| int _nRegex; | | | |
| | | | |
| // so we delete the mem when we're done: | | // so we delete the mem when we're done: | |
| vector< shared_ptr< BSONObjBuilder > > _builders; | | vector< shared_ptr< BSONObjBuilder > > _builders; | |
| list< shared_ptr< Matcher > > _andMatchers; | | list< shared_ptr< Matcher > > _andMatchers; | |
| list< shared_ptr< Matcher > > _orMatchers; | | list< shared_ptr< Matcher > > _orMatchers; | |
| list< shared_ptr< Matcher > > _norMatchers; | | list< shared_ptr< Matcher > > _norMatchers; | |
| vector< shared_ptr< FieldRangeVector > > _orDedupConstraints; | | vector< shared_ptr< FieldRangeVector > > _orDedupConstraints; | |
| | | | |
| friend class CoveredIndexMatcher; | | friend class CoveredIndexMatcher; | |
| }; | | }; | |
| | | | |
End of changes. 9 change blocks. |
| 7 lines changed or deleted | | 29 lines changed or added | |
|
| mmap.h | | mmap.h | |
| | | | |
| skipping to change at line 33 | | skipping to change at line 33 | |
| | | | |
| class MAdvise { | | class MAdvise { | |
| void *_p; | | void *_p; | |
| unsigned _len; | | unsigned _len; | |
| public: | | public: | |
| enum Advice { Sequential=1 }; | | enum Advice { Sequential=1 }; | |
| MAdvise(void *p, unsigned len, Advice a); | | MAdvise(void *p, unsigned len, Advice a); | |
| ~MAdvise(); // destructor resets the range to MADV_NORMAL | | ~MAdvise(); // destructor resets the range to MADV_NORMAL | |
| }; | | }; | |
| | | | |
|
| | | // lock order: lock dbMutex before this if you lock both | |
| | | class LockMongoFilesShared { | |
| | | friend class LockMongoFilesExclusive; | |
| | | static RWLockRecursiveNongreedy mmmutex; | |
| | | static unsigned era; | |
| | | RWLockRecursive::Shared lk; | |
| | | public: | |
| | | LockMongoFilesShared() : lk(mmmutex) { } | |
| | | | |
| | | /** era changes anytime memory maps come and go. thus you can use | |
| | | this as a cheap way to verify | |
| | | that things are still in the condition you expected. of course | |
| | | you must be shared locked | |
| | | otherwise someone could be in progress. if you have unlocked t | |
| | | his is a reasonable way to | |
| | | check your memory mapped pointer is still good. | |
| | | */ | |
| | | static unsigned getEra() { return era; } | |
| | | | |
| | | static void assertExclusivelyLocked() { mmmutex.assertExclusivelyLo | |
| | | cked(); } | |
| | | }; | |
| | | | |
| | | class LockMongoFilesExclusive { | |
| | | RWLockRecursive::Exclusive lk; | |
| | | public: | |
| | | LockMongoFilesExclusive() : lk(LockMongoFilesShared::mmmutex) { | |
| | | LockMongoFilesShared::era++; | |
| | | } | |
| | | }; | |
| | | | |
| /* the administrative-ish stuff here */ | | /* the administrative-ish stuff here */ | |
| class MongoFile : boost::noncopyable { | | class MongoFile : boost::noncopyable { | |
| public: | | public: | |
| /** Flushable has to fail nicely if the underlying object gets kill
ed */ | | /** Flushable has to fail nicely if the underlying object gets kill
ed */ | |
| class Flushable { | | class Flushable { | |
| public: | | public: | |
| virtual ~Flushable() {} | | virtual ~Flushable() {} | |
| virtual void flush() = 0; | | virtual void flush() = 0; | |
| }; | | }; | |
| | | | |
| | | | |
| skipping to change at line 76 | | skipping to change at line 103 | |
| static void closeAllFiles( stringstream &message ); | | static void closeAllFiles( stringstream &message ); | |
| | | | |
| #if defined(_DEBUG) | | #if defined(_DEBUG) | |
| static void markAllWritable(); | | static void markAllWritable(); | |
| static void unmarkAllWritable(); | | static void unmarkAllWritable(); | |
| #else | | #else | |
| static void markAllWritable() { } | | static void markAllWritable() { } | |
| static void unmarkAllWritable() { } | | static void unmarkAllWritable() { } | |
| #endif | | #endif | |
| | | | |
|
| static bool exists(boost::filesystem::path p) { return boost::files | | | |
| ystem::exists(p); } | | | |
| | | | |
| virtual bool isMongoMMF() { return false; } | | virtual bool isMongoMMF() { return false; } | |
| | | | |
| string filename() const { return _filename; } | | string filename() const { return _filename; } | |
| void setFilename(string fn); | | void setFilename(string fn); | |
| | | | |
| private: | | private: | |
| string _filename; | | string _filename; | |
| static int _flushAll( bool sync ); // returns n flushed | | static int _flushAll( bool sync ); // returns n flushed | |
| protected: | | protected: | |
| virtual void close() = 0; | | virtual void close() = 0; | |
| | | | |
| skipping to change at line 113 | | skipping to change at line 138 | |
| | | | |
| virtual unsigned long long length() const = 0; | | virtual unsigned long long length() const = 0; | |
| | | | |
| // only supporting on posix mmap | | // only supporting on posix mmap | |
| virtual void _lock() {} | | virtual void _lock() {} | |
| virtual void _unlock() {} | | virtual void _unlock() {} | |
| | | | |
| static set<MongoFile*> mmfiles; | | static set<MongoFile*> mmfiles; | |
| public: | | public: | |
| static map<string,MongoFile*> pathToFile; | | static map<string,MongoFile*> pathToFile; | |
|
| | | | |
| // lock order: lock dbMutex before this if you lock both | | | |
| static RWLockRecursive mmmutex; | | | |
| }; | | }; | |
| | | | |
| /** look up a MMF by filename. scoped mutex locking convention. | | /** look up a MMF by filename. scoped mutex locking convention. | |
| example: | | example: | |
| MMFFinderByName finder; | | MMFFinderByName finder; | |
| MongoMMF *a = finder.find("file_name_a"); | | MongoMMF *a = finder.find("file_name_a"); | |
| MongoMMF *b = finder.find("file_name_b"); | | MongoMMF *b = finder.find("file_name_b"); | |
| */ | | */ | |
| class MongoFileFinder : boost::noncopyable { | | class MongoFileFinder : boost::noncopyable { | |
| public: | | public: | |
|
| MongoFileFinder() : _lk(MongoFile::mmmutex) { } | | MongoFileFinder() { } | |
| | | | |
| /** @return The MongoFile object associated with the specified file
name. If no file is open | | /** @return The MongoFile object associated with the specified file
name. If no file is open | |
| with the specified name, returns null. | | with the specified name, returns null. | |
| */ | | */ | |
| MongoFile* findByPath(string path) { | | MongoFile* findByPath(string path) { | |
| map<string,MongoFile*>::iterator i = MongoFile::pathToFile.find
(path); | | map<string,MongoFile*>::iterator i = MongoFile::pathToFile.find
(path); | |
| return i == MongoFile::pathToFile.end() ? NULL : i->second; | | return i == MongoFile::pathToFile.end() ? NULL : i->second; | |
| } | | } | |
| | | | |
| private: | | private: | |
|
| RWLockRecursive::Shared _lk; | | LockMongoFilesShared _lk; | |
| }; | | }; | |
| | | | |
| struct MongoFileAllowWrites { | | struct MongoFileAllowWrites { | |
| MongoFileAllowWrites() { | | MongoFileAllowWrites() { | |
| MongoFile::markAllWritable(); | | MongoFile::markAllWritable(); | |
| } | | } | |
| ~MongoFileAllowWrites() { | | ~MongoFileAllowWrites() { | |
| MongoFile::unmarkAllWritable(); | | MongoFile::unmarkAllWritable(); | |
| } | | } | |
| }; | | }; | |
| | | | |
| skipping to change at line 161 | | skipping to change at line 183 | |
| virtual void* viewForFlushing() { | | virtual void* viewForFlushing() { | |
| if( views.size() == 0 ) | | if( views.size() == 0 ) | |
| return 0; | | return 0; | |
| assert( views.size() == 1 ); | | assert( views.size() == 1 ); | |
| return views[0]; | | return views[0]; | |
| } | | } | |
| public: | | public: | |
| MemoryMappedFile(); | | MemoryMappedFile(); | |
| | | | |
| virtual ~MemoryMappedFile() { | | virtual ~MemoryMappedFile() { | |
|
| RWLockRecursive::Exclusive lk(mmmutex); | | LockMongoFilesExclusive lk; | |
| close(); | | close(); | |
| } | | } | |
| | | | |
| virtual void close(); | | virtual void close(); | |
| | | | |
| // Throws exception if file doesn't exist. (dm may2010: not sure if
this is always true?) | | // Throws exception if file doesn't exist. (dm may2010: not sure if
this is always true?) | |
| void* map(const char *filename); | | void* map(const char *filename); | |
| | | | |
|
| /** @param options see MongoFile::Options */ | | /** @param options see MongoFile::Options | |
| | | */ | |
| void* mapWithOptions(const char *filename, int options); | | void* mapWithOptions(const char *filename, int options); | |
| | | | |
| /* Creates with length if DNE, otherwise uses existing file length, | | /* Creates with length if DNE, otherwise uses existing file length, | |
| passed length. | | passed length. | |
| @param options MongoFile::Options bits | | @param options MongoFile::Options bits | |
| */ | | */ | |
| void* map(const char *filename, unsigned long long &length, int opt
ions = 0 ); | | void* map(const char *filename, unsigned long long &length, int opt
ions = 0 ); | |
| | | | |
| /* Create. Must not exist. | | /* Create. Must not exist. | |
| @param zero fill file with zeros when true | | @param zero fill file with zeros when true | |
| | | | |
| skipping to change at line 236 | | skipping to change at line 259 | |
| | | | |
| /** close the current private view and open a new replacement */ | | /** close the current private view and open a new replacement */ | |
| void* remapPrivateView(void *oldPrivateAddr); | | void* remapPrivateView(void *oldPrivateAddr); | |
| }; | | }; | |
| | | | |
| typedef MemoryMappedFile MMF; | | typedef MemoryMappedFile MMF; | |
| | | | |
| /** p is called from within a mutex that MongoFile uses. so be careful
not to deadlock. */ | | /** p is called from within a mutex that MongoFile uses. so be careful
not to deadlock. */ | |
| template < class F > | | template < class F > | |
| inline void MongoFile::forEach( F p ) { | | inline void MongoFile::forEach( F p ) { | |
|
| RWLockRecursive::Shared lklk(mmmutex); | | LockMongoFilesShared lklk; | |
| for ( set<MongoFile*>::iterator i = mmfiles.begin(); i != mmfiles.e
nd(); i++ ) | | for ( set<MongoFile*>::iterator i = mmfiles.begin(); i != mmfiles.e
nd(); i++ ) | |
| p(*i); | | p(*i); | |
| } | | } | |
| | | | |
| #if defined(_WIN32) | | #if defined(_WIN32) | |
| class ourbitset { | | class ourbitset { | |
| volatile unsigned bits[MemoryMappedFile::NChunks]; // volatile as w
e are doing double check locking | | volatile unsigned bits[MemoryMappedFile::NChunks]; // volatile as w
e are doing double check locking | |
| public: | | public: | |
| ourbitset() { | | ourbitset() { | |
| memset((void*) bits, 0, sizeof(bits)); | | memset((void*) bits, 0, sizeof(bits)); | |
| | | | |
| skipping to change at line 277 | | skipping to change at line 300 | |
| inline void MemoryMappedFile::makeWritable(void *_p, unsigned len) { | | inline void MemoryMappedFile::makeWritable(void *_p, unsigned len) { | |
| size_t p = (size_t) _p; | | size_t p = (size_t) _p; | |
| unsigned a = p/ChunkSize; | | unsigned a = p/ChunkSize; | |
| unsigned b = (p+len)/ChunkSize; | | unsigned b = (p+len)/ChunkSize; | |
| for( unsigned i = a; i <= b; i++ ) { | | for( unsigned i = a; i <= b; i++ ) { | |
| if( !writable.get(i) ) { | | if( !writable.get(i) ) { | |
| makeChunkWritable(i); | | makeChunkWritable(i); | |
| } | | } | |
| } | | } | |
| } | | } | |
|
| extern void* getNextMemoryMappedFileLocation( unsigned long long mmfSiz
e ); | | | |
| #endif | | #endif | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 9 change blocks. |
| 12 lines changed or deleted | | 38 lines changed or added | |
|
| mongomutex.h | | mongomutex.h | |
| | | | |
| skipping to change at line 19 | | skipping to change at line 19 | |
| * | | * | |
| * This program is distributed in the hope that it will be useful, | | * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Affero General Public License for more details. | | * GNU Affero General Public License for more details. | |
| * | | * | |
| * You should have received a copy of the GNU Affero General Public Lice
nse | | * You should have received a copy of the GNU Affero General Public Lice
nse | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | | */ | |
| | | | |
|
| | | /* Mutex heirarchy (1 = "leaf") | |
| | | name level | |
| | | Logstream::mutex 1 | |
| | | ClientCursor::ccmutex 2 | |
| | | dblock 3 | |
| | | | |
| | | End func name with _inlock to indicate "caller must lock before callin | |
| | | g". | |
| | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
|
| // note: include concurrency.h, not this. | | #include "../util/concurrency/rwlock.h" | |
| | | #include "../util/mmap.h" | |
| | | #include "../util/time_support.h" | |
| | | #include "d_globals.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| /** the 'big lock' we use for most operations. a read/write lock. | | class Client; | |
| there is one of these, dbMutex. | | Client* curopWaitingForLock( int type ); | |
| | | void curopGotLock(Client*); | |
| | | | |
| | | /* mongomutex time stats */ | |
| | | class MutexInfo { | |
| | | unsigned long long enter, timeLocked; // microseconds | |
| | | int locked; | |
| | | unsigned long long start; // last as we touch this least often | |
| | | public: | |
| | | MutexInfo() : timeLocked(0) , locked(0) { | |
| | | start = curTimeMicros64(); | |
| | | } | |
| | | void entered() { | |
| | | if ( locked == 0 ) | |
| | | enter = curTimeMicros64(); | |
| | | locked++; | |
| | | assert( locked >= 1 ); | |
| | | } | |
| | | void leaving() { | |
| | | locked--; | |
| | | assert( locked >= 0 ); | |
| | | if ( locked == 0 ) | |
| | | timeLocked += curTimeMicros64() - enter; | |
| | | } | |
| | | int isLocked() const { return locked; } | |
| | | void getTimingInfo(unsigned long long &s, unsigned long long &tl) c | |
| | | onst { | |
| | | s = start; | |
| | | tl = timeLocked; | |
| | | } | |
| | | unsigned long long getTimeLocked() const { return timeLocked; } | |
| | | }; | |
| | | | |
| | | /** the 'big lock'. a read/write lock. | |
| | | there is one of these, d.dbMutex. | |
| | | | |
| generally if you need to declare a mutex use the right primitive cl
ass, not this. | | generally if you need to declare a mutex use the right primitive cl
ass, not this. | |
| | | | |
| use readlock and writelock classes for scoped locks on this rather
than direct | | use readlock and writelock classes for scoped locks on this rather
than direct | |
| manipulation. | | manipulation. | |
| */ | | */ | |
| class MongoMutex { | | class MongoMutex { | |
| public: | | public: | |
| MongoMutex(const char * name); | | MongoMutex(const char * name); | |
| | | | |
| /** @return | | /** @return | |
| * > 0 write lock | | * > 0 write lock | |
| * = 0 no lock | | * = 0 no lock | |
| * < 0 read lock | | * < 0 read lock | |
| */ | | */ | |
| int getState() const { return _state.get(); } | | int getState() const { return _state.get(); } | |
| | | | |
| bool atLeastReadLocked() const { return _state.get() != 0; } | | bool atLeastReadLocked() const { return _state.get() != 0; } | |
| void assertAtLeastReadLocked() const { assert(atLeastReadLocked());
} | | void assertAtLeastReadLocked() const { assert(atLeastReadLocked());
} | |
|
| bool isWriteLocked() const { return getState() > 0; } | | bool isWriteLocked/*by our thread*/() const { return getState() > 0
; } | |
| void assertWriteLocked() const { | | void assertWriteLocked() const { | |
| assert( getState() > 0 ); | | assert( getState() > 0 ); | |
| DEV assert( !_releasedEarly.get() ); | | DEV assert( !_releasedEarly.get() ); | |
| } | | } | |
| | | | |
| // write lock. use the writelock scoped lock class, not this direc
tly. | | // write lock. use the writelock scoped lock class, not this direc
tly. | |
| void lock() { | | void lock() { | |
| if ( _writeLockedAlready() ) | | if ( _writeLockedAlready() ) | |
| return; | | return; | |
| | | | |
| _state.set(1); | | _state.set(1); | |
| | | | |
|
| Client *c = curopWaitingForLock( 1 ); // stats | | curopWaitingForLock( 1 ); // stats | |
| _m.lock(); | | _m.lock(); | |
|
| curopGotLock(c); | | | |
| | | | |
| _minfo.entered(); | | | |
| | | | |
| MongoFile::markAllWritable(); // for _DEBUG validation -- a no
op for release build | | MongoFile::markAllWritable(); // for _DEBUG validation -- a no
op for release build | |
|
| | | | |
| _acquiredWriteLock(); | | _acquiredWriteLock(); | |
| } | | } | |
| | | | |
| // try write lock | | // try write lock | |
| bool lock_try( int millis ) { | | bool lock_try( int millis ) { | |
|
| if ( _writeLockedAlready() ) | | if ( _writeLockedAlready() ) // adjusts _state | |
| return true; | | return true; | |
| | | | |
|
| Client *c = curopWaitingForLock( 1 ); | | curopWaitingForLock( 1 ); | |
| bool got = _m.lock_try( millis ); | | bool got = _m.lock_try( millis ); | |
| | | | |
| if ( got ) { | | if ( got ) { | |
|
| curopGotLock(c); | | | |
| _minfo.entered(); | | | |
| _state.set(1); | | _state.set(1); | |
| MongoFile::markAllWritable(); // for _DEBUG validation -- a
no op for release build | | MongoFile::markAllWritable(); // for _DEBUG validation -- a
no op for release build | |
| _acquiredWriteLock(); | | _acquiredWriteLock(); | |
| } | | } | |
| | | | |
| return got; | | return got; | |
| } | | } | |
| | | | |
| // un write lock | | // un write lock | |
| void unlock() { | | void unlock() { | |
| | | | |
| skipping to change at line 106 | | skipping to change at line 144 | |
| if( s != 1 ) { | | if( s != 1 ) { | |
| if( _releasedEarly.get() ) { | | if( _releasedEarly.get() ) { | |
| _releasedEarly.set(false); | | _releasedEarly.set(false); | |
| return; | | return; | |
| } | | } | |
| massert( 12599, "internal error: attempt to unlock when was
n't in a write lock", false); | | massert( 12599, "internal error: attempt to unlock when was
n't in a write lock", false); | |
| } | | } | |
| _releasingWriteLock(); | | _releasingWriteLock(); | |
| MongoFile::unmarkAllWritable(); // _DEBUG validation | | MongoFile::unmarkAllWritable(); // _DEBUG validation | |
| _state.set(0); | | _state.set(0); | |
|
| _minfo.leaving(); | | | |
| _m.unlock(); | | _m.unlock(); | |
| } | | } | |
| | | | |
| /* unlock (write lock), and when unlock() is called later, | | /* unlock (write lock), and when unlock() is called later, | |
| be smart then and don't unlock it again. | | be smart then and don't unlock it again. | |
| */ | | */ | |
| void releaseEarly() { | | void releaseEarly() { | |
| assert( getState() == 1 ); // must not be recursive | | assert( getState() == 1 ); // must not be recursive | |
| assert( !_releasedEarly.get() ); | | assert( !_releasedEarly.get() ); | |
| _releasedEarly.set(true); | | _releasedEarly.set(true); | |
| | | | |
| skipping to change at line 163 | | skipping to change at line 200 | |
| */ | | */ | |
| bool got = _m.lock_shared_try( millis ); | | bool got = _m.lock_shared_try( millis ); | |
| if ( got ) | | if ( got ) | |
| _state.set(-1); | | _state.set(-1); | |
| return got; | | return got; | |
| } | | } | |
| | | | |
| void unlock_shared() { | | void unlock_shared() { | |
| int s = _state.get(); | | int s = _state.get(); | |
| if( s > 0 ) { | | if( s > 0 ) { | |
|
| assert( s > 1 ); /* we must have done a lock write first to
have s > 1 */ | | wassert( s > 1 ); /* we must have done a lock write first t
o have s > 1 */ | |
| _state.set(s-1); | | _state.set(s-1); | |
| return; | | return; | |
| } | | } | |
| if( s < -1 ) { | | if( s < -1 ) { | |
| _state.set(s+1); | | _state.set(s+1); | |
| return; | | return; | |
| } | | } | |
|
| assert( s == -1 ); | | wassert( s == -1 ); | |
| _state.set(0); | | _state.set(0); | |
| _m.unlock_shared(); | | _m.unlock_shared(); | |
| } | | } | |
| | | | |
| MutexInfo& info() { return _minfo; } | | MutexInfo& info() { return _minfo; } | |
| | | | |
| private: | | private: | |
|
| | | void lockedExclusively(); | |
| | | void unlockingExclusively(); | |
| void _acquiredWriteLock(); | | void _acquiredWriteLock(); | |
| void _releasingWriteLock(); | | void _releasingWriteLock(); | |
| | | | |
| /* @return true if was already write locked. increments recursive
lock count. */ | | /* @return true if was already write locked. increments recursive
lock count. */ | |
| bool _writeLockedAlready(); | | bool _writeLockedAlready(); | |
| | | | |
| RWLock _m; | | RWLock _m; | |
| | | | |
| /* > 0 write lock with recurse count | | /* > 0 write lock with recurse count | |
| < 0 read lock | | < 0 read lock | |
| | | | |
| skipping to change at line 210 | | skipping to change at line 249 | |
| we use a separate TLS value for releasedEarly - that is ok as | | we use a separate TLS value for releasedEarly - that is ok as | |
| our normal/common code path, we never even touch it */ | | our normal/common code path, we never even touch it */ | |
| ThreadLocalValue<bool> _releasedEarly; | | ThreadLocalValue<bool> _releasedEarly; | |
| | | | |
| /* this is for fsyncAndLock command. otherwise write lock's greedi
ness will | | /* this is for fsyncAndLock command. otherwise write lock's greedi
ness will | |
| make us block on any attempted write lock the the fsync's lock. | | make us block on any attempted write lock the the fsync's lock. | |
| */ | | */ | |
| //volatile bool _blockWrites; | | //volatile bool _blockWrites; | |
| }; | | }; | |
| | | | |
|
| extern MongoMutex &dbMutex; | | | |
| | | | |
| namespace dur { | | namespace dur { | |
| void REMAPPRIVATEVIEW(); | | void REMAPPRIVATEVIEW(); | |
| void releasingWriteLock(); // because it's hard to include dur.h he
re | | void releasingWriteLock(); // because it's hard to include dur.h he
re | |
| } | | } | |
| | | | |
| inline void MongoMutex::_releasingWriteLock() { | | inline void MongoMutex::_releasingWriteLock() { | |
| dur::releasingWriteLock(); | | dur::releasingWriteLock(); | |
|
| | | unlockingExclusively(); | |
| } | | } | |
| | | | |
| inline void MongoMutex::_acquiredWriteLock() { | | inline void MongoMutex::_acquiredWriteLock() { | |
|
| | | lockedExclusively(); | |
| if( _remapPrivateViewRequested ) { | | if( _remapPrivateViewRequested ) { | |
| dur::REMAPPRIVATEVIEW(); | | dur::REMAPPRIVATEVIEW(); | |
| dassert( !_remapPrivateViewRequested ); | | dassert( !_remapPrivateViewRequested ); | |
| } | | } | |
| } | | } | |
| | | | |
|
| | | string sayClientState(); | |
| | | | |
| /* @return true if was already write locked. increments recursive lock
count. */ | | /* @return true if was already write locked. increments recursive lock
count. */ | |
| inline bool MongoMutex::_writeLockedAlready() { | | inline bool MongoMutex::_writeLockedAlready() { | |
| int s = _state.get(); | | int s = _state.get(); | |
| if( s > 0 ) { | | if( s > 0 ) { | |
| _state.set(s+1); | | _state.set(s+1); | |
| return true; | | return true; | |
| } | | } | |
| massert( 10293 , string("internal error: locks are not upgradeable:
") + sayClientState() , s == 0 ); | | massert( 10293 , string("internal error: locks are not upgradeable:
") + sayClientState() , s == 0 ); | |
| return false; | | return false; | |
| } | | } | |
| | | | |
|
| | | struct writelock { | |
| | | writelock() { d.dbMutex.lock(); } | |
| | | writelock(const string& ns) { d.dbMutex.lock(); } | |
| | | ~writelock() { | |
| | | DESTRUCTOR_GUARD( | |
| | | d.dbMutex.unlock(); | |
| | | ); | |
| | | } | |
| | | }; | |
| | | | |
| | | struct readlock { | |
| | | readlock(const string& ns) { | |
| | | d.dbMutex.lock_shared(); | |
| | | } | |
| | | readlock() { d.dbMutex.lock_shared(); } | |
| | | ~readlock() { | |
| | | DESTRUCTOR_GUARD( | |
| | | d.dbMutex.unlock_shared(); | |
| | | ); | |
| | | } | |
| | | }; | |
| | | struct readlocktry { | |
| | | readlocktry( const string&ns , int tryms ) { | |
| | | _got = d.dbMutex.lock_shared_try( tryms ); | |
| | | } | |
| | | ~readlocktry() { | |
| | | if ( _got ) { | |
| | | d.dbMutex.unlock_shared(); | |
| | | } | |
| | | } | |
| | | bool got() const { return _got; } | |
| | | private: | |
| | | bool _got; | |
| | | }; | |
| | | | |
| | | struct writelocktry { | |
| | | writelocktry( const string&ns , int tryms ) { | |
| | | _got = d.dbMutex.lock_try( tryms ); | |
| | | } | |
| | | ~writelocktry() { | |
| | | if ( _got ) { | |
| | | d.dbMutex.unlock(); | |
| | | } | |
| | | } | |
| | | bool got() const { return _got; } | |
| | | private: | |
| | | bool _got; | |
| | | }; | |
| | | | |
| | | struct readlocktryassert : public readlocktry { | |
| | | readlocktryassert(const string& ns, int tryms) : | |
| | | readlocktry(ns,tryms) { | |
| | | uassert(13142, "timeout getting readlock", got()); | |
| | | } | |
| | | }; | |
| | | | |
| | | /** assure we have at least a read lock - they key with this being | |
| | | if you have a write lock, that's ok too. | |
| | | */ | |
| | | struct atleastreadlock { | |
| | | atleastreadlock( const string& ns = "" ) { | |
| | | _prev = d.dbMutex.getState(); | |
| | | if ( _prev == 0 ) | |
| | | d.dbMutex.lock_shared(); | |
| | | } | |
| | | ~atleastreadlock() { | |
| | | if ( _prev == 0 ) | |
| | | d.dbMutex.unlock_shared(); | |
| | | } | |
| | | private: | |
| | | int _prev; | |
| | | }; | |
| | | | |
| | | /* parameterized choice of read or write locking | |
| | | use readlock and writelock instead of this when statically known whi | |
| | | ch you want | |
| | | */ | |
| | | class mongolock { | |
| | | bool _writelock; | |
| | | public: | |
| | | mongolock(bool write) : _writelock(write) { | |
| | | if( _writelock ) { | |
| | | d.dbMutex.lock(); | |
| | | } | |
| | | else | |
| | | d.dbMutex.lock_shared(); | |
| | | } | |
| | | ~mongolock() { | |
| | | DESTRUCTOR_GUARD( | |
| | | if( _writelock ) { | |
| | | d.dbMutex.unlock(); | |
| | | } | |
| | | else { | |
| | | d.dbMutex.unlock_shared(); | |
| | | } | |
| | | ); | |
| | | } | |
| | | /* this unlocks, does NOT upgrade. that works for our current usage | |
| | | */ | |
| | | //void releaseAndWriteLock(); | |
| | | }; | |
| | | | |
| | | /* deprecated - use writelock and readlock instead */ | |
| | | struct dblock : public writelock { | |
| | | dblock() : writelock("") { } | |
| | | }; | |
| | | | |
| | | // eliminate this - we should just type "d.dbMutex.assertWriteLocked(); | |
| | | " instead | |
| | | inline void assertInWriteLock() { d.dbMutex.assertWriteLocked(); } | |
| | | | |
| } | | } | |
| | | | |
End of changes. 19 change blocks. |
| 19 lines changed or deleted | | 173 lines changed or added | |
|
| mutex.h | | mutex.h | |
| | | | |
| skipping to change at line 20 | | skipping to change at line 20 | |
| * | | * | |
| * Unless required by applicable law or agreed to in writing, software | | * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | | * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | |
| * See the License for the specific language governing permissions and | | * See the License for the specific language governing permissions and | |
| * limitations under the License. | | * limitations under the License. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
|
| #include <map> | | | |
| #include <set> | | | |
| #include "../heapcheck.h" | | #include "../heapcheck.h" | |
|
| | | #include "threadlocal.h" | |
| | | #if defined(_DEBUG) | |
| | | #include "mutexdebugger.h" | |
| | | #endif | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| void printStackTrace( ostream &o ); | | | |
| | | | |
| class mutex; | | | |
| | | | |
| inline boost::xtime incxtimemillis( long long s ) { | | inline boost::xtime incxtimemillis( long long s ) { | |
| boost::xtime xt; | | boost::xtime xt; | |
| boost::xtime_get(&xt, boost::TIME_UTC); | | boost::xtime_get(&xt, boost::TIME_UTC); | |
| xt.sec += (int)( s / 1000 ); | | xt.sec += (int)( s / 1000 ); | |
| xt.nsec += (int)(( s % 1000 ) * 1000000); | | xt.nsec += (int)(( s % 1000 ) * 1000000); | |
| if ( xt.nsec >= 1000000000 ) { | | if ( xt.nsec >= 1000000000 ) { | |
| xt.nsec -= 1000000000; | | xt.nsec -= 1000000000; | |
| xt.sec++; | | xt.sec++; | |
| } | | } | |
| return xt; | | return xt; | |
| } | | } | |
| | | | |
|
| /** only used on _DEBUG builds. | | | |
| MutexDebugger checks that we always acquire locks for multiple mute | | | |
| xes in a consistant (acyclic) order. | | | |
| If we were inconsistent we could deadlock. | | | |
| */ | | | |
| class MutexDebugger { | | | |
| typedef const char * mid; // mid = mutex ID | | | |
| typedef map<mid,int> Preceeding; | | | |
| map< mid, int > maxNest; | | | |
| boost::thread_specific_ptr< Preceeding > us; | | | |
| map< mid, set<mid> > followers; | | | |
| boost::mutex &x; | | | |
| unsigned magic; | | | |
| void aBreakPoint() { } // for debugging | | | |
| public: | | | |
| // set these to create an assert that | | | |
| // b must never be locked before a | | | |
| // so | | | |
| // a.lock(); b.lock(); is fine | | | |
| // b.lock(); alone is fine too | | | |
| // only checked on _DEBUG builds. | | | |
| string a,b; | | | |
| | | | |
| /** outputs some diagnostic info on mutexes (on _DEBUG builds) */ | | | |
| void programEnding(); | | | |
| | | | |
| MutexDebugger(); | | | |
| | | | |
| void entering(mid m) { | | | |
| if( this == 0 ) return; | | | |
| assert( magic == 0x12345678 ); | | | |
| | | | |
| Preceeding *_preceeding = us.get(); | | | |
| if( _preceeding == 0 ) | | | |
| us.reset( _preceeding = new Preceeding() ); | | | |
| Preceeding &preceeding = *_preceeding; | | | |
| | | | |
| if( a == m ) { | | | |
| aBreakPoint(); | | | |
| if( preceeding[b.c_str()] ) { | | | |
| cout << "****** MutexDebugger error! warning " << b << | | | |
| " was locked before " << a << endl; | | | |
| assert(false); | | | |
| } | | | |
| } | | | |
| | | | |
| preceeding[m]++; | | | |
| if( preceeding[m] > 1 ) { | | | |
| // recursive re-locking. | | | |
| if( preceeding[m] > maxNest[m] ) | | | |
| maxNest[m] = preceeding[m]; | | | |
| return; | | | |
| } | | | |
| | | | |
| bool failed = false; | | | |
| string err; | | | |
| { | | | |
| boost::mutex::scoped_lock lk(x); | | | |
| followers[m]; | | | |
| for( Preceeding::iterator i = preceeding.begin(); i != prec | | | |
| eeding.end(); i++ ) { | | | |
| if( m != i->first && i->second > 0 ) { | | | |
| followers[i->first].insert(m); | | | |
| if( followers[m].count(i->first) != 0 ) { | | | |
| failed = true; | | | |
| stringstream ss; | | | |
| mid bad = i->first; | | | |
| ss << "mutex problem" << | | | |
| "\n when locking " << m << | | | |
| "\n " << bad << " was already locked and sh | | | |
| ould not be." | | | |
| "\n set a and b above to debug.\n"; | | | |
| stringstream q; | | | |
| for( Preceeding::iterator i = preceeding.begin( | | | |
| ); i != preceeding.end(); i++ ) { | | | |
| if( i->first != m && i->first != bad && i-> | | | |
| second > 0 ) | | | |
| q << " " << i->first << '\n'; | | | |
| } | | | |
| string also = q.str(); | | | |
| if( !also.empty() ) | | | |
| ss << "also locked before " << m << " in th | | | |
| is thread (no particular order):\n" << also; | | | |
| err = ss.str(); | | | |
| break; | | | |
| } | | | |
| } | | | |
| } | | | |
| } | | | |
| if( failed ) { | | | |
| cout << err << endl; | | | |
| assert( 0 ); | | | |
| } | | | |
| } | | | |
| void leaving(mid m) { | | | |
| if( this == 0 ) return; // still in startup pre-main() | | | |
| Preceeding& preceeding = *us.get(); | | | |
| preceeding[m]--; | | | |
| if( preceeding[m] < 0 ) { | | | |
| cout << "ERROR: lock count for " << m << " is " << preceedi | | | |
| ng[m] << endl; | | | |
| assert( preceeding[m] >= 0 ); | | | |
| } | | | |
| } | | | |
| }; | | | |
| extern MutexDebugger &mutexDebugger; | | | |
| | | | |
| // If you create a local static instance of this class, that instance w
ill be destroyed | | // If you create a local static instance of this class, that instance w
ill be destroyed | |
| // before all global static objects are destroyed, so _destroyingStatic
s will be set | | // before all global static objects are destroyed, so _destroyingStatic
s will be set | |
| // to true before the global static variables are destroyed. | | // to true before the global static variables are destroyed. | |
| class StaticObserver : boost::noncopyable { | | class StaticObserver : boost::noncopyable { | |
| public: | | public: | |
| static bool _destroyingStatics; | | static bool _destroyingStatics; | |
| ~StaticObserver() { _destroyingStatics = true; } | | ~StaticObserver() { _destroyingStatics = true; } | |
| }; | | }; | |
| | | | |
| /** On pthread systems, it is an error to destroy a mutex while held (b
oost mutex | | /** On pthread systems, it is an error to destroy a mutex while held (b
oost mutex | |
| * may use pthread). Static global mutexes may be held upon shutdow
n in our | | * may use pthread). Static global mutexes may be held upon shutdow
n in our | |
| * implementation, and this way we avoid destroying them. | | * implementation, and this way we avoid destroying them. | |
| * NOT recursive. | | * NOT recursive. | |
| */ | | */ | |
| class mutex : boost::noncopyable { | | class mutex : boost::noncopyable { | |
| public: | | public: | |
|
| #if defined(_DEBUG) | | | |
| const char * const _name; | | const char * const _name; | |
| mutex(const char *name) : _name(name) | | mutex(const char *name) : _name(name) | |
|
| #else | | | |
| mutex(const char *) | | | |
| #endif | | | |
| { | | { | |
| _m = new boost::timed_mutex(); | | _m = new boost::timed_mutex(); | |
| IGNORE_OBJECT( _m ); // Turn-off heap checking on _m | | IGNORE_OBJECT( _m ); // Turn-off heap checking on _m | |
| } | | } | |
| ~mutex() { | | ~mutex() { | |
| if( !StaticObserver::_destroyingStatics ) { | | if( !StaticObserver::_destroyingStatics ) { | |
| UNIGNORE_OBJECT( _m ); | | UNIGNORE_OBJECT( _m ); | |
| delete _m; | | delete _m; | |
| } | | } | |
| } | | } | |
| | | | |
| skipping to change at line 194 | | skipping to change at line 89 | |
| private: | | private: | |
| boost::timed_mutex::scoped_timed_lock _l; | | boost::timed_mutex::scoped_timed_lock _l; | |
| public: | | public: | |
| const bool ok; | | const bool ok; | |
| }; | | }; | |
| | | | |
| class scoped_lock : boost::noncopyable { | | class scoped_lock : boost::noncopyable { | |
| public: | | public: | |
| #if defined(_DEBUG) | | #if defined(_DEBUG) | |
| struct PostStaticCheck { | | struct PostStaticCheck { | |
|
| PostStaticCheck() { | | PostStaticCheck(); | |
| if ( StaticObserver::_destroyingStatics ) { | | } _check; | |
| cout << "trying to lock a mongo::mutex during stati | | | |
| c shutdown" << endl; | | | |
| printStackTrace( cout ); | | | |
| } | | | |
| } | | | |
| }; | | | |
| | | | |
| PostStaticCheck _check; | | | |
| mongo::mutex * const _mut; | | mongo::mutex * const _mut; | |
| #endif | | #endif | |
| scoped_lock( mongo::mutex &m ) : | | scoped_lock( mongo::mutex &m ) : | |
| #if defined(_DEBUG) | | #if defined(_DEBUG) | |
| _mut(&m), | | _mut(&m), | |
| #endif | | #endif | |
| _l( m.boost() ) { | | _l( m.boost() ) { | |
| #if defined(_DEBUG) | | #if defined(_DEBUG) | |
| mutexDebugger.entering(_mut->_name); | | mutexDebugger.entering(_mut->_name); | |
| #endif | | #endif | |
| | | | |
| skipping to change at line 243 | | skipping to change at line 131 | |
| implemented using OS-specific facilities in all environments (if
desired). | | implemented using OS-specific facilities in all environments (if
desired). | |
| On Windows, the implementation below is faster than boost mutex. | | On Windows, the implementation below is faster than boost mutex. | |
| */ | | */ | |
| #if defined(_WIN32) | | #if defined(_WIN32) | |
| class SimpleMutex : boost::noncopyable { | | class SimpleMutex : boost::noncopyable { | |
| CRITICAL_SECTION _cs; | | CRITICAL_SECTION _cs; | |
| public: | | public: | |
| SimpleMutex(const char *name) { InitializeCriticalSection(&_cs); } | | SimpleMutex(const char *name) { InitializeCriticalSection(&_cs); } | |
| ~SimpleMutex() { DeleteCriticalSection(&_cs); } | | ~SimpleMutex() { DeleteCriticalSection(&_cs); } | |
| | | | |
|
| void lock() { EnterCriticalSection(&_cs); } | | #if defined(_DEBUG) | |
| void unlock() { LeaveCriticalSection(&_cs); } | | ThreadLocalValue<int> _nlocksByMe; | |
| | | void lock() { | |
| | | assert( _nlocksByMe.get() == 0 ); // indicates you rae trying t | |
| | | o lock recursively | |
| | | _nlocksByMe.set(1); | |
| | | EnterCriticalSection(&_cs); | |
| | | } | |
| | | void dassertLocked() const { | |
| | | assert( _nlocksByMe.get() == 1 ); | |
| | | } | |
| | | void unlock() { | |
| | | dassertLocked(); | |
| | | _nlocksByMe.set(0); | |
| | | LeaveCriticalSection(&_cs); | |
| | | } | |
| | | #else | |
| | | void dassertLocked() const { } | |
| | | void lock() { | |
| | | EnterCriticalSection(&_cs); | |
| | | } | |
| | | void unlock() { | |
| | | LeaveCriticalSection(&_cs); | |
| | | } | |
| | | #endif | |
| | | | |
| class scoped_lock : boost::noncopyable { | | class scoped_lock : boost::noncopyable { | |
| SimpleMutex& _m; | | SimpleMutex& _m; | |
| public: | | public: | |
| scoped_lock( SimpleMutex &m ) : _m(m) { _m.lock(); } | | scoped_lock( SimpleMutex &m ) : _m(m) { _m.lock(); } | |
| ~scoped_lock() { _m.unlock(); } | | ~scoped_lock() { _m.unlock(); } | |
|
| | | # if defined(_DEBUG) | |
| | | const SimpleMutex& m() const { return _m; } | |
| | | # endif | |
| }; | | }; | |
| }; | | }; | |
| #else | | #else | |
| class SimpleMutex : boost::noncopyable { | | class SimpleMutex : boost::noncopyable { | |
| public: | | public: | |
|
| | | void dassertLocked() const { } | |
| SimpleMutex(const char* name) { assert( pthread_mutex_init(&_lock,0
) == 0 ); } | | SimpleMutex(const char* name) { assert( pthread_mutex_init(&_lock,0
) == 0 ); } | |
| ~SimpleMutex(){ | | ~SimpleMutex(){ | |
| if ( ! StaticObserver::_destroyingStatics ) { | | if ( ! StaticObserver::_destroyingStatics ) { | |
| assert( pthread_mutex_destroy(&_lock) == 0 ); | | assert( pthread_mutex_destroy(&_lock) == 0 ); | |
| } | | } | |
| } | | } | |
| | | | |
| void lock() { assert( pthread_mutex_lock(&_lock) == 0 ); } | | void lock() { assert( pthread_mutex_lock(&_lock) == 0 ); } | |
| void unlock() { assert( pthread_mutex_unlock(&_lock) == 0 ); } | | void unlock() { assert( pthread_mutex_unlock(&_lock) == 0 ); } | |
|
| | | public: | |
| class scoped_lock : boost::noncopyable { | | class scoped_lock : boost::noncopyable { | |
| SimpleMutex& _m; | | SimpleMutex& _m; | |
| public: | | public: | |
| scoped_lock( SimpleMutex &m ) : _m(m) { _m.lock(); } | | scoped_lock( SimpleMutex &m ) : _m(m) { _m.lock(); } | |
| ~scoped_lock() { _m.unlock(); } | | ~scoped_lock() { _m.unlock(); } | |
|
| | | const SimpleMutex& m() const { return _m; } | |
| }; | | }; | |
| | | | |
| private: | | private: | |
| pthread_mutex_t _lock; | | pthread_mutex_t _lock; | |
| }; | | }; | |
| | | | |
| #endif | | #endif | |
| | | | |
|
| | | /** This can be used instead of boost recursive mutex. The advantage is | |
| | | the _DEBUG checks | |
| | | * and ability to assertLocked(). This has not yet been tested for spe | |
| | | ed vs. the boost one. | |
| | | */ | |
| | | class RecursiveMutex : boost::noncopyable { | |
| | | public: | |
| | | RecursiveMutex(const char* name) : m(name) { } | |
| | | bool isLocked() const { return n.get() > 0; } | |
| | | class scoped_lock : boost::noncopyable { | |
| | | RecursiveMutex& rm; | |
| | | int& nLocksByMe; | |
| | | public: | |
| | | scoped_lock( RecursiveMutex &m ) : rm(m), nLocksByMe(rm.n.getRe | |
| | | f()) { | |
| | | if( nLocksByMe++ == 0 ) | |
| | | rm.m.lock(); | |
| | | } | |
| | | ~scoped_lock() { | |
| | | assert( nLocksByMe > 0 ); | |
| | | if( --nLocksByMe == 0 ) { | |
| | | rm.m.unlock(); | |
| | | } | |
| | | } | |
| | | }; | |
| | | private: | |
| | | SimpleMutex m; | |
| | | ThreadLocalValue<int> n; | |
| | | }; | |
| | | | |
| } | | } | |
| | | | |
End of changes. 13 change blocks. |
| 130 lines changed or deleted | | 67 lines changed or added | |
|
| namespace.h | | namespace.h | |
| | | | |
| skipping to change at line 22 | | skipping to change at line 22 | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Affero General Public License for more details. | | * GNU Affero General Public License for more details. | |
| * | | * | |
| * You should have received a copy of the GNU Affero General Public Licen
se | | * You should have received a copy of the GNU Affero General Public Licen
se | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "../pch.h" | | #include "../pch.h" | |
|
| | | #include "namespacestring.h" | |
| #include "jsobj.h" | | #include "jsobj.h" | |
| #include "querypattern.h" | | #include "querypattern.h" | |
| #include "diskloc.h" | | #include "diskloc.h" | |
| #include "../util/hashtab.h" | | #include "../util/hashtab.h" | |
| #include "mongommf.h" | | #include "mongommf.h" | |
|
| | | #include "d_concurrency.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| /* in the mongo source code, "client" means "database". */ | | class Database; | |
| | | | |
| const int MaxDatabaseNameLen = 256; // max str len for the db name, inc | | | |
| luding null char | | | |
| | | | |
| /* e.g. | | | |
| NamespaceString ns("acme.orders"); | | | |
| cout << ns.coll; // "orders" | | | |
| */ | | | |
| class NamespaceString { | | | |
| public: | | | |
| string db; | | | |
| string coll; // note collection names can have periods in them for | | | |
| organizing purposes (e.g. "system.indexes") | | | |
| | | | |
| NamespaceString( const char * ns ) { init(ns); } | | | |
| NamespaceString( const string& ns ) { init(ns.c_str()); } | | | |
| string ns() const { return db + '.' + coll; } | | | |
| bool isSystem() const { return strncmp(coll.c_str(), "system.", 7) | | | |
| == 0; } | | | |
| | | | |
| /** | | | |
| * @return true if ns is 'normal'. $ used for collections holding | | | |
| index data, which do not contain BSON objects in their records. | | | |
| * special case for the local.oplog.$main ns -- naming it as such w | | | |
| as a mistake. | | | |
| */ | | | |
| static bool normal(const char* ns) { | | | |
| const char *p = strchr(ns, '$'); | | | |
| if( p == 0 ) | | | |
| return true; | | | |
| return strcmp( ns, "local.oplog.$main" ) == 0; | | | |
| } | | | |
| | | | |
| static bool special(const char *ns) { | | | |
| return !normal(ns) || strstr(ns, ".system."); | | | |
| } | | | |
| private: | | | |
| void init(const char *ns) { | | | |
| const char *p = strchr(ns, '.'); | | | |
| if( p == 0 ) return; | | | |
| db = string(ns, p - ns); | | | |
| coll = p + 1; | | | |
| } | | | |
| }; | | | |
| | | | |
| #pragma pack(1) | | #pragma pack(1) | |
|
| /* This helper class is used to make the HashMap below in NamespaceDeta
ils e.g. see line: | | /* This helper class is used to make the HashMap below in NamespaceInde
x e.g. see line: | |
| HashTable<Namespace,NamespaceDetails> *ht; | | HashTable<Namespace,NamespaceDetails> *ht; | |
| */ | | */ | |
| class Namespace { | | class Namespace { | |
| public: | | public: | |
| explicit Namespace(const char *ns) { *this = ns; } | | explicit Namespace(const char *ns) { *this = ns; } | |
| Namespace& operator=(const char *ns); | | Namespace& operator=(const char *ns); | |
| | | | |
| bool hasDollarSign() const { return strchr( buf , '$' ) > 0; } | | bool hasDollarSign() const { return strchr( buf , '$' ) > 0; } | |
| void kill() { buf[0] = 0x7f; } | | void kill() { buf[0] = 0x7f; } | |
| bool operator==(const char *r) const { return strcmp(buf, r) == 0;
} | | bool operator==(const char *r) const { return strcmp(buf, r) == 0;
} | |
| | | | |
| skipping to change at line 321 | | skipping to change at line 284 | |
| */ | | */ | |
| IndexDetails& addIndex(const char *thisns, bool resetTransient=true
); | | IndexDetails& addIndex(const char *thisns, bool resetTransient=true
); | |
| | | | |
| void aboutToDeleteAnIndex() { | | void aboutToDeleteAnIndex() { | |
| *getDur().writing(&flags) = flags & ~Flag_HaveIdIndex; | | *getDur().writing(&flags) = flags & ~Flag_HaveIdIndex; | |
| } | | } | |
| | | | |
| /* returns index of the first index in which the field is present.
-1 if not present. */ | | /* returns index of the first index in which the field is present.
-1 if not present. */ | |
| int fieldIsIndexed(const char *fieldName); | | int fieldIsIndexed(const char *fieldName); | |
| | | | |
|
| | | /* called to indicate that an update fit in place. | |
| | | fits also called on an insert -- idea there is that if you had s | |
| | | ome mix and then went to | |
| | | pure inserts it would adapt and PF would trend to 1.0. note upd | |
| | | ate calls insert on a move | |
| | | so there is a double count there that must be adjusted for below | |
| | | . | |
| | | | |
| | | todo: greater sophistication could be helpful and added later. | |
| | | for example the absolute | |
| | | size of documents might be considered -- in some cases sma | |
| | | ller ones are more likely | |
| | | to grow than larger ones in the same collection? (not alwa | |
| | | ys) | |
| | | */ | |
| void paddingFits() { | | void paddingFits() { | |
|
| double x = paddingFactor - 0.01; | | MONGO_SOMETIMES(sometimes, 4) { // do this on a sampled basis t | |
| if ( x >= 1.0 ) { | | o journal less | |
| *getDur().writing(&paddingFactor) = x; | | double x = paddingFactor - 0.001; | |
| //getDur().setNoJournal(&paddingFactor, &x, sizeof(x)); | | if ( x >= 1.0 ) { | |
| | | *getDur().writing(&paddingFactor) = x; | |
| | | //getDur().setNoJournal(&paddingFactor, &x, sizeof(x)); | |
| | | } | |
| } | | } | |
| } | | } | |
| void paddingTooSmall() { | | void paddingTooSmall() { | |
|
| double x = paddingFactor + 0.6; | | MONGO_SOMETIMES(sometimes, 4) { // do this on a sampled basis t | |
| if ( x <= 2.0 ) { | | o journal less | |
| *getDur().writing(&paddingFactor) = x; | | /* the more indexes we have, the higher the cost of a move. | |
| //getDur().setNoJournal(&paddingFactor, &x, sizeof(x)); | | so we take that into | |
| | | account herein. note on a move that insert() calls padd | |
| | | ingFits(), thus | |
| | | here for example with no inserts and nIndexes = 1 we hav | |
| | | e | |
| | | .001*4-.001 or a 3:1 ratio to non moves -> 75% nonmoves. | |
| | | insert heavy | |
| | | can pushes this down considerably. further tweaking will | |
| | | be a good idea but | |
| | | this should be an adequate starting point. | |
| | | */ | |
| | | double N = min(nIndexes,7) + 3; | |
| | | double x = paddingFactor + (0.001 * N); | |
| | | if ( x <= 2.0 ) { | |
| | | *getDur().writing(&paddingFactor) = x; | |
| | | //getDur().setNoJournal(&paddingFactor, &x, sizeof(x)); | |
| | | } | |
| } | | } | |
| } | | } | |
| | | | |
| // @return offset in indexes[] | | // @return offset in indexes[] | |
| int findIndexByName(const char *name); | | int findIndexByName(const char *name); | |
| | | | |
| // @return offset in indexes[] | | // @return offset in indexes[] | |
| int findIndexByKeyPattern(const BSONObj& keyPattern); | | int findIndexByKeyPattern(const BSONObj& keyPattern); | |
| | | | |
| void findIndexByType( const string& name , vector<int>& matches ) { | | void findIndexByType( const string& name , vector<int>& matches ) { | |
| | | | |
| skipping to change at line 374 | | skipping to change at line 358 | |
| } | | } | |
| | | | |
| /* return which "deleted bucket" for this size object */ | | /* return which "deleted bucket" for this size object */ | |
| static int bucket(int n) { | | static int bucket(int n) { | |
| for ( int i = 0; i < Buckets; i++ ) | | for ( int i = 0; i < Buckets; i++ ) | |
| if ( bucketSizes[i] > n ) | | if ( bucketSizes[i] > n ) | |
| return i; | | return i; | |
| return Buckets-1; | | return Buckets-1; | |
| } | | } | |
| | | | |
|
| | | /* predetermine location of the next alloc without actually doing i | |
| | | t. | |
| | | if cannot predetermine returns null (so still call alloc() then) | |
| | | */ | |
| | | DiskLoc allocWillBeAt(const char *ns, int lenToAlloc); | |
| | | | |
| /* allocate a new record. lenToAlloc includes headers. */ | | /* allocate a new record. lenToAlloc includes headers. */ | |
| DiskLoc alloc(const char *ns, int lenToAlloc, DiskLoc& extentLoc); | | DiskLoc alloc(const char *ns, int lenToAlloc, DiskLoc& extentLoc); | |
|
| | | | |
| /* add a given record to the deleted chains for this NS */ | | /* add a given record to the deleted chains for this NS */ | |
| void addDeletedRec(DeletedRecord *d, DiskLoc dloc); | | void addDeletedRec(DeletedRecord *d, DiskLoc dloc); | |
| void dumpDeleted(set<DiskLoc> *extents = 0); | | void dumpDeleted(set<DiskLoc> *extents = 0); | |
| // Start from firstExtent by default. | | // Start from firstExtent by default. | |
| DiskLoc firstRecord( const DiskLoc &startExtent = DiskLoc() ) const
; | | DiskLoc firstRecord( const DiskLoc &startExtent = DiskLoc() ) const
; | |
| // Start from lastExtent by default. | | // Start from lastExtent by default. | |
| DiskLoc lastRecord( const DiskLoc &startExtent = DiskLoc() ) const; | | DiskLoc lastRecord( const DiskLoc &startExtent = DiskLoc() ) const; | |
| long long storageSize( int * numExtents = 0 , BSONArrayBuilder * ex
tentInfo = 0 ) const; | | long long storageSize( int * numExtents = 0 , BSONArrayBuilder * ex
tentInfo = 0 ) const; | |
| | | | |
| int averageObjectSize() { | | int averageObjectSize() { | |
| | | | |
| skipping to change at line 400 | | skipping to change at line 390 | |
| | | | |
| NamespaceDetails *writingWithoutExtra() { | | NamespaceDetails *writingWithoutExtra() { | |
| return ( NamespaceDetails* ) getDur().writingPtr( this, sizeof(
NamespaceDetails ) ); | | return ( NamespaceDetails* ) getDur().writingPtr( this, sizeof(
NamespaceDetails ) ); | |
| } | | } | |
| /** Make all linked Extra objects writeable as well */ | | /** Make all linked Extra objects writeable as well */ | |
| NamespaceDetails *writingWithExtra(); | | NamespaceDetails *writingWithExtra(); | |
| | | | |
| private: | | private: | |
| DiskLoc _alloc(const char *ns, int len); | | DiskLoc _alloc(const char *ns, int len); | |
| void maybeComplain( const char *ns, int len ) const; | | void maybeComplain( const char *ns, int len ) const; | |
|
| DiskLoc __stdAlloc(int len); | | DiskLoc __stdAlloc(int len, bool willBeAt); | |
| void compact(); // combine adjacent deleted records | | void compact(); // combine adjacent deleted records | |
| friend class NamespaceIndex; | | friend class NamespaceIndex; | |
| struct ExtraOld { | | struct ExtraOld { | |
| // note we could use this field for more chaining later, so don
't waste it: | | // note we could use this field for more chaining later, so don
't waste it: | |
| unsigned long long reserved1; | | unsigned long long reserved1; | |
| IndexDetails details[NIndexesExtra]; | | IndexDetails details[NIndexesExtra]; | |
| unsigned reserved2; | | unsigned reserved2; | |
| unsigned reserved3; | | unsigned reserved3; | |
| }; | | }; | |
| /** Update cappedLastDelRecLastExtent() after capExtent changed in
cappedTruncateAfter() */ | | /** Update cappedLastDelRecLastExtent() after capExtent changed in
cappedTruncateAfter() */ | |
| | | | |
| skipping to change at line 431 | | skipping to change at line 421 | |
| these are things we know / compute about a namespace that are transi
ent -- things | | these are things we know / compute about a namespace that are transi
ent -- things | |
| we don't actually store in the .ns file. so mainly caching of frequ
ently used | | we don't actually store in the .ns file. so mainly caching of frequ
ently used | |
| information. | | information. | |
| | | | |
| CAUTION: Are you maintaining this properly on a collection drop()?
A dropdatabase()? Be careful. | | CAUTION: Are you maintaining this properly on a collection drop()?
A dropdatabase()? Be careful. | |
| The current field "allIndexKeys" may have too many keys in
it on such an occurrence; | | The current field "allIndexKeys" may have too many keys in
it on such an occurrence; | |
| as currently used that does not cause anything terrible to
happen. | | as currently used that does not cause anything terrible to
happen. | |
| | | | |
| todo: cleanup code, need abstractions and separation | | todo: cleanup code, need abstractions and separation | |
| */ | | */ | |
|
| | | // todo: multiple db's with the same name (repairDatbase) is not handle | |
| | | d herein. that may be | |
| | | // the way to go, if not used by repair, but need some sort of en | |
| | | forcement / asserts. | |
| class NamespaceDetailsTransient : boost::noncopyable { | | class NamespaceDetailsTransient : boost::noncopyable { | |
| BOOST_STATIC_ASSERT( sizeof(NamespaceDetails) == 496 ); | | BOOST_STATIC_ASSERT( sizeof(NamespaceDetails) == 496 ); | |
| | | | |
|
| /* general -------------------------------------------------------- | | //Database *database; | |
| ----- */ | | const string _ns; | |
| private: | | | |
| string _ns; | | | |
| void reset(); | | void reset(); | |
|
| static std::map< string, shared_ptr< NamespaceDetailsTransient > > | | static std::map< string, shared_ptr< NamespaceDetailsTransient > > | |
| _map; | | _nsdMap; | |
| public: | | | |
| NamespaceDetailsTransient(const char *ns) : _ns(ns), _keysComputed( | | NamespaceDetailsTransient(Database*,const char *ns); | |
| false), _qcWriteCount() { } | | | |
| private: | | | |
| /* _get() is not threadsafe -- see get_inlock() comments */ | | | |
| static NamespaceDetailsTransient& _get(const char *ns); | | | |
| public: | | public: | |
|
| /* use get_w() when doing write operations. this is safe as there i | | ~NamespaceDetailsTransient(); | |
| s only 1 write op and it's exclusive to everything else. | | void addedIndex() { assertInWriteLock(); reset(); } | |
| for reads you must lock and then use get_inlock() instead. */ | | void deletedIndex() { assertInWriteLock(); reset(); } | |
| static NamespaceDetailsTransient& get_w(const char *ns) { | | | |
| DEV assertInWriteLock(); | | | |
| return _get(ns); | | | |
| } | | | |
| void addedIndex() { reset(); } | | | |
| void deletedIndex() { reset(); } | | | |
| /* Drop cached information on all namespaces beginning with the spe
cified prefix. | | /* Drop cached information on all namespaces beginning with the spe
cified prefix. | |
| Can be useful as index namespaces share the same start as the re
gular collection. | | Can be useful as index namespaces share the same start as the re
gular collection. | |
| SLOW - sequential scan of all NamespaceDetailsTransient objects
*/ | | SLOW - sequential scan of all NamespaceDetailsTransient objects
*/ | |
| static void clearForPrefix(const char *prefix); | | static void clearForPrefix(const char *prefix); | |
| static void eraseForPrefix(const char *prefix); | | static void eraseForPrefix(const char *prefix); | |
| | | | |
| /** | | /** | |
| * @return a cursor interface to the query optimizer. The implemen
tation may | | * @return a cursor interface to the query optimizer. The implemen
tation may | |
| * utilize a single query plan or interleave results from multiple
query | | * utilize a single query plan or interleave results from multiple
query | |
| * plans before settling on a single query plan. Note that the sch
ema of | | * plans before settling on a single query plan. Note that the sch
ema of | |
| * currKey() documents, the matcher(), and the isMultiKey() nature
of the | | * currKey() documents, the matcher(), and the isMultiKey() nature
of the | |
| * cursor may change over the course of iteration. | | * cursor may change over the course of iteration. | |
| * | | * | |
|
| * @param order - If no index exists that satisfies this sort order | | * @param query - Query used to select indexes and populate matcher | |
| , an | | s; this is not copied, and must outlive the Cursor | |
| * empty shared_ptr will be returned. | | * | |
| | | * @param order - Required ordering spec for documents produced by | |
| | | this cursor, | |
| | | * empty object default indicates no order requirement. If no inde | |
| | | x exists that | |
| | | * satisfies the required sort order, an empty shared_ptr is return | |
| | | ed. This is nit copied, and must outlive the Cursor | |
| | | * | |
| | | * @param requireIndex - If true, no unindexed (ie collection scan) | |
| | | cursors are | |
| | | * used to generate the returned cursor. If an unindexed cursor is | |
| | | required, an | |
| | | * assertion is raised by the cursor during iteration. | |
| | | * | |
| | | * @param simpleEqualityMatch - Set to true for certain simple quer | |
| | | ies - | |
| | | * see queryoptimizer.cpp. | |
| * | | * | |
| * The returned cursor may @throw inside of advance() or recoverFro
mYield() in | | * The returned cursor may @throw inside of advance() or recoverFro
mYield() in | |
| * certain error cases, for example if a capped overrun occurred du
ring a yield. | | * certain error cases, for example if a capped overrun occurred du
ring a yield. | |
| * This indicates that the cursor was unable to perform a complete
scan. | | * This indicates that the cursor was unable to perform a complete
scan. | |
| * | | * | |
| * This is a work in progress. Partial list of features not yet im
plemented: | | * This is a work in progress. Partial list of features not yet im
plemented: | |
|
| * - modification of scanned documents | | | |
| * - covered indexes | | * - covered indexes | |
|
| | | * - in memory sorting | |
| */ | | */ | |
|
| static shared_ptr<Cursor> getCursor( const char *ns, const BSONObj | | static shared_ptr<Cursor> getCursor( const char *ns, const BSONObj | |
| &query, const BSONObj &order = BSONObj() ); | | &query, | |
| | | const BSONObj &order = BSONObj( | |
| | | ), bool requireIndex = false, | |
| | | bool *simpleEqualityMatch = 0 ) | |
| | | ; | |
| | | | |
| | | /** | |
| | | * @return a single cursor that may work well for the given query. | |
| | | * It is possible no cursor is returned if the sort is not supporte | |
| | | d by an index. Clients are responsible | |
| | | * for checking this if they are not sure an index for a sort exist | |
| | | s, and defaulting to a non-sort if | |
| | | * no suitable indices exist. | |
| | | */ | |
| | | static shared_ptr<Cursor> bestGuessCursor( const char *ns, const BS | |
| | | ONObj &query, const BSONObj &sort ); | |
| | | | |
| /* indexKeys() cache ----------------------------------------------
------ */ | | /* indexKeys() cache ----------------------------------------------
------ */ | |
| /* assumed to be in write lock for this */ | | /* assumed to be in write lock for this */ | |
| private: | | private: | |
| bool _keysComputed; | | bool _keysComputed; | |
| set<string> _indexKeys; | | set<string> _indexKeys; | |
| void computeIndexKeys(); | | void computeIndexKeys(); | |
| public: | | public: | |
| /* get set of index keys for this namespace. handy to quickly chec
k if a given | | /* get set of index keys for this namespace. handy to quickly chec
k if a given | |
| field is indexed (Note it might be a secondary component of a co
mpound index.) | | field is indexed (Note it might be a secondary component of a co
mpound index.) | |
| | | | |
| skipping to change at line 517 | | skipping to change at line 520 | |
| assert( spec._finishedInit ); | | assert( spec._finishedInit ); | |
| } | | } | |
| } | | } | |
| return spec; | | return spec; | |
| } | | } | |
| | | | |
| /* query cache (for query optimizer) ------------------------------
------- */ | | /* query cache (for query optimizer) ------------------------------
------- */ | |
| private: | | private: | |
| int _qcWriteCount; | | int _qcWriteCount; | |
| map< QueryPattern, pair< BSONObj, long long > > _qcCache; | | map< QueryPattern, pair< BSONObj, long long > > _qcCache; | |
|
| | | static NamespaceDetailsTransient& make_inlock(const char *ns); | |
| public: | | public: | |
| static SimpleMutex _qcMutex; | | static SimpleMutex _qcMutex; | |
|
| /* you must be in the qcMutex when calling this (and using the retu | | | |
| rned val): */ | | /* you must be in the qcMutex when calling this. | |
| static NamespaceDetailsTransient& get_inlock(const char *ns) { | | A NamespaceDetailsTransient object will not go out of scope on y | |
| return _get(ns); | | ou if you are | |
| | | d.dbMutex.atLeastReadLocked(), so you do't have to stay locked. | |
| | | Creates a NamespaceDetailsTransient before returning if one DNE. | |
| | | todo: avoid creating too many on erroneous ns queries. | |
| | | */ | |
| | | static NamespaceDetailsTransient& get_inlock(const char *ns); | |
| | | | |
| | | static NamespaceDetailsTransient& get(const char *ns) { | |
| | | SimpleMutex::scoped_lock lk(_qcMutex); | |
| | | return get_inlock(ns); | |
| } | | } | |
|
| | | | |
| void clearQueryCache() { // public for unit tests | | void clearQueryCache() { // public for unit tests | |
| _qcCache.clear(); | | _qcCache.clear(); | |
| _qcWriteCount = 0; | | _qcWriteCount = 0; | |
| } | | } | |
| /* you must notify the cache if you are doing writes, as query plan
optimality will change */ | | /* you must notify the cache if you are doing writes, as query plan
optimality will change */ | |
| void notifyOfWriteOp() { | | void notifyOfWriteOp() { | |
| if ( _qcCache.empty() ) | | if ( _qcCache.empty() ) | |
| return; | | return; | |
|
| if ( ++_qcWriteCount >= 1000 ) | | if ( ++_qcWriteCount >= 100 ) | |
| clearQueryCache(); | | clearQueryCache(); | |
| } | | } | |
| BSONObj indexForPattern( const QueryPattern &pattern ) { | | BSONObj indexForPattern( const QueryPattern &pattern ) { | |
| return _qcCache[ pattern ].first; | | return _qcCache[ pattern ].first; | |
| } | | } | |
| long long nScannedForPattern( const QueryPattern &pattern ) { | | long long nScannedForPattern( const QueryPattern &pattern ) { | |
| return _qcCache[ pattern ].second; | | return _qcCache[ pattern ].second; | |
| } | | } | |
| void registerIndexForPattern( const QueryPattern &pattern, const BS
ONObj &indexKey, long long nScanned ) { | | void registerIndexForPattern( const QueryPattern &pattern, const BS
ONObj &indexKey, long long nScanned ) { | |
| _qcCache[ pattern ] = make_pair( indexKey, nScanned ); | | _qcCache[ pattern ] = make_pair( indexKey, nScanned ); | |
| } | | } | |
| | | | |
| }; /* NamespaceDetailsTransient */ | | }; /* NamespaceDetailsTransient */ | |
| | | | |
|
| inline NamespaceDetailsTransient& NamespaceDetailsTransient::_get(const | | inline NamespaceDetailsTransient& NamespaceDetailsTransient::get_inlock | |
| char *ns) { | | (const char *ns) { | |
| shared_ptr< NamespaceDetailsTransient > &t = _map[ ns ]; | | std::map< string, shared_ptr< NamespaceDetailsTransient > >::iterat | |
| if ( t.get() == 0 ) | | or i = _nsdMap.find(ns); | |
| t.reset( new NamespaceDetailsTransient(ns) ); | | if( i != _nsdMap.end() && | |
| return *t; | | i->second.get() ) { // could be null ptr from clearForPrefix | |
| | | return *i->second; | |
| | | } | |
| | | return make_inlock(ns); | |
| } | | } | |
| | | | |
| /* NamespaceIndex is the ".ns" file you see in the data directory. It
is the "system catalog" | | /* NamespaceIndex is the ".ns" file you see in the data directory. It
is the "system catalog" | |
| if you will: at least the core parts. (Additional info in system.*
collections.) | | if you will: at least the core parts. (Additional info in system.*
collections.) | |
| */ | | */ | |
| class NamespaceIndex { | | class NamespaceIndex { | |
|
| friend class NamespaceCursor; | | | |
| | | | |
| public: | | public: | |
| NamespaceIndex(const string &dir, const string &database) : | | NamespaceIndex(const string &dir, const string &database) : | |
| ht( 0 ), dir_( dir ), database_( database ) {} | | ht( 0 ), dir_( dir ), database_( database ) {} | |
| | | | |
| /* returns true if new db will be created if we init lazily */ | | /* returns true if new db will be created if we init lazily */ | |
| bool exists() const; | | bool exists() const; | |
| | | | |
|
| void init(); | | void init() { | |
| | | if( !ht ) | |
| void add_ns(const char *ns, DiskLoc& loc, bool capped) { | | _init(); | |
| NamespaceDetails details( loc, capped ); | | | |
| add_ns( ns, details ); | | | |
| } | | | |
| void add_ns( const char *ns, const NamespaceDetails &details ) { | | | |
| init(); | | | |
| Namespace n(ns); | | | |
| uassert( 10081 , "too many namespaces/collections", ht->put(n, | | | |
| details)); | | | |
| } | | } | |
| | | | |
|
| /* just for diagnostics */ | | void add_ns(const char *ns, DiskLoc& loc, bool capped); | |
| /*size_t detailsOffset(NamespaceDetails *d) { | | void add_ns( const char *ns, const NamespaceDetails &details ); | |
| if ( !ht ) | | | |
| return -1; | | | |
| return ((char *) d) - (char *) ht->nodes; | | | |
| }*/ | | | |
| | | | |
| NamespaceDetails* details(const char *ns) { | | NamespaceDetails* details(const char *ns) { | |
| if ( !ht ) | | if ( !ht ) | |
| return 0; | | return 0; | |
| Namespace n(ns); | | Namespace n(ns); | |
| NamespaceDetails *d = ht->get(n); | | NamespaceDetails *d = ht->get(n); | |
| if ( d && d->capped ) | | if ( d && d->capped ) | |
| d->cappedCheckMigrate(); | | d->cappedCheckMigrate(); | |
| return d; | | return d; | |
| } | | } | |
| | | | |
| skipping to change at line 606 | | skipping to change at line 609 | |
| | | | |
| bool find(const char *ns, DiskLoc& loc) { | | bool find(const char *ns, DiskLoc& loc) { | |
| NamespaceDetails *l = details(ns); | | NamespaceDetails *l = details(ns); | |
| if ( l ) { | | if ( l ) { | |
| loc = l->firstExtent; | | loc = l->firstExtent; | |
| return true; | | return true; | |
| } | | } | |
| return false; | | return false; | |
| } | | } | |
| | | | |
|
| bool allocated() const { | | bool allocated() const { return ht != 0; } | |
| return ht != 0; | | | |
| } | | | |
| | | | |
| void getNamespaces( list<string>& tofill , bool onlyCollections = t
rue ) const; | | void getNamespaces( list<string>& tofill , bool onlyCollections = t
rue ) const; | |
| | | | |
| NamespaceDetails::Extra* newExtra(const char *ns, int n, NamespaceD
etails *d); | | NamespaceDetails::Extra* newExtra(const char *ns, int n, NamespaceD
etails *d); | |
| | | | |
| boost::filesystem::path path() const; | | boost::filesystem::path path() const; | |
| | | | |
| unsigned long long fileLength() const { return f.length(); } | | unsigned long long fileLength() const { return f.length(); } | |
| | | | |
| private: | | private: | |
|
| | | void _init(); | |
| void maybeMkdir() const; | | void maybeMkdir() const; | |
| | | | |
| MongoMMF f; | | MongoMMF f; | |
| HashTable<Namespace,NamespaceDetails> *ht; | | HashTable<Namespace,NamespaceDetails> *ht; | |
| string dir_; | | string dir_; | |
| string database_; | | string database_; | |
| }; | | }; | |
| | | | |
| extern string dbpath; // --dbpath parm | | extern string dbpath; // --dbpath parm | |
| extern bool directoryperdb; | | extern bool directoryperdb; | |
| | | | |
| // Rename a namespace within current 'client' db. | | // Rename a namespace within current 'client' db. | |
| // (Arguments should include db name) | | // (Arguments should include db name) | |
|
| void renameNamespace( const char *from, const char *to ); | | void renameNamespace( const char *from, const char *to, bool stayTemp); | |
| | | | |
| // "database.a.b.c" -> "database" | | | |
| inline void nsToDatabase(const char *ns, char *database) { | | | |
| const char *p = ns; | | | |
| char *q = database; | | | |
| while ( *p != '.' ) { | | | |
| if ( *p == 0 ) | | | |
| break; | | | |
| *q++ = *p++; | | | |
| } | | | |
| *q = 0; | | | |
| if (q-database>=MaxDatabaseNameLen) { | | | |
| log() << "nsToDatabase: ns too long. terminating, buf overrun c | | | |
| ondition" << endl; | | | |
| dbexit( EXIT_POSSIBLE_CORRUPTION ); | | | |
| } | | | |
| } | | | |
| inline string nsToDatabase(const char *ns) { | | | |
| char buf[MaxDatabaseNameLen]; | | | |
| nsToDatabase(ns, buf); | | | |
| return buf; | | | |
| } | | | |
| inline string nsToDatabase(const string& ns) { | | | |
| size_t i = ns.find( '.' ); | | | |
| if ( i == string::npos ) | | | |
| return ns; | | | |
| return ns.substr( 0 , i ); | | | |
| } | | | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 29 change blocks. |
| 144 lines changed or deleted | | 137 lines changed or added | |
|
| oplogreader.h | | oplogreader.h | |
| | | | |
| skipping to change at line 18 | | skipping to change at line 18 | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| /* started abstracting out the querying of the primary/master's oplog | | /* started abstracting out the querying of the primary/master's oplog | |
| still fairly awkward but a start. | | still fairly awkward but a start. | |
| */ | | */ | |
| class OplogReader { | | class OplogReader { | |
| shared_ptr<DBClientConnection> _conn; | | shared_ptr<DBClientConnection> _conn; | |
| shared_ptr<DBClientCursor> cursor; | | shared_ptr<DBClientCursor> cursor; | |
| public: | | public: | |
|
| | | OplogReader() { } | |
| OplogReader() { | | ~OplogReader() { } | |
| } | | void resetCursor() { cursor.reset(); } | |
| ~OplogReader() { | | | |
| } | | | |
| | | | |
| void resetCursor() { | | | |
| cursor.reset(); | | | |
| } | | | |
| void resetConnection() { | | void resetConnection() { | |
| cursor.reset(); | | cursor.reset(); | |
| _conn.reset(); | | _conn.reset(); | |
| } | | } | |
| DBClientConnection* conn() { return _conn.get(); } | | DBClientConnection* conn() { return _conn.get(); } | |
| BSONObj findOne(const char *ns, const Query& q) { | | BSONObj findOne(const char *ns, const Query& q) { | |
| return conn()->findOne(ns, q, 0, QueryOption_SlaveOk); | | return conn()->findOne(ns, q, 0, QueryOption_SlaveOk); | |
| } | | } | |
|
| | | | |
| BSONObj getLastOp(const char *ns) { | | BSONObj getLastOp(const char *ns) { | |
| return findOne(ns, Query().sort(reverseNaturalObj)); | | return findOne(ns, Query().sort(reverseNaturalObj)); | |
| } | | } | |
| | | | |
| /* ok to call if already connected */ | | /* ok to call if already connected */ | |
| bool connect(string hostname); | | bool connect(string hostname); | |
| | | | |
| bool connect(const BSONObj& rid, const int from, const string& to); | | bool connect(const BSONObj& rid, const int from, const string& to); | |
| | | | |
| void tailCheck() { | | void tailCheck() { | |
| if( cursor.get() && cursor->isDead() ) { | | if( cursor.get() && cursor->isDead() ) { | |
| log() << "repl: old cursor isDead, will initiate a new one"
<< endl; | | log() << "repl: old cursor isDead, will initiate a new one"
<< endl; | |
| resetCursor(); | | resetCursor(); | |
| } | | } | |
| } | | } | |
| | | | |
| bool haveCursor() { return cursor.get() != 0; } | | bool haveCursor() { return cursor.get() != 0; } | |
| | | | |
|
| | | /** this is ok but commented out as when used one should consider i | |
| | | f QueryOption_OplogReplay | |
| | | is needed; if not fine, but if so, need to change. | |
| | | *//* | |
| void query(const char *ns, const BSONObj& query) { | | void query(const char *ns, const BSONObj& query) { | |
| assert( !haveCursor() ); | | assert( !haveCursor() ); | |
| cursor.reset( _conn->query(ns, query, 0, 0, 0, QueryOption_Slav
eOk).release() ); | | cursor.reset( _conn->query(ns, query, 0, 0, 0, QueryOption_Slav
eOk).release() ); | |
|
| } | | }*/ | |
| | | | |
|
| | | /** this can be used; it is commented out as it does not indicate | |
| | | QueryOption_OplogReplay and that is likely important. could be | |
| | | uncommented | |
| | | just need to add that. | |
| | | */ | |
| | | /* | |
| void queryGTE(const char *ns, OpTime t) { | | void queryGTE(const char *ns, OpTime t) { | |
| BSONObjBuilder q; | | BSONObjBuilder q; | |
| q.appendDate("$gte", t.asDate()); | | q.appendDate("$gte", t.asDate()); | |
| BSONObjBuilder q2; | | BSONObjBuilder q2; | |
| q2.append("ts", q.done()); | | q2.append("ts", q.done()); | |
| query(ns, q2.done()); | | query(ns, q2.done()); | |
| } | | } | |
|
| | | */ | |
| | | | |
| void tailingQuery(const char *ns, const BSONObj& query, const BSONO
bj* fields=0) { | | void tailingQuery(const char *ns, const BSONObj& query, const BSONO
bj* fields=0) { | |
| assert( !haveCursor() ); | | assert( !haveCursor() ); | |
| log(2) << "repl: " << ns << ".find(" << query.toString() << ')'
<< endl; | | log(2) << "repl: " << ns << ".find(" << query.toString() << ')'
<< endl; | |
| cursor.reset( _conn->query( ns, query, 0, 0, fields, | | cursor.reset( _conn->query( ns, query, 0, 0, fields, | |
| QueryOption_CursorTailable | QueryO
ption_SlaveOk | QueryOption_OplogReplay | | | QueryOption_CursorTailable | QueryO
ption_SlaveOk | QueryOption_OplogReplay | | |
|
| /* TODO: slaveok maybe shouldn't us
e? */ | | /* TODO: slaveOk maybe shouldn't us
e? */ | |
| QueryOption_AwaitData | | QueryOption_AwaitData | |
| ).release() ); | | ).release() ); | |
| } | | } | |
| | | | |
| void tailingQueryGTE(const char *ns, OpTime t, const BSONObj* field
s=0) { | | void tailingQueryGTE(const char *ns, OpTime t, const BSONObj* field
s=0) { | |
| BSONObjBuilder q; | | BSONObjBuilder q; | |
| q.appendDate("$gte", t.asDate()); | | q.appendDate("$gte", t.asDate()); | |
| BSONObjBuilder query; | | BSONObjBuilder query; | |
| query.append("ts", q.done()); | | query.append("ts", q.done()); | |
| tailingQuery(ns, query.done(), fields); | | tailingQuery(ns, query.done(), fields); | |
| } | | } | |
| | | | |
| /* Do a tailing query, but only send the ts field back. */ | | /* Do a tailing query, but only send the ts field back. */ | |
| void ghostQueryGTE(const char *ns, OpTime t) { | | void ghostQueryGTE(const char *ns, OpTime t) { | |
| const BSONObj fields = BSON("ts" << 1 << "_id" << 0); | | const BSONObj fields = BSON("ts" << 1 << "_id" << 0); | |
| return tailingQueryGTE(ns, t, &fields); | | return tailingQueryGTE(ns, t, &fields); | |
| } | | } | |
| | | | |
| bool more() { | | bool more() { | |
|
| assert( cursor.get() ); | | uassert( 15910, "Doesn't have cursor for reading oplog", cursor
.get() ); | |
| return cursor->more(); | | return cursor->more(); | |
| } | | } | |
|
| | | | |
| bool moreInCurrentBatch() { | | bool moreInCurrentBatch() { | |
|
| assert( cursor.get() ); | | uassert( 15911, "Doesn't have cursor for reading oplog", cursor
.get() ); | |
| return cursor->moreInCurrentBatch(); | | return cursor->moreInCurrentBatch(); | |
| } | | } | |
| | | | |
| /* old mongod's can't do the await flag... */ | | /* old mongod's can't do the await flag... */ | |
| bool awaitCapable() { | | bool awaitCapable() { | |
| return cursor->hasResultFlag(ResultFlag_AwaitCapable); | | return cursor->hasResultFlag(ResultFlag_AwaitCapable); | |
| } | | } | |
| | | | |
| void peek(vector<BSONObj>& v, int n) { | | void peek(vector<BSONObj>& v, int n) { | |
| if( cursor.get() ) | | if( cursor.get() ) | |
| cursor->peek(v,n); | | cursor->peek(v,n); | |
| } | | } | |
|
| | | | |
| BSONObj nextSafe() { return cursor->nextSafe(); } | | BSONObj nextSafe() { return cursor->nextSafe(); } | |
|
| | | | |
| BSONObj next() { return cursor->next(); } | | BSONObj next() { return cursor->next(); } | |
|
| | | | |
| void putBack(BSONObj op) { cursor->putBack(op); } | | void putBack(BSONObj op) { cursor->putBack(op); } | |
| | | | |
| private: | | private: | |
| bool commonConnect(const string& hostName); | | bool commonConnect(const string& hostName); | |
| bool passthroughHandshake(const BSONObj& rid, const int f); | | bool passthroughHandshake(const BSONObj& rid, const int f); | |
| }; | | }; | |
| | | | |
| } | | } | |
| | | | |
End of changes. 13 change blocks. |
| 17 lines changed or deleted | | 19 lines changed or added | |
|
| parallel.h | | parallel.h | |
| | | | |
| skipping to change at line 22 | | skipping to change at line 22 | |
| * distributed under the License is distributed on an "AS IS" BASIS, | | * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | |
| * See the License for the specific language governing permissions and | | * See the License for the specific language governing permissions and | |
| * limitations under the License. | | * limitations under the License. | |
| */ | | */ | |
| | | | |
| /** | | /** | |
| tools for working in parallel/sharded/clustered environment | | tools for working in parallel/sharded/clustered environment | |
| */ | | */ | |
| | | | |
|
| | | #pragma once | |
| | | | |
| #include "../pch.h" | | #include "../pch.h" | |
| #include "dbclient.h" | | #include "dbclient.h" | |
| #include "redef_macros.h" | | #include "redef_macros.h" | |
| #include "../db/dbmessage.h" | | #include "../db/dbmessage.h" | |
| #include "../db/matcher.h" | | #include "../db/matcher.h" | |
| #include "../util/concurrency/mvar.h" | | #include "../util/concurrency/mvar.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| /** | | /** | |
| | | | |
| skipping to change at line 72 | | skipping to change at line 74 | |
| BSONObj _extra; | | BSONObj _extra; | |
| BSONObj _orderObject; | | BSONObj _orderObject; | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * this is a cursor that works over a set of servers | | * this is a cursor that works over a set of servers | |
| * can be used in serial/paralellel as controlled by sub classes | | * can be used in serial/paralellel as controlled by sub classes | |
| */ | | */ | |
| class ClusteredCursor { | | class ClusteredCursor { | |
| public: | | public: | |
|
| | | ClusteredCursor( const QuerySpec& q ); | |
| ClusteredCursor( QueryMessage& q ); | | ClusteredCursor( QueryMessage& q ); | |
| ClusteredCursor( const string& ns , const BSONObj& q , int options=
0 , const BSONObj& fields=BSONObj() ); | | ClusteredCursor( const string& ns , const BSONObj& q , int options=
0 , const BSONObj& fields=BSONObj() ); | |
| virtual ~ClusteredCursor(); | | virtual ~ClusteredCursor(); | |
| | | | |
| /** call before using */ | | /** call before using */ | |
| void init(); | | void init(); | |
| | | | |
| virtual bool more() = 0; | | virtual bool more() = 0; | |
| virtual BSONObj next() = 0; | | virtual BSONObj next() = 0; | |
| | | | |
| static BSONObj concatQuery( const BSONObj& query , const BSONObj& e
xtraFilter ); | | static BSONObj concatQuery( const BSONObj& query , const BSONObj& e
xtraFilter ); | |
| | | | |
| virtual string type() const = 0; | | virtual string type() const = 0; | |
| | | | |
|
| virtual BSONObj explain(); | | virtual void explain(BSONObjBuilder& b) = 0; | |
| | | | |
| protected: | | protected: | |
| | | | |
| virtual void _init() = 0; | | virtual void _init() = 0; | |
| | | | |
| auto_ptr<DBClientCursor> query( const string& server , int num = 0
, BSONObj extraFilter = BSONObj() , int skipLeft = 0 , bool lazy=false ); | | auto_ptr<DBClientCursor> query( const string& server , int num = 0
, BSONObj extraFilter = BSONObj() , int skipLeft = 0 , bool lazy=false ); | |
| BSONObj explain( const string& server , BSONObj extraFilter = BSONO
bj() ); | | BSONObj explain( const string& server , BSONObj extraFilter = BSONO
bj() ); | |
| | | | |
| /** | | /** | |
| * checks the cursor for any errors | | * checks the cursor for any errors | |
| * will throw an exceptionif an error is encountered | | * will throw an exceptionif an error is encountered | |
| */ | | */ | |
| void _checkCursor( DBClientCursor * cursor ); | | void _checkCursor( DBClientCursor * cursor ); | |
| | | | |
| static BSONObj _concatFilter( const BSONObj& filter , const BSONObj
& extraFilter ); | | static BSONObj _concatFilter( const BSONObj& filter , const BSONObj
& extraFilter ); | |
| | | | |
| virtual void _explain( map< string,list<BSONObj> >& out ) = 0; | | virtual void _explain( map< string,list<BSONObj> >& out ) = 0; | |
| | | | |
| string _ns; | | string _ns; | |
| BSONObj _query; | | BSONObj _query; | |
|
| | | BSONObj _hint; | |
| | | BSONObj _sort; | |
| | | | |
| int _options; | | int _options; | |
| BSONObj _fields; | | BSONObj _fields; | |
| int _batchSize; | | int _batchSize; | |
| | | | |
| bool _didInit; | | bool _didInit; | |
| | | | |
| bool _done; | | bool _done; | |
| }; | | }; | |
| | | | |
|
| | | class ParallelConnectionMetadata; | |
| | | | |
| | | // TODO: We probably don't really need this as a separate class. | |
| class FilteringClientCursor { | | class FilteringClientCursor { | |
| public: | | public: | |
| FilteringClientCursor( const BSONObj filter = BSONObj() ); | | FilteringClientCursor( const BSONObj filter = BSONObj() ); | |
| FilteringClientCursor( DBClientCursor* cursor , const BSONObj filte
r = BSONObj() ); | | FilteringClientCursor( DBClientCursor* cursor , const BSONObj filte
r = BSONObj() ); | |
| FilteringClientCursor( auto_ptr<DBClientCursor> cursor , const BSON
Obj filter = BSONObj() ); | | FilteringClientCursor( auto_ptr<DBClientCursor> cursor , const BSON
Obj filter = BSONObj() ); | |
| ~FilteringClientCursor(); | | ~FilteringClientCursor(); | |
| | | | |
| void reset( auto_ptr<DBClientCursor> cursor ); | | void reset( auto_ptr<DBClientCursor> cursor ); | |
|
| void reset( DBClientCursor* cursor ); | | void reset( DBClientCursor* cursor, ParallelConnectionMetadata* _pc
mData = NULL ); | |
| | | | |
| bool more(); | | bool more(); | |
| BSONObj next(); | | BSONObj next(); | |
| | | | |
| BSONObj peek(); | | BSONObj peek(); | |
| | | | |
| DBClientCursor* raw() { return _cursor.get(); } | | DBClientCursor* raw() { return _cursor.get(); } | |
|
| | | ParallelConnectionMetadata* rawMData(){ return _pcmData; } | |
| | | | |
| | | // Required for new PCursor | |
| | | void release(){ | |
| | | _cursor.release(); | |
| | | _pcmData = NULL; | |
| | | } | |
| | | | |
| private: | | private: | |
| void _advance(); | | void _advance(); | |
| | | | |
| Matcher _matcher; | | Matcher _matcher; | |
| auto_ptr<DBClientCursor> _cursor; | | auto_ptr<DBClientCursor> _cursor; | |
|
| | | ParallelConnectionMetadata* _pcmData; | |
| | | | |
| BSONObj _next; | | BSONObj _next; | |
| bool _done; | | bool _done; | |
| }; | | }; | |
| | | | |
| class Servers { | | class Servers { | |
| public: | | public: | |
| Servers() { | | Servers() { | |
| } | | } | |
| | | | |
| | | | |
| skipping to change at line 219 | | skipping to change at line 236 | |
| void _init() {} | | void _init() {} | |
| | | | |
| vector<ServerAndQuery> _servers; | | vector<ServerAndQuery> _servers; | |
| unsigned _serverIndex; | | unsigned _serverIndex; | |
| | | | |
| FilteringClientCursor _current; | | FilteringClientCursor _current; | |
| | | | |
| int _needToSkip; | | int _needToSkip; | |
| }; | | }; | |
| | | | |
|
| | | class CommandInfo { | |
| | | public: | |
| | | string versionedNS; | |
| | | BSONObj cmdFilter; | |
| | | | |
| | | CommandInfo() {} | |
| | | CommandInfo( const string& vns, const BSONObj& filter ) : versioned | |
| | | NS( vns ), cmdFilter( filter ) {} | |
| | | | |
| | | bool isEmpty(){ | |
| | | return versionedNS.size() == 0; | |
| | | } | |
| | | | |
| | | string toString() const { | |
| | | return str::stream() << "CInfo " << BSON( "v_ns" << versionedNS | |
| | | << "filter" << cmdFilter ); | |
| | | } | |
| | | }; | |
| | | | |
| | | class ShardConnection; | |
| | | typedef shared_ptr<ShardConnection> ShardConnectionPtr; | |
| | | | |
| | | class DBClientCursor; | |
| | | typedef shared_ptr<DBClientCursor> DBClientCursorPtr; | |
| | | | |
| | | class Shard; | |
| | | typedef shared_ptr<Shard> ShardPtr; | |
| | | | |
| | | class ChunkManager; | |
| | | typedef shared_ptr<const ChunkManager> ChunkManagerPtr; | |
| | | | |
| | | class ParallelConnectionState { | |
| | | public: | |
| | | | |
| | | ParallelConnectionState() : | |
| | | count( 0 ), done( false ) { } | |
| | | | |
| | | ShardConnectionPtr conn; | |
| | | DBClientCursorPtr cursor; | |
| | | | |
| | | // Version information | |
| | | ChunkManagerPtr manager; | |
| | | ShardPtr primary; | |
| | | | |
| | | // Cursor status information | |
| | | long long count; | |
| | | bool done; | |
| | | | |
| | | BSONObj toBSON() const; | |
| | | | |
| | | string toString() const { | |
| | | return str::stream() << "PCState : " << toBSON(); | |
| | | } | |
| | | }; | |
| | | | |
| | | typedef ParallelConnectionState PCState; | |
| | | typedef shared_ptr<PCState> PCStatePtr; | |
| | | | |
| | | class ParallelConnectionMetadata { | |
| | | public: | |
| | | | |
| | | ParallelConnectionMetadata() : | |
| | | retryNext( false ), initialized( false ), finished( false ), co | |
| | | mpleted( false ), errored( false ) { } | |
| | | | |
| | | ~ParallelConnectionMetadata(){ | |
| | | cleanup( true ); | |
| | | } | |
| | | | |
| | | void cleanup( bool full = true ); | |
| | | | |
| | | PCStatePtr pcState; | |
| | | | |
| | | bool retryNext; | |
| | | | |
| | | bool initialized; | |
| | | bool finished; | |
| | | bool completed; | |
| | | | |
| | | bool errored; | |
| | | | |
| | | BSONObj toBSON() const; | |
| | | | |
| | | string toString() const { | |
| | | return str::stream() << "PCMData : " << toBSON(); | |
| | | } | |
| | | }; | |
| | | | |
| | | typedef ParallelConnectionMetadata PCMData; | |
| | | typedef shared_ptr<PCMData> PCMDataPtr; | |
| | | | |
| /** | | /** | |
|
| * runs a query in parellel across N servers | | * Runs a query in parallel across N servers. New logic has several mo | |
| * sots | | des - | |
| | | * 1) Standard query, enforces compatible chunk versions for queries ac | |
| | | ross all results | |
| | | * 2) Standard query, sent to particular servers with no compatible chu | |
| | | nk version enforced, but handling | |
| | | * stale configuration exceptions | |
| | | * 3) Command query, either enforcing compatible chunk versions or sent | |
| | | to particular shards. | |
| */ | | */ | |
| class ParallelSortClusteredCursor : public ClusteredCursor { | | class ParallelSortClusteredCursor : public ClusteredCursor { | |
| public: | | public: | |
|
| | | | |
| | | ParallelSortClusteredCursor( const QuerySpec& qSpec, const CommandI | |
| | | nfo& cInfo = CommandInfo() ); | |
| | | ParallelSortClusteredCursor( const set<Shard>& servers, const Query | |
| | | Spec& qSpec ); | |
| | | | |
| | | // LEGACY Constructors | |
| ParallelSortClusteredCursor( const set<ServerAndQuery>& servers , Q
ueryMessage& q , const BSONObj& sortKey ); | | ParallelSortClusteredCursor( const set<ServerAndQuery>& servers , Q
ueryMessage& q , const BSONObj& sortKey ); | |
| ParallelSortClusteredCursor( const set<ServerAndQuery>& servers , c
onst string& ns , | | ParallelSortClusteredCursor( const set<ServerAndQuery>& servers , c
onst string& ns , | |
| const Query& q , int options=0, const
BSONObj& fields=BSONObj() ); | | const Query& q , int options=0, const
BSONObj& fields=BSONObj() ); | |
|
| | | | |
| virtual ~ParallelSortClusteredCursor(); | | virtual ~ParallelSortClusteredCursor(); | |
| virtual bool more(); | | virtual bool more(); | |
| virtual BSONObj next(); | | virtual BSONObj next(); | |
| virtual string type() const { return "ParallelSort"; } | | virtual string type() const { return "ParallelSort"; } | |
|
| | | | |
| | | void fullInit(); | |
| | | void startInit(); | |
| | | void finishInit(); | |
| | | | |
| | | bool isCommand(){ return NamespaceString( _qSpec.ns() ).isCommand() | |
| | | ; } | |
| | | bool isExplain(){ return _qSpec.isExplain(); } | |
| | | bool isVersioned(){ return _qShards.size() == 0; } | |
| | | | |
| | | bool isSharded(); | |
| | | ShardPtr getPrimary(); | |
| | | void getQueryShards( set<Shard>& shards ); | |
| | | ChunkManagerPtr getChunkManager( const Shard& shard ); | |
| | | DBClientCursorPtr getShardCursor( const Shard& shard ); | |
| | | | |
| | | BSONObj toBSON() const; | |
| | | string toString() const; | |
| | | | |
| | | virtual void explain(BSONObjBuilder& b); | |
| | | | |
| protected: | | protected: | |
| void _finishCons(); | | void _finishCons(); | |
| void _init(); | | void _init(); | |
|
| | | void _oldInit(); | |
| | | | |
| virtual void _explain( map< string,list<BSONObj> >& out ); | | virtual void _explain( map< string,list<BSONObj> >& out ); | |
| | | | |
|
| | | void _markStaleNS( const NamespaceString& staleNS, bool& forceReloa | |
| | | d, bool& fullReload ); | |
| | | void _handleStaleNS( const NamespaceString& staleNS, bool forceRelo | |
| | | ad, bool fullReload ); | |
| | | | |
| | | set<Shard> _qShards; | |
| | | QuerySpec _qSpec; | |
| | | CommandInfo _cInfo; | |
| | | | |
| | | // Count round-trips req'd for namespaces and total | |
| | | map<string,int> _staleNSMap; | |
| | | int _totalTries; | |
| | | | |
| | | map<Shard,PCMData> _cursorMap; | |
| | | | |
| | | // LEGACY BELOW | |
| int _numServers; | | int _numServers; | |
|
| int _lastFrom; | | | |
| set<ServerAndQuery> _servers; | | set<ServerAndQuery> _servers; | |
| BSONObj _sortKey; | | BSONObj _sortKey; | |
| | | | |
| FilteringClientCursor * _cursors; | | FilteringClientCursor * _cursors; | |
| int _needToSkip; | | int _needToSkip; | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * tools for doing asynchronous operations | | * tools for doing asynchronous operations | |
| * right now uses underlying sync network ops and uses another thread | | * right now uses underlying sync network ops and uses another thread | |
| | | | |
| skipping to change at line 275 | | skipping to change at line 423 | |
| | | | |
| BSONObj result() const { | | BSONObj result() const { | |
| assert( _done ); | | assert( _done ); | |
| return _res; | | return _res; | |
| } | | } | |
| | | | |
| /** | | /** | |
| blocks until command is done | | blocks until command is done | |
| returns ok() | | returns ok() | |
| */ | | */ | |
|
| bool join(); | | bool join( int maxRetries = 1 ); | |
| | | | |
| private: | | private: | |
| | | | |
| CommandResult( const string& server , const string& db , const
BSONObj& cmd , int options , DBClientBase * conn ); | | CommandResult( const string& server , const string& db , const
BSONObj& cmd , int options , DBClientBase * conn ); | |
|
| | | void init(); | |
| | | | |
| string _server; | | string _server; | |
| string _db; | | string _db; | |
| int _options; | | int _options; | |
| BSONObj _cmd; | | BSONObj _cmd; | |
| DBClientBase * _conn; | | DBClientBase * _conn; | |
| scoped_ptr<ScopedDbConnection> _connHolder; // used if not prov
ided a connection | | scoped_ptr<ScopedDbConnection> _connHolder; // used if not prov
ided a connection | |
| | | | |
| scoped_ptr<DBClientCursor> _cursor; | | scoped_ptr<DBClientCursor> _cursor; | |
| | | | |
| | | | |
End of changes. 18 change blocks. |
| 6 lines changed or deleted | | 167 lines changed or added | |
|
| pch.h | | pch.h | |
|
| /** @file pch.h : include file for standard system include files, | | /** @file pch.h : include file for standard system include files, | |
| * or project specific include files that are used frequently, but | | * or project specific include files that are used frequently, but | |
| * are changed infrequently | | * are changed infrequently | |
| */ | | */ | |
| | | | |
| /* Copyright 2009 10gen Inc. | | /* Copyright 2009 10gen Inc. | |
| * | | * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | | * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | | * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | | * You may obtain a copy of the License at | |
| * | | * | |
| | | | |
| skipping to change at line 25 | | skipping to change at line 25 | |
| * distributed under the License is distributed on an "AS IS" BASIS, | | * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impli
ed. | |
| * See the License for the specific language governing permissions and | | * See the License for the specific language governing permissions and | |
| * limitations under the License. | | * limitations under the License. | |
| */ | | */ | |
| | | | |
| #ifndef MONGO_PCH_H | | #ifndef MONGO_PCH_H | |
| #define MONGO_PCH_H | | #define MONGO_PCH_H | |
| | | | |
| #if defined(MONGO_EXPOSE_MACROS) | | #if defined(MONGO_EXPOSE_MACROS) | |
|
| | | // this is defined in server.h for non-MONGO_EXPOSE_MACROS | |
| | | # define BOOST_ENABLE_ASSERT_HANDLER 1 | |
| | | | |
| # define JS_C_STRINGS_ARE_UTF8 | | # define JS_C_STRINGS_ARE_UTF8 | |
| # undef SUPPORT_UCP | | # undef SUPPORT_UCP | |
| # define SUPPORT_UCP | | # define SUPPORT_UCP | |
| # undef SUPPORT_UTF8 | | # undef SUPPORT_UTF8 | |
| # define SUPPORT_UTF8 | | # define SUPPORT_UTF8 | |
| # undef _CRT_SECURE_NO_WARNINGS | | # undef _CRT_SECURE_NO_WARNINGS | |
| # define _CRT_SECURE_NO_WARNINGS | | # define _CRT_SECURE_NO_WARNINGS | |
| #endif | | #endif | |
| | | | |
| #if defined(_WIN32) | | #if defined(_WIN32) | |
| | | | |
| skipping to change at line 60 | | skipping to change at line 63 | |
| # define __builtin_strlen strlen | | # define __builtin_strlen strlen | |
| # define __builtin_memchr memchr | | # define __builtin_memchr memchr | |
| # define __builtin_memcmp memcmp | | # define __builtin_memcmp memcmp | |
| # define __builtin_memcpy memcpy | | # define __builtin_memcpy memcpy | |
| # define __builtin_memset memset | | # define __builtin_memset memset | |
| # define __builtin_memmove memmove | | # define __builtin_memmove memmove | |
| #endif | | #endif | |
| | | | |
| #include <ctime> | | #include <ctime> | |
| #include <cstring> | | #include <cstring> | |
|
| #include <sstream> | | | |
| #include <string> | | #include <string> | |
| #include <memory> | | #include <memory> | |
| #include <string> | | #include <string> | |
| #include <iostream> | | #include <iostream> | |
|
| #include <fstream> | | | |
| #include <map> | | #include <map> | |
| #include <vector> | | #include <vector> | |
| #include <set> | | #include <set> | |
| #include <stdio.h> | | #include <stdio.h> | |
| #include <stdlib.h> | | #include <stdlib.h> | |
|
| #include <sstream> | | | |
| #include <signal.h> | | #include <signal.h> | |
| #include "targetver.h" | | #include "targetver.h" | |
| #include "time.h" | | #include "time.h" | |
| #include "string.h" | | #include "string.h" | |
| #include "limits.h" | | #include "limits.h" | |
| | | | |
|
| //#include <boost/any.hpp> | | | |
| #include "boost/thread/once.hpp" | | | |
| //#include <boost/archive/iterators/transform_width.hpp> | | | |
| #define BOOST_FILESYSTEM_VERSION 2 | | #define BOOST_FILESYSTEM_VERSION 2 | |
|
| #include <boost/filesystem/convenience.hpp> | | #include <boost/lexical_cast.hpp> | |
| #include <boost/filesystem/exception.hpp> | | | |
| #include <boost/filesystem/operations.hpp> | | | |
| #include <boost/program_options.hpp> | | | |
| #include <boost/shared_ptr.hpp> | | #include <boost/shared_ptr.hpp> | |
| #include <boost/smart_ptr.hpp> | | #include <boost/smart_ptr.hpp> | |
| #include <boost/function.hpp> | | #include <boost/function.hpp> | |
|
| #include "boost/bind.hpp" | | #include <boost/bind.hpp> | |
| #include "boost/function.hpp" | | | |
| #include <boost/thread/tss.hpp> | | #include <boost/thread/tss.hpp> | |
|
| #include "boost/detail/endian.hpp" | | #include <boost/detail/endian.hpp> | |
| #define BOOST_SPIRIT_THREADSAFE | | | |
| #include <boost/version.hpp> | | #include <boost/version.hpp> | |
|
| #include <boost/tuple/tuple.hpp> | | | |
| #include <boost/thread/thread.hpp> | | #include <boost/thread/thread.hpp> | |
| #include <boost/thread/condition.hpp> | | #include <boost/thread/condition.hpp> | |
| #include <boost/thread/recursive_mutex.hpp> | | #include <boost/thread/recursive_mutex.hpp> | |
| #include <boost/thread/xtime.hpp> | | #include <boost/thread/xtime.hpp> | |
|
| | | #undef assert | |
| | | #define assert MONGO_assert | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| using namespace std; | | using namespace std; | |
| using boost::shared_ptr; | | using boost::shared_ptr; | |
| | | | |
| #if defined(_DEBUG) | | #if defined(_DEBUG) | |
| const bool debug=true; | | const bool debug=true; | |
| #else | | #else | |
| const bool debug=false; | | const bool debug=false; | |
| #endif | | #endif | |
| | | | |
| // pdfile versions | | // pdfile versions | |
|
| const int VERSION = 4; | | const int PDFILE_VERSION = 4; | |
| const int VERSION_MINOR = 5; | | const int PDFILE_VERSION_MINOR = 5; | |
| | | | |
| enum ExitCode { | | enum ExitCode { | |
| EXIT_CLEAN = 0 , | | EXIT_CLEAN = 0 , | |
| EXIT_BADOPTIONS = 2 , | | EXIT_BADOPTIONS = 2 , | |
| EXIT_REPLICATION_ERROR = 3 , | | EXIT_REPLICATION_ERROR = 3 , | |
| EXIT_NEED_UPGRADE = 4 , | | EXIT_NEED_UPGRADE = 4 , | |
| EXIT_SHARDING_ERROR = 5 , | | EXIT_SHARDING_ERROR = 5 , | |
| EXIT_KILL = 12 , | | EXIT_KILL = 12 , | |
| EXIT_ABRUPT = 14 , | | EXIT_ABRUPT = 14 , | |
| EXIT_NTSERVICE_ERROR = 20 , | | EXIT_NTSERVICE_ERROR = 20 , | |
| EXIT_JAVA = 21 , | | EXIT_JAVA = 21 , | |
| EXIT_OOM_MALLOC = 42 , | | EXIT_OOM_MALLOC = 42 , | |
| EXIT_OOM_REALLOC = 43 , | | EXIT_OOM_REALLOC = 43 , | |
| EXIT_FS = 45 , | | EXIT_FS = 45 , | |
| EXIT_CLOCK_SKEW = 47 , | | EXIT_CLOCK_SKEW = 47 , | |
| EXIT_NET_ERROR = 48 , | | EXIT_NET_ERROR = 48 , | |
| EXIT_WINDOWS_SERVICE_STOP = 49 , | | EXIT_WINDOWS_SERVICE_STOP = 49 , | |
| EXIT_POSSIBLE_CORRUPTION = 60 , // this means we detected a possibl
e corruption situation, like a buf overflow | | EXIT_POSSIBLE_CORRUPTION = 60 , // this means we detected a possibl
e corruption situation, like a buf overflow | |
| EXIT_UNCAUGHT = 100 , // top level exception that wasn't caught | | EXIT_UNCAUGHT = 100 , // top level exception that wasn't caught | |
|
| EXIT_TEST = 101 , | | EXIT_TEST = 101 | |
| | | | |
| }; | | }; | |
| | | | |
| void dbexit( ExitCode returnCode, const char *whyMsg = "", bool tryToGe
tLock = false); | | void dbexit( ExitCode returnCode, const char *whyMsg = "", bool tryToGe
tLock = false); | |
| | | | |
| /** | | /** | |
| this is here so you can't just type exit() to quit the program | | this is here so you can't just type exit() to quit the program | |
| you should either use dbexit to shutdown cleanly, or ::exit to tell
the system to quit | | you should either use dbexit to shutdown cleanly, or ::exit to tell
the system to quit | |
| if you use this, you'll get a link error since mongo::exit isn't def
ined | | if you use this, you'll get a link error since mongo::exit isn't def
ined | |
| */ | | */ | |
| void exit( ExitCode returnCode ); | | void exit( ExitCode returnCode ); | |
| bool inShutdown(); | | bool inShutdown(); | |
| | | | |
|
| using namespace boost::filesystem; | | | |
| void asserted(const char *msg, const char *file, unsigned line); | | void asserted(const char *msg, const char *file, unsigned line); | |
| } | | } | |
| | | | |
|
| #include "util/allocator.h" | | | |
| #include "client/redef_macros.h" | | | |
| | | | |
| // TODO: Rework the headers so we don't need this craziness | | // TODO: Rework the headers so we don't need this craziness | |
| #include "bson/inline_decls.h" | | #include "bson/inline_decls.h" | |
| #define MONGO_assert(_Expression) (void)( MONGO_likely(!!(_Expression)) ||
(mongo::asserted(#_Expression, __FILE__, __LINE__), 0) ) | | #define MONGO_assert(_Expression) (void)( MONGO_likely(!!(_Expression)) ||
(mongo::asserted(#_Expression, __FILE__, __LINE__), 0) ) | |
| | | | |
| #include "util/debug_util.h" | | #include "util/debug_util.h" | |
| #include "util/goodies.h" | | #include "util/goodies.h" | |
| #include "util/log.h" | | #include "util/log.h" | |
|
| | | #include "util/allocator.h" | |
| #include "util/assert_util.h" | | #include "util/assert_util.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| void sayDbContext(const char *msg = 0); | | void sayDbContext(const char *msg = 0); | |
| void rawOut( const string &s ); | | void rawOut( const string &s ); | |
| | | | |
| typedef char _TCHAR; | | typedef char _TCHAR; | |
| | | | |
| using boost::uint32_t; | | using boost::uint32_t; | |
| using boost::uint64_t; | | using boost::uint64_t; | |
| | | | |
| /** called by mongos, mongod, test. do not call from clients and such. | | /** called by mongos, mongod, test. do not call from clients and such. | |
| invoked before about everything except global var construction. | | invoked before about everything except global var construction. | |
| */ | | */ | |
|
| void doPreServerStatupInits(); | | void doPreServerStartupInits(); | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
| #endif // MONGO_PCH_H | | #endif // MONGO_PCH_H | |
| | | | |
End of changes. 17 change blocks. |
| 26 lines changed or deleted | | 14 lines changed or added | |
|
| pdfile.h | | pdfile.h | |
| | | | |
| skipping to change at line 35 | | skipping to change at line 35 | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "../pch.h" | | #include "../pch.h" | |
| #include "../util/mmap.h" | | #include "../util/mmap.h" | |
| #include "diskloc.h" | | #include "diskloc.h" | |
| #include "jsobjmanipulator.h" | | #include "jsobjmanipulator.h" | |
| #include "namespace-inl.h" | | #include "namespace-inl.h" | |
| #include "client.h" | | #include "client.h" | |
| #include "mongommf.h" | | #include "mongommf.h" | |
|
| | | #include "memconcept.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| class DataFileHeader; | | class DataFileHeader; | |
| class Extent; | | class Extent; | |
| class Record; | | class Record; | |
| class Cursor; | | class Cursor; | |
| class OpDebug; | | class OpDebug; | |
| | | | |
| void dropDatabase(string db); | | void dropDatabase(string db); | |
| | | | |
| skipping to change at line 64 | | skipping to change at line 65 | |
| | | | |
| bool isValidNS( const StringData& ns ); | | bool isValidNS( const StringData& ns ); | |
| | | | |
| /*---------------------------------------------------------------------
*/ | | /*---------------------------------------------------------------------
*/ | |
| | | | |
| class MongoDataFile { | | class MongoDataFile { | |
| friend class DataFileMgr; | | friend class DataFileMgr; | |
| friend class BasicCursor; | | friend class BasicCursor; | |
| public: | | public: | |
| MongoDataFile(int fn) : _mb(0), fileNo(fn) { } | | MongoDataFile(int fn) : _mb(0), fileNo(fn) { } | |
|
| | | | |
| | | /** @return true if found and opened. if uninitialized (prealloc on | |
| | | ly) does not open. */ | |
| | | bool openExisting( const char *filename ); | |
| | | | |
| | | /** creates if DNE */ | |
| void open(const char *filename, int requestedDataSize = 0, bool pre
allocateOnly = false); | | void open(const char *filename, int requestedDataSize = 0, bool pre
allocateOnly = false); | |
| | | | |
| /* allocate a new extent from this datafile. | | /* allocate a new extent from this datafile. | |
| @param capped - true if capped collection | | @param capped - true if capped collection | |
| @param loops is our recursion check variable - you want to pass
in zero | | @param loops is our recursion check variable - you want to pass
in zero | |
| */ | | */ | |
| Extent* createExtent(const char *ns, int approxSize, bool capped =
false, int loops = 0); | | Extent* createExtent(const char *ns, int approxSize, bool capped =
false, int loops = 0); | |
| | | | |
| DataFileHeader *getHeader() { return header(); } | | DataFileHeader *getHeader() { return header(); } | |
| | | | |
| | | | |
| skipping to change at line 199 | | skipping to change at line 205 | |
| | | | |
| /** be careful when referencing this that your write intent was cor
rect */ | | /** be careful when referencing this that your write intent was cor
rect */ | |
| char data[4]; | | char data[4]; | |
| | | | |
| int netLength() { | | int netLength() { | |
| return lengthWithHeaders - HeaderSize; | | return lengthWithHeaders - HeaderSize; | |
| } | | } | |
| //void setNewLength(int netlen) { lengthWithHeaders = netlen + Head
erSize; } | | //void setNewLength(int netlen) { lengthWithHeaders = netlen + Head
erSize; } | |
| | | | |
| /* use this when a record is deleted. basically a union with next/p
rev fields */ | | /* use this when a record is deleted. basically a union with next/p
rev fields */ | |
|
| DeletedRecord& asDeleted() { | | DeletedRecord& asDeleted() { return *((DeletedRecord*) this); } | |
| return *((DeletedRecord*) this); | | | |
| } | | Extent* myExtent(const DiskLoc& myLoc) { return DataFileMgr::getExt | |
| | | ent(DiskLoc(myLoc.a(), extentOfs)); } | |
| | | | |
|
| Extent* myExtent(const DiskLoc& myLoc) { | | | |
| return DataFileMgr::getExtent(DiskLoc(myLoc.a(), extentOfs)); | | | |
| } | | | |
| /* get the next record in the namespace, traversing extents as nece
ssary */ | | /* get the next record in the namespace, traversing extents as nece
ssary */ | |
| DiskLoc getNext(const DiskLoc& myLoc); | | DiskLoc getNext(const DiskLoc& myLoc); | |
| DiskLoc getPrev(const DiskLoc& myLoc); | | DiskLoc getPrev(const DiskLoc& myLoc); | |
| | | | |
| DiskLoc nextInExtent(const DiskLoc& myLoc) { | | DiskLoc nextInExtent(const DiskLoc& myLoc) { | |
| if ( nextOfs == DiskLoc::NullOfs ) | | if ( nextOfs == DiskLoc::NullOfs ) | |
| return DiskLoc(); | | return DiskLoc(); | |
| assert( nextOfs ); | | assert( nextOfs ); | |
| return DiskLoc(myLoc.a(), nextOfs); | | return DiskLoc(myLoc.a(), nextOfs); | |
| } | | } | |
| | | | |
| skipping to change at line 316 | | skipping to change at line 319 | |
| assert( dl.sameFile(myLoc) ); | | assert( dl.sameFile(myLoc) ); | |
| int x = dl.getOfs() - myLoc.getOfs(); | | int x = dl.getOfs() - myLoc.getOfs(); | |
| assert( x > 0 ); | | assert( x > 0 ); | |
| return (Record *) (((char *) this) + x); | | return (Record *) (((char *) this) + x); | |
| } | | } | |
| | | | |
| Extent* getNextExtent() { return xnext.isNull() ? 0 : DataFileMgr::
getExtent(xnext); } | | Extent* getNextExtent() { return xnext.isNull() ? 0 : DataFileMgr::
getExtent(xnext); } | |
| Extent* getPrevExtent() { return xprev.isNull() ? 0 : DataFileMgr::
getExtent(xprev); } | | Extent* getPrevExtent() { return xprev.isNull() ? 0 : DataFileMgr::
getExtent(xprev); } | |
| | | | |
| static int maxSize(); | | static int maxSize(); | |
|
| static int minSize() { return 0x1000; } | | static int minSize() { return 0x100; } | |
| /** | | /** | |
| * @param len lengt of record we need | | * @param len lengt of record we need | |
| * @param lastRecord size of last extent which is a factor in next
extent size | | * @param lastRecord size of last extent which is a factor in next
extent size | |
| */ | | */ | |
| static int followupSize(int len, int lastExtentLen); | | static int followupSize(int len, int lastExtentLen); | |
| | | | |
| /** get a suggested size for the first extent in a namespace | | /** get a suggested size for the first extent in a namespace | |
| * @param len length of record we need to insert | | * @param len length of record we need to insert | |
| */ | | */ | |
| static int initialSize(int len); | | static int initialSize(int len); | |
| | | | |
| skipping to change at line 369 | | skipping to change at line 372 | |
| int versionMinor; | | int versionMinor; | |
| int fileLength; | | int fileLength; | |
| DiskLoc unused; /* unused is the portion of the file that doesn't b
elong to any allocated extents. -1 = no more */ | | DiskLoc unused; /* unused is the portion of the file that doesn't b
elong to any allocated extents. -1 = no more */ | |
| int unusedLength; | | int unusedLength; | |
| char reserved[8192 - 4*4 - 8]; | | char reserved[8192 - 4*4 - 8]; | |
| | | | |
| char data[4]; // first extent starts here | | char data[4]; // first extent starts here | |
| | | | |
| enum { HeaderSize = 8192 }; | | enum { HeaderSize = 8192 }; | |
| | | | |
|
| bool isCurrentVersion() const { return ( version == VERSION ) && (
versionMinor == VERSION_MINOR ); } | | bool isCurrentVersion() const { return ( version == PDFILE_VERSION
) && ( versionMinor == PDFILE_VERSION_MINOR ); } | |
| | | | |
| bool uninitialized() const { return version == 0; } | | bool uninitialized() const { return version == 0; } | |
| | | | |
| void init(int fileno, int filelength, const char* filename) { | | void init(int fileno, int filelength, const char* filename) { | |
| if ( uninitialized() ) { | | if ( uninitialized() ) { | |
|
| | | DEV log() << "datafileheader::init initializing " << filena
me << " n:" << fileno << endl; | |
| if( !(filelength > 32768 ) ) { | | if( !(filelength > 32768 ) ) { | |
| massert(13640, str::stream() << "DataFileHeader looks c
orrupt at file open filelength:" << filelength << " fileno:" << fileno, fal
se); | | massert(13640, str::stream() << "DataFileHeader looks c
orrupt at file open filelength:" << filelength << " fileno:" << fileno, fal
se); | |
| } | | } | |
|
| | | | |
| | | { | |
| | | if( !d.dbMutex.isWriteLocked() ) { | |
| | | log() << "*** TEMP NOT INITIALIZING FILE " << filen | |
| | | ame << ", not in a write lock." << endl; | |
| | | log() << "temp bypass until more elaborate change - | |
| | | case that is manifesting is benign anyway" << endl; | |
| | | return; | |
| | | /** | |
| | | log() << "ERROR can't create outside a write lock" | |
| | | << endl; | |
| | | printStackTrace(); | |
| | | ::abort(); | |
| | | **/ | |
| | | } | |
| | | } | |
| | | | |
| getDur().createdFile(filename, filelength); | | getDur().createdFile(filename, filelength); | |
| assert( HeaderSize == 8192 ); | | assert( HeaderSize == 8192 ); | |
| DataFileHeader *h = getDur().writing(this); | | DataFileHeader *h = getDur().writing(this); | |
| h->fileLength = filelength; | | h->fileLength = filelength; | |
|
| h->version = VERSION; | | h->version = PDFILE_VERSION; | |
| h->versionMinor = VERSION_MINOR; | | h->versionMinor = PDFILE_VERSION_MINOR; | |
| h->unused.set( fileno, HeaderSize ); | | h->unused.set( fileno, HeaderSize ); | |
| assert( (data-(char*)this) == HeaderSize ); | | assert( (data-(char*)this) == HeaderSize ); | |
| h->unusedLength = fileLength - HeaderSize - 16; | | h->unusedLength = fileLength - HeaderSize - 16; | |
| } | | } | |
| } | | } | |
| | | | |
| bool isEmpty() const { | | bool isEmpty() const { | |
| return uninitialized() || ( unusedLength == fileLength - Header
Size - 16 ); | | return uninitialized() || ( unusedLength == fileLength - Header
Size - 16 ); | |
| } | | } | |
| }; | | }; | |
| | | | |
| skipping to change at line 406 | | skipping to change at line 424 | |
| | | | |
| inline Extent* MongoDataFile::_getExtent(DiskLoc loc) const { | | inline Extent* MongoDataFile::_getExtent(DiskLoc loc) const { | |
| loc.assertOk(); | | loc.assertOk(); | |
| Extent *e = (Extent *) (p()+loc.getOfs()); | | Extent *e = (Extent *) (p()+loc.getOfs()); | |
| return e; | | return e; | |
| } | | } | |
| | | | |
| inline Extent* MongoDataFile::getExtent(DiskLoc loc) const { | | inline Extent* MongoDataFile::getExtent(DiskLoc loc) const { | |
| Extent *e = _getExtent(loc); | | Extent *e = _getExtent(loc); | |
| e->assertOk(); | | e->assertOk(); | |
|
| | | memconcept::is(e, memconcept::extent); | |
| return e; | | return e; | |
| } | | } | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
| #include "cursor.h" | | #include "cursor.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| inline Record* MongoDataFile::recordAt(DiskLoc dl) { | | inline Record* MongoDataFile::recordAt(DiskLoc dl) { | |
| | | | |
| skipping to change at line 457 | | skipping to change at line 476 | |
| } | | } | |
| inline DiskLoc Record::getPrev(const DiskLoc& myLoc) { | | inline DiskLoc Record::getPrev(const DiskLoc& myLoc) { | |
| if ( prevOfs != DiskLoc::NullOfs ) | | if ( prevOfs != DiskLoc::NullOfs ) | |
| return DiskLoc(myLoc.a(), prevOfs); | | return DiskLoc(myLoc.a(), prevOfs); | |
| Extent *e = myExtent(myLoc); | | Extent *e = myExtent(myLoc); | |
| if ( e->xprev.isNull() ) | | if ( e->xprev.isNull() ) | |
| return DiskLoc(); | | return DiskLoc(); | |
| return e->xprev.ext()->lastRecord; | | return e->xprev.ext()->lastRecord; | |
| } | | } | |
| | | | |
|
| inline Record* DiskLoc::rec() const { | | | |
| return DataFileMgr::getRecord(*this); | | | |
| } | | | |
| inline BSONObj DiskLoc::obj() const { | | inline BSONObj DiskLoc::obj() const { | |
| return BSONObj(rec()->accessed()); | | return BSONObj(rec()->accessed()); | |
| } | | } | |
| inline DeletedRecord* DiskLoc::drec() const { | | inline DeletedRecord* DiskLoc::drec() const { | |
| assert( _a != -1 ); | | assert( _a != -1 ); | |
|
| return (DeletedRecord*) rec(); | | DeletedRecord* dr = (DeletedRecord*) rec(); | |
| | | memconcept::is(dr, memconcept::deletedrecord); | |
| | | return dr; | |
| } | | } | |
| inline Extent* DiskLoc::ext() const { | | inline Extent* DiskLoc::ext() const { | |
| return DataFileMgr::getExtent(*this); | | return DataFileMgr::getExtent(*this); | |
| } | | } | |
| | | | |
| template< class V > | | template< class V > | |
| inline | | inline | |
| const BtreeBucket<V> * DiskLoc::btree() const { | | const BtreeBucket<V> * DiskLoc::btree() const { | |
| assert( _a != -1 ); | | assert( _a != -1 ); | |
| return (const BtreeBucket<V> *) rec()->data; | | return (const BtreeBucket<V> *) rec()->data; | |
| } | | } | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
| #include "database.h" | | #include "database.h" | |
|
| | | #include "memconcept.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| boost::intmax_t dbSize( const char *database ); | | boost::intmax_t dbSize( const char *database ); | |
| | | | |
| inline NamespaceIndex* nsindex(const char *ns) { | | inline NamespaceIndex* nsindex(const char *ns) { | |
| Database *database = cc().database(); | | Database *database = cc().database(); | |
| assert( database ); | | assert( database ); | |
|
| | | memconcept::is(database, memconcept::database, ns, sizeof(Database)
); | |
| DEV { | | DEV { | |
| char buf[256]; | | char buf[256]; | |
| nsToDatabase(ns, buf); | | nsToDatabase(ns, buf); | |
| if ( database->name != buf ) { | | if ( database->name != buf ) { | |
|
| out() << "ERROR: attempt to write to wrong database databas
e\n"; | | out() << "ERROR: attempt to write to wrong database\n"; | |
| out() << " ns:" << ns << '\n'; | | out() << " ns:" << ns << '\n'; | |
| out() << " database->name:" << database->name << endl; | | out() << " database->name:" << database->name << endl; | |
| assert( database->name == buf ); | | assert( database->name == buf ); | |
| } | | } | |
| } | | } | |
| return &database->namespaceIndex; | | return &database->namespaceIndex; | |
| } | | } | |
| | | | |
| inline NamespaceDetails* nsdetails(const char *ns) { | | inline NamespaceDetails* nsdetails(const char *ns) { | |
| // if this faults, did you set the current db first? (Client::Cont
ext + dblock) | | // if this faults, did you set the current db first? (Client::Cont
ext + dblock) | |
|
| return nsindex(ns)->details(ns); | | NamespaceDetails *d = nsindex(ns)->details(ns); | |
| | | if( d ) { | |
| | | memconcept::is(d, memconcept::nsdetails, ns, sizeof(NamespaceDe | |
| | | tails)); | |
| | | } | |
| | | return d; | |
| } | | } | |
| | | | |
| inline Extent* DataFileMgr::getExtent(const DiskLoc& dl) { | | inline Extent* DataFileMgr::getExtent(const DiskLoc& dl) { | |
| assert( dl.a() != -1 ); | | assert( dl.a() != -1 ); | |
| return cc().database()->getFile(dl.a())->getExtent(dl); | | return cc().database()->getFile(dl.a())->getExtent(dl); | |
| } | | } | |
| | | | |
| inline Record* DataFileMgr::getRecord(const DiskLoc& dl) { | | inline Record* DataFileMgr::getRecord(const DiskLoc& dl) { | |
| assert( dl.a() != -1 ); | | assert( dl.a() != -1 ); | |
|
| return cc().database()->getFile(dl.a())->recordAt(dl); | | Record* r = cc().database()->getFile(dl.a())->recordAt(dl); | |
| | | return r; | |
| } | | } | |
| | | | |
| BOOST_STATIC_ASSERT( 16 == sizeof(DeletedRecord) ); | | BOOST_STATIC_ASSERT( 16 == sizeof(DeletedRecord) ); | |
| | | | |
| inline DeletedRecord* DataFileMgr::makeDeletedRecord(const DiskLoc& dl,
int len) { | | inline DeletedRecord* DataFileMgr::makeDeletedRecord(const DiskLoc& dl,
int len) { | |
| assert( dl.a() != -1 ); | | assert( dl.a() != -1 ); | |
| return (DeletedRecord*) cc().database()->getFile(dl.a())->makeRecor
d(dl, sizeof(DeletedRecord)); | | return (DeletedRecord*) cc().database()->getFile(dl.a())->makeRecor
d(dl, sizeof(DeletedRecord)); | |
| } | | } | |
| | | | |
| void ensureHaveIdIndex(const char *ns); | | void ensureHaveIdIndex(const char *ns); | |
| | | | |
End of changes. 17 change blocks. |
| 17 lines changed or deleted | | 48 lines changed or added | |
|
| queryoptimizer.h | | queryoptimizer.h | |
| | | | |
| skipping to change at line 52 | | skipping to change at line 52 | |
| int idxNo, // -1 = no index | | int idxNo, // -1 = no index | |
| const FieldRangeSetPair &frsp, | | const FieldRangeSetPair &frsp, | |
| const FieldRangeSetPair *originalFrsp, | | const FieldRangeSetPair *originalFrsp, | |
| const BSONObj &originalQuery, | | const BSONObj &originalQuery, | |
| const BSONObj &order, | | const BSONObj &order, | |
| bool mustAssertOnYieldFailure = true, | | bool mustAssertOnYieldFailure = true, | |
| const BSONObj &startKey = BSONObj(), | | const BSONObj &startKey = BSONObj(), | |
| const BSONObj &endKey = BSONObj(), | | const BSONObj &endKey = BSONObj(), | |
| string special="" ); | | string special="" ); | |
| | | | |
|
| /** @return true iff no other plans should be considered. */ | | /** @return true iff this plan cannot return any documents. */ | |
| | | bool impossible() const { return _impossible; } | |
| | | /** | |
| | | * @return true iff this plan should run as the only candidate plan | |
| | | in the absence of an | |
| | | * impossible plan. | |
| | | */ | |
| bool optimal() const { return _optimal; } | | bool optimal() const { return _optimal; } | |
|
| /* @return true iff this plan should not be considered at all. */ | | /** @return true iff this plan should not be considered at all. */ | |
| bool unhelpful() const { return _unhelpful; } | | bool unhelpful() const { return _unhelpful; } | |
| /** @return true iff ScanAndOrder processing will be required for r
esult set. */ | | /** @return true iff ScanAndOrder processing will be required for r
esult set. */ | |
| bool scanAndOrderRequired() const { return _scanAndOrderRequired; } | | bool scanAndOrderRequired() const { return _scanAndOrderRequired; } | |
| /** | | /** | |
| * @return true iff the index we are using has keys such that it ca
n completely resolve the | | * @return true iff the index we are using has keys such that it ca
n completely resolve the | |
| * query expression to match by itself without ever checking the ma
in object. | | * query expression to match by itself without ever checking the ma
in object. | |
| */ | | */ | |
| bool exactKeyMatch() const { return _exactKeyMatch; } | | bool exactKeyMatch() const { return _exactKeyMatch; } | |
| /** @return true iff this QueryPlan would perform an unindexed scan
. */ | | /** @return true iff this QueryPlan would perform an unindexed scan
. */ | |
| bool willScanTable() const { return _idxNo < 0 && !_impossible; } | | bool willScanTable() const { return _idxNo < 0 && !_impossible; } | |
|
| | | /** @return 'special' attribute of the plan, which was either set e | |
| | | xplicitly or generated from the index. */ | |
| | | const string &special() const { return _special; } | |
| | | | |
| /** @return a new cursor based on this QueryPlan's index and FieldR
angeSet. */ | | /** @return a new cursor based on this QueryPlan's index and FieldR
angeSet. */ | |
| shared_ptr<Cursor> newCursor( const DiskLoc &startLoc = DiskLoc() ,
int numWanted=0 ) const; | | shared_ptr<Cursor> newCursor( const DiskLoc &startLoc = DiskLoc() ,
int numWanted=0 ) const; | |
| /** @return a new reverse cursor if this is an unindexed plan. */ | | /** @return a new reverse cursor if this is an unindexed plan. */ | |
| shared_ptr<Cursor> newReverseCursor() const; | | shared_ptr<Cursor> newReverseCursor() const; | |
| /** Register this plan as a winner for its QueryPattern, with speci
fied 'nscanned'. */ | | /** Register this plan as a winner for its QueryPattern, with speci
fied 'nscanned'. */ | |
| void registerSelf( long long nScanned ) const; | | void registerSelf( long long nScanned ) const; | |
| | | | |
| int direction() const { return _direction; } | | int direction() const { return _direction; } | |
| BSONObj indexKey() const; | | BSONObj indexKey() const; | |
| | | | |
| skipping to change at line 88 | | skipping to change at line 95 | |
| NamespaceDetails *nsd() const { return _d; } | | NamespaceDetails *nsd() const { return _d; } | |
| BSONObj originalQuery() const { return _originalQuery; } | | BSONObj originalQuery() const { return _originalQuery; } | |
| BSONObj simplifiedQuery( const BSONObj& fields = BSONObj() ) const
{ return _frs.simplifiedQuery( fields ); } | | BSONObj simplifiedQuery( const BSONObj& fields = BSONObj() ) const
{ return _frs.simplifiedQuery( fields ); } | |
| const FieldRange &range( const char *fieldName ) const { return _fr
s.range( fieldName ); } | | const FieldRange &range( const char *fieldName ) const { return _fr
s.range( fieldName ); } | |
| shared_ptr<FieldRangeVector> originalFrv() const { return _original
Frv; } | | shared_ptr<FieldRangeVector> originalFrv() const { return _original
Frv; } | |
| | | | |
| const FieldRangeSet &multikeyFrs() const { return _frsMulti; } | | const FieldRangeSet &multikeyFrs() const { return _frsMulti; } | |
| | | | |
| bool mustAssertOnYieldFailure() const { return _mustAssertOnYieldFa
ilure; } | | bool mustAssertOnYieldFailure() const { return _mustAssertOnYieldFa
ilure; } | |
| | | | |
|
| /** just for testing */ | | /** The following member functions are just for testing. */ | |
| | | | |
| shared_ptr<FieldRangeVector> frv() const { return _frv; } | | shared_ptr<FieldRangeVector> frv() const { return _frv; } | |
| bool isMultiKey() const; | | bool isMultiKey() const; | |
| | | | |
| private: | | private: | |
|
| | | void checkTableScanAllowed() const; | |
| | | void warnOnCappedIdTableScan() const; | |
| | | | |
| NamespaceDetails * _d; | | NamespaceDetails * _d; | |
| int _idxNo; | | int _idxNo; | |
| const FieldRangeSet &_frs; | | const FieldRangeSet &_frs; | |
| const FieldRangeSet &_frsMulti; | | const FieldRangeSet &_frsMulti; | |
| const BSONObj &_originalQuery; | | const BSONObj &_originalQuery; | |
| const BSONObj &_order; | | const BSONObj &_order; | |
| const IndexDetails * _index; | | const IndexDetails * _index; | |
| bool _optimal; | | bool _optimal; | |
| bool _scanAndOrderRequired; | | bool _scanAndOrderRequired; | |
| bool _exactKeyMatch; | | bool _exactKeyMatch; | |
| | | | |
| skipping to change at line 293 | | skipping to change at line 303 | |
| } | | } | |
| | | | |
| /** Initialize or iterate a runner generated from @param originalOp
. */ | | /** Initialize or iterate a runner generated from @param originalOp
. */ | |
| shared_ptr<QueryOp> nextOp( QueryOp &originalOp, bool retried = fal
se ); | | shared_ptr<QueryOp> nextOp( QueryOp &originalOp, bool retried = fal
se ); | |
| | | | |
| /** Yield the runner member. */ | | /** Yield the runner member. */ | |
| | | | |
| bool prepareToYield(); | | bool prepareToYield(); | |
| void recoverFromYield(); | | void recoverFromYield(); | |
| | | | |
|
| | | /** Clear the runner member. */ | |
| | | void clearRunner(); | |
| | | | |
| QueryPlanPtr firstPlan() const { return _plans[ 0 ]; } | | QueryPlanPtr firstPlan() const { return _plans[ 0 ]; } | |
| | | | |
| /** @return metadata about cursors and index bounds for all plans,
suitable for explain output. */ | | /** @return metadata about cursors and index bounds for all plans,
suitable for explain output. */ | |
| BSONObj explain() const; | | BSONObj explain() const; | |
| /** @return true iff a plan is selected based on previous success o
f this plan. */ | | /** @return true iff a plan is selected based on previous success o
f this plan. */ | |
|
| bool usingPrerecordedPlan() const { return _usingPrerecordedPlan; } | | bool usingCachedPlan() const { return _usingCachedPlan; } | |
| /** @return a single plan that may work well for the specified quer
y. */ | | /** @return a single plan that may work well for the specified quer
y. */ | |
| QueryPlanPtr getBestGuess() const; | | QueryPlanPtr getBestGuess() const; | |
| | | | |
| //for testing | | //for testing | |
| const FieldRangeSetPair &frsp() const { return *_frsp; } | | const FieldRangeSetPair &frsp() const { return *_frsp; } | |
| const FieldRangeSetPair *originalFrsp() const { return _originalFrs
p.get(); } | | const FieldRangeSetPair *originalFrsp() const { return _originalFrs
p.get(); } | |
| bool modifiedKeys() const; | | bool modifiedKeys() const; | |
| bool hasMultiKey() const; | | bool hasMultiKey() const; | |
| | | | |
| private: | | private: | |
| void addOtherPlans( bool checkFirst ); | | void addOtherPlans( bool checkFirst ); | |
| void addPlan( QueryPlanPtr plan, bool checkFirst ) { | | void addPlan( QueryPlanPtr plan, bool checkFirst ) { | |
| if ( checkFirst && plan->indexKey().woCompare( _plans[ 0 ]->ind
exKey() ) == 0 ) | | if ( checkFirst && plan->indexKey().woCompare( _plans[ 0 ]->ind
exKey() ) == 0 ) | |
| return; | | return; | |
| _plans.push_back( plan ); | | _plans.push_back( plan ); | |
| } | | } | |
| void init(); | | void init(); | |
| void addHint( IndexDetails &id ); | | void addHint( IndexDetails &id ); | |
|
| void warnOnCappedIdTableScan() const; | | | |
| class Runner { | | class Runner { | |
| public: | | public: | |
| Runner( QueryPlanSet &plans, QueryOp &op ); | | Runner( QueryPlanSet &plans, QueryOp &op ); | |
| | | | |
| /** | | /** | |
| * Iterate interactively through candidate documents on all pla
ns. | | * Iterate interactively through candidate documents on all pla
ns. | |
| * QueryOp objects are returned at each interleaved step. | | * QueryOp objects are returned at each interleaved step. | |
| */ | | */ | |
| | | | |
| /** @return a plan that has completed, otherwise an arbitrary p
lan. */ | | /** @return a plan that has completed, otherwise an arbitrary p
lan. */ | |
| | | | |
| skipping to change at line 369 | | skipping to change at line 381 | |
| }; | | }; | |
| our_priority_queue<OpHolder> _queue; | | our_priority_queue<OpHolder> _queue; | |
| }; | | }; | |
| | | | |
| const char *_ns; | | const char *_ns; | |
| BSONObj _originalQuery; | | BSONObj _originalQuery; | |
| auto_ptr<FieldRangeSetPair> _frsp; | | auto_ptr<FieldRangeSetPair> _frsp; | |
| auto_ptr<FieldRangeSetPair> _originalFrsp; | | auto_ptr<FieldRangeSetPair> _originalFrsp; | |
| PlanSet _plans; | | PlanSet _plans; | |
| bool _mayRecordPlan; | | bool _mayRecordPlan; | |
|
| bool _usingPrerecordedPlan; | | bool _usingCachedPlan; | |
| BSONObj _hint; | | BSONObj _hint; | |
| BSONObj _order; | | BSONObj _order; | |
| long long _oldNScanned; | | long long _oldNScanned; | |
| bool _honorRecordedPlan; | | bool _honorRecordedPlan; | |
| BSONObj _min; | | BSONObj _min; | |
| BSONObj _max; | | BSONObj _max; | |
| string _special; | | string _special; | |
| bool _bestGuessOnly; | | bool _bestGuessOnly; | |
| bool _mayYield; | | bool _mayYield; | |
| ElapsedTracker _yieldSometimesTracker; | | ElapsedTracker _yieldSometimesTracker; | |
| | | | |
| skipping to change at line 429 | | skipping to change at line 441 | |
| /** Initialize or iterate a runner generated from @param originalOp
. */ | | /** Initialize or iterate a runner generated from @param originalOp
. */ | |
| | | | |
| void initialOp( const shared_ptr<QueryOp> &originalOp ) { _baseOp =
originalOp; } | | void initialOp( const shared_ptr<QueryOp> &originalOp ) { _baseOp =
originalOp; } | |
| shared_ptr<QueryOp> nextOp(); | | shared_ptr<QueryOp> nextOp(); | |
| | | | |
| /** Yield the runner member. */ | | /** Yield the runner member. */ | |
| | | | |
| bool prepareToYield(); | | bool prepareToYield(); | |
| void recoverFromYield(); | | void recoverFromYield(); | |
| | | | |
|
| | | /** Clear the runner member. */ | |
| | | void clearRunner(); | |
| | | | |
| | | int currentNPlans() const; | |
| | | | |
| /** | | /** | |
| * @return a single simple cursor if the scanner would run a single
cursor | | * @return a single simple cursor if the scanner would run a single
cursor | |
| * for this query, otherwise return an empty shared_ptr. | | * for this query, otherwise return an empty shared_ptr. | |
| */ | | */ | |
| shared_ptr<Cursor> singleCursor() const; | | shared_ptr<Cursor> singleCursor() const; | |
| | | | |
|
| | | /** | |
| | | * @return the query plan that would be used if the scanner would r | |
| | | un a single | |
| | | * cursor for this query, otherwise 0. The returned plan is invali | |
| | | d if this | |
| | | * MultiPlanScanner is destroyed, hence we return a raw pointer. | |
| | | */ | |
| | | const QueryPlan *singlePlan() const; | |
| | | | |
| /** @return true iff more $or clauses need to be scanned. */ | | /** @return true iff more $or clauses need to be scanned. */ | |
|
| bool mayRunMore() const { return _or ? ( !_tableScanned && !_org->o | | bool mayRunMore() const { | |
| rFinished() ) : _i == 0; } | | return _or ? ( !_tableScanned && !_org->orRangesExhausted() ) : | |
| | | _i == 0; | |
| | | } | |
| /** @return non-$or version of explain output. */ | | /** @return non-$or version of explain output. */ | |
| BSONObj oldExplain() const { assertNotOr(); return _currentQps->exp
lain(); } | | BSONObj oldExplain() const { assertNotOr(); return _currentQps->exp
lain(); } | |
| /** @return true iff this is not a $or query and a plan is selected
based on previous success of this plan. */ | | /** @return true iff this is not a $or query and a plan is selected
based on previous success of this plan. */ | |
|
| bool usingPrerecordedPlan() const { return !_or && _currentQps->usi
ngPrerecordedPlan(); } | | bool usingCachedPlan() const { return !_or && _currentQps->usingCac
hedPlan(); } | |
| /** Don't attempt to scan multiple plans, just use the best guess.
*/ | | /** Don't attempt to scan multiple plans, just use the best guess.
*/ | |
| void setBestGuessOnly() { _bestGuessOnly = true; } | | void setBestGuessOnly() { _bestGuessOnly = true; } | |
| /** Yielding is allowed while running each QueryPlan. */ | | /** Yielding is allowed while running each QueryPlan. */ | |
| void mayYield( bool val ) { _mayYield = val; } | | void mayYield( bool val ) { _mayYield = val; } | |
| bool modifiedKeys() const { return _currentQps->modifiedKeys(); } | | bool modifiedKeys() const { return _currentQps->modifiedKeys(); } | |
| bool hasMultiKey() const { return _currentQps->hasMultiKey(); } | | bool hasMultiKey() const { return _currentQps->hasMultiKey(); } | |
| | | | |
| private: | | private: | |
| void assertNotOr() const { | | void assertNotOr() const { | |
| massert( 13266, "not implemented for $or query", !_or ); | | massert( 13266, "not implemented for $or query", !_or ); | |
| } | | } | |
| void assertMayRunMore() const { | | void assertMayRunMore() const { | |
| massert( 13271, "can't run more ops", mayRunMore() ); | | massert( 13271, "can't run more ops", mayRunMore() ); | |
| } | | } | |
| shared_ptr<QueryOp> nextOpBeginningClause(); | | shared_ptr<QueryOp> nextOpBeginningClause(); | |
| shared_ptr<QueryOp> nextOpHandleEndOfClause(); | | shared_ptr<QueryOp> nextOpHandleEndOfClause(); | |
| bool uselessOr( const BSONElement &hint ) const; | | bool uselessOr( const BSONElement &hint ) const; | |
|
| const char * _ns; | | const string _ns; | |
| bool _or; | | bool _or; | |
| BSONObj _query; | | BSONObj _query; | |
| shared_ptr<OrRangeGenerator> _org; // May be null in certain non $o
r query cases. | | shared_ptr<OrRangeGenerator> _org; // May be null in certain non $o
r query cases. | |
| auto_ptr<QueryPlanSet> _currentQps; | | auto_ptr<QueryPlanSet> _currentQps; | |
| int _i; | | int _i; | |
| bool _honorRecordedPlan; | | bool _honorRecordedPlan; | |
| bool _bestGuessOnly; | | bool _bestGuessOnly; | |
| BSONObj _hint; | | BSONObj _hint; | |
| bool _mayYield; | | bool _mayYield; | |
| bool _tableScanned; | | bool _tableScanned; | |
| | | | |
| skipping to change at line 482 | | skipping to change at line 508 | |
| /** Provides a cursor interface for certain limited uses of a MultiPlan
Scanner. */ | | /** Provides a cursor interface for certain limited uses of a MultiPlan
Scanner. */ | |
| class MultiCursor : public Cursor { | | class MultiCursor : public Cursor { | |
| public: | | public: | |
| class CursorOp : public QueryOp { | | class CursorOp : public QueryOp { | |
| public: | | public: | |
| CursorOp() {} | | CursorOp() {} | |
| CursorOp( const QueryOp &other ) : QueryOp( other ) {} | | CursorOp( const QueryOp &other ) : QueryOp( other ) {} | |
| virtual shared_ptr<Cursor> newCursor() const = 0; | | virtual shared_ptr<Cursor> newCursor() const = 0; | |
| }; | | }; | |
| /** takes ownership of 'op' */ | | /** takes ownership of 'op' */ | |
|
| MultiCursor( const char *ns, const BSONObj &pattern, const BSONObj
&order, shared_ptr<CursorOp> op = shared_ptr<CursorOp>(), bool mayYield = f
alse, bool hintIdElseNatural = false ); | | MultiCursor( const char *ns, const BSONObj &pattern, const BSONObj
&order, shared_ptr<CursorOp> op = shared_ptr<CursorOp>(), bool mayYield = f
alse ); | |
| /** | | /** | |
| * Used | | * Used | |
| * 1. To handoff a query to a getMore() | | * 1. To handoff a query to a getMore() | |
| * 2. To handoff a QueryOptimizerCursor | | * 2. To handoff a QueryOptimizerCursor | |
| * @param nscanned is an optional initial value, if not supplied ns
canned() | | * @param nscanned is an optional initial value, if not supplied ns
canned() | |
| * will always return -1 | | * will always return -1 | |
| */ | | */ | |
| MultiCursor( auto_ptr<MultiPlanScanner> mps, const shared_ptr<Curso
r> &c, const shared_ptr<CoveredIndexMatcher> &matcher, const QueryOp &op, l
ong long nscanned = -1 ); | | MultiCursor( auto_ptr<MultiPlanScanner> mps, const shared_ptr<Curso
r> &c, const shared_ptr<CoveredIndexMatcher> &matcher, const QueryOp &op, l
ong long nscanned = -1 ); | |
| | | | |
| virtual bool ok() { return _c->ok(); } | | virtual bool ok() { return _c->ok(); } | |
| | | | |
| skipping to change at line 518 | | skipping to change at line 544 | |
| virtual bool supportYields() { return _c->supportYields(); } | | virtual bool supportYields() { return _c->supportYields(); } | |
| virtual BSONObj indexKeyPattern() { return _c->indexKeyPattern(); } | | virtual BSONObj indexKeyPattern() { return _c->indexKeyPattern(); } | |
| | | | |
| /** | | /** | |
| * with update we could potentially get the same document on multip
le | | * with update we could potentially get the same document on multip
le | |
| * indexes, but update appears to already handle this with seenObje
cts | | * indexes, but update appears to already handle this with seenObje
cts | |
| * so we don't have to do anything special here. | | * so we don't have to do anything special here. | |
| */ | | */ | |
| virtual bool getsetdup(DiskLoc loc) { return _c->getsetdup( loc );
} | | virtual bool getsetdup(DiskLoc loc) { return _c->getsetdup( loc );
} | |
| | | | |
|
| | | virtual bool autoDedup() const { return _c->autoDedup(); } | |
| | | | |
| virtual bool modifiedKeys() const { return _mps->modifiedKeys(); } | | virtual bool modifiedKeys() const { return _mps->modifiedKeys(); } | |
| | | | |
| virtual bool isMultiKey() const { return _mps->hasMultiKey(); } | | virtual bool isMultiKey() const { return _mps->hasMultiKey(); } | |
| | | | |
| virtual shared_ptr< CoveredIndexMatcher > matcherPtr() const { retu
rn _matcher; } | | virtual shared_ptr< CoveredIndexMatcher > matcherPtr() const { retu
rn _matcher; } | |
| virtual CoveredIndexMatcher* matcher() const { return _matcher.get(
); } | | virtual CoveredIndexMatcher* matcher() const { return _matcher.get(
); } | |
| | | | |
|
| | | virtual bool capped() const { return _c->capped(); } | |
| | | | |
| /** return -1 if we're a getmore handoff */ | | /** return -1 if we're a getmore handoff */ | |
| virtual long long nscanned() { return _nscanned >= 0 ? _nscanned +
_c->nscanned() : _nscanned; } | | virtual long long nscanned() { return _nscanned >= 0 ? _nscanned +
_c->nscanned() : _nscanned; } | |
| /** just for testing */ | | /** just for testing */ | |
| shared_ptr<Cursor> sub_c() const { return _c; } | | shared_ptr<Cursor> sub_c() const { return _c; } | |
| private: | | private: | |
| class NoOp : public CursorOp { | | class NoOp : public CursorOp { | |
| public: | | public: | |
| NoOp() {} | | NoOp() {} | |
| NoOp( const QueryOp &other ) : CursorOp( other ) {} | | NoOp( const QueryOp &other ) : CursorOp( other ) {} | |
| virtual void _init() { setComplete(); } | | virtual void _init() { setComplete(); } | |
| virtual void next() {} | | virtual void next() {} | |
| virtual bool mayRecordPlan() const { return false; } | | virtual bool mayRecordPlan() const { return false; } | |
| virtual QueryOp *_createChild() const { return new NoOp(); } | | virtual QueryOp *_createChild() const { return new NoOp(); } | |
| virtual shared_ptr<Cursor> newCursor() const { return qp().newC
ursor(); } | | virtual shared_ptr<Cursor> newCursor() const { return qp().newC
ursor(); } | |
| virtual long long nscanned() { assert( false ); return 0; } | | virtual long long nscanned() { assert( false ); return 0; } | |
| }; | | }; | |
| void nextClause(); | | void nextClause(); | |
|
| static BSONObj idElseNaturalHint( const char *ns ); | | | |
| shared_ptr<CursorOp> _op; | | shared_ptr<CursorOp> _op; | |
| shared_ptr<Cursor> _c; | | shared_ptr<Cursor> _c; | |
|
| BSONObj _hint; | | | |
| BSONElement _hintElt; | | | |
| auto_ptr<MultiPlanScanner> _mps; | | auto_ptr<MultiPlanScanner> _mps; | |
| shared_ptr<CoveredIndexMatcher> _matcher; | | shared_ptr<CoveredIndexMatcher> _matcher; | |
| long long _nscanned; | | long long _nscanned; | |
| }; | | }; | |
| | | | |
| /** NOTE min, max, and keyPattern will be updated to be consistent with
the selected index. */ | | /** NOTE min, max, and keyPattern will be updated to be consistent with
the selected index. */ | |
| IndexDetails *indexDetailsForRange( const char *ns, string &errmsg, BSO
NObj &min, BSONObj &max, BSONObj &keyPattern ); | | IndexDetails *indexDetailsForRange( const char *ns, string &errmsg, BSO
NObj &min, BSONObj &max, BSONObj &keyPattern ); | |
| | | | |
|
| bool isSimpleIdQuery( const BSONObj& query ); | | | |
| | | | |
| /** | | | |
| * @return a single cursor that may work well for the given query. | | | |
| * It is possible no cursor is returned if the sort is not supported by | | | |
| an index. Clients are responsible | | | |
| * for checking this if they are not sure an index for a sort exists, a | | | |
| nd defaulting to a non-sort if | | | |
| * no suitable indices exist. | | | |
| */ | | | |
| shared_ptr<Cursor> bestGuessCursor( const char *ns, const BSONObj &quer | | | |
| y, const BSONObj &sort ); | | | |
| | | | |
| /** | | /** | |
| * Add-on functionality for queryutil classes requiring access to index
ing | | * Add-on functionality for queryutil classes requiring access to index
ing | |
| * functionality not currently linked to mongos. | | * functionality not currently linked to mongos. | |
| * TODO Clean this up a bit, possibly with separate sharded and non sha
rded | | * TODO Clean this up a bit, possibly with separate sharded and non sha
rded | |
| * implementations for the appropriate queryutil classes or by pulling
index | | * implementations for the appropriate queryutil classes or by pulling
index | |
| * related functionality into separate wrapper classes. | | * related functionality into separate wrapper classes. | |
| */ | | */ | |
| struct QueryUtilIndexed { | | struct QueryUtilIndexed { | |
| /** @return true if the index may be useful according to its KeySpe
c. */ | | /** @return true if the index may be useful according to its KeySpe
c. */ | |
| static bool indexUseful( const FieldRangeSetPair &frsp, NamespaceDe
tails *d, int idxNo, const BSONObj &order ); | | static bool indexUseful( const FieldRangeSetPair &frsp, NamespaceDe
tails *d, int idxNo, const BSONObj &order ); | |
| | | | |
End of changes. 20 change blocks. |
| 27 lines changed or deleted | | 45 lines changed or added | |
|
| queryutil.h | | queryutil.h | |
| | | | |
| skipping to change at line 91 | | skipping to change at line 91 | |
| | | | |
| BSONElement min() const { assert( !empty() ); return _intervals[ 0
]._lower._bound; } | | BSONElement min() const { assert( !empty() ); return _intervals[ 0
]._lower._bound; } | |
| BSONElement max() const { assert( !empty() ); return _intervals[ _i
ntervals.size() - 1 ]._upper._bound; } | | BSONElement max() const { assert( !empty() ); return _intervals[ _i
ntervals.size() - 1 ]._upper._bound; } | |
| bool minInclusive() const { assert( !empty() ); return _intervals[
0 ]._lower._inclusive; } | | bool minInclusive() const { assert( !empty() ); return _intervals[
0 ]._lower._inclusive; } | |
| bool maxInclusive() const { assert( !empty() ); return _intervals[
_intervals.size() - 1 ]._upper._inclusive; } | | bool maxInclusive() const { assert( !empty() ); return _intervals[
_intervals.size() - 1 ]._upper._inclusive; } | |
| | | | |
| /** @return true iff this range expresses a single equality interva
l. */ | | /** @return true iff this range expresses a single equality interva
l. */ | |
| bool equality() const; | | bool equality() const; | |
| /** @return true if all the intervals for this range are equalities
*/ | | /** @return true if all the intervals for this range are equalities
*/ | |
| bool inQuery() const; | | bool inQuery() const; | |
|
| /** @return true iff this range does not include every BSONElement | | /** | |
| */ | | * @return true iff this range includes all BSONElements | |
| bool nontrivial() const; | | * (the range is the universal set of BSONElements). | |
| /** @return true iff this range matches no BSONElements. */ | | */ | |
| | | bool universal() const; | |
| | | /** @return true iff this range includes no BSONElements. */ | |
| bool empty() const { return _intervals.empty(); } | | bool empty() const { return _intervals.empty(); } | |
| | | | |
|
| /** Empty the range so it matches no BSONElements. */ | | /** Empty the range so it includes no BSONElements. */ | |
| void makeEmpty() { _intervals.clear(); } | | void makeEmpty() { _intervals.clear(); } | |
| const vector<FieldInterval> &intervals() const { return _intervals;
} | | const vector<FieldInterval> &intervals() const { return _intervals;
} | |
| string getSpecial() const { return _special; } | | string getSpecial() const { return _special; } | |
| /** Make component intervals noninclusive. */ | | /** Make component intervals noninclusive. */ | |
| void setExclusiveBounds(); | | void setExclusiveBounds(); | |
| /** | | /** | |
| * Constructs a range where all FieldIntervals and FieldBounds are
in | | * Constructs a range where all FieldIntervals and FieldBounds are
in | |
| * the opposite order of the current range. | | * the opposite order of the current range. | |
| * NOTE the resulting intervals might not be strictValid(). | | * NOTE the resulting intervals might not be strictValid(). | |
| */ | | */ | |
| | | | |
| skipping to change at line 141 | | skipping to change at line 144 | |
| /** | | /** | |
| * A set of FieldRanges determined from constraints on the fields of a
query, | | * A set of FieldRanges determined from constraints on the fields of a
query, | |
| * that may be used to determine index bounds. | | * that may be used to determine index bounds. | |
| */ | | */ | |
| class FieldRangeSet { | | class FieldRangeSet { | |
| public: | | public: | |
| friend class OrRangeGenerator; | | friend class OrRangeGenerator; | |
| friend class FieldRangeVector; | | friend class FieldRangeVector; | |
| FieldRangeSet( const char *ns, const BSONObj &query , bool singleKe
y , bool optimize=true ); | | FieldRangeSet( const char *ns, const BSONObj &query , bool singleKe
y , bool optimize=true ); | |
| | | | |
|
| /** @return true if there is a nontrivial range for the given field | | | |
| . */ | | | |
| bool hasRange( const char *fieldName ) const { | | | |
| map<string, FieldRange>::const_iterator f = _ranges.find( field | | | |
| Name ); | | | |
| return f != _ranges.end(); | | | |
| } | | | |
| /** @return range for the given field. */ | | /** @return range for the given field. */ | |
| const FieldRange &range( const char *fieldName ) const; | | const FieldRange &range( const char *fieldName ) const; | |
| /** @return range for the given field. */ | | /** @return range for the given field. */ | |
| FieldRange &range( const char *fieldName ); | | FieldRange &range( const char *fieldName ); | |
|
| /** @return the number of nontrivial ranges. */ | | /** @return the number of non universal ranges. */ | |
| int nNontrivialRanges() const; | | int numNonUniversalRanges() const; | |
| | | /** @return the field ranges comprising this set. */ | |
| | | const map<string,FieldRange> &ranges() const { return _ranges; } | |
| /** | | /** | |
| * @return true if a match could be possible on every field. Genera
lly this | | * @return true if a match could be possible on every field. Genera
lly this | |
| * is not useful information for a single key FieldRangeSet and | | * is not useful information for a single key FieldRangeSet and | |
| * matchPossibleForIndex() should be used instead. | | * matchPossibleForIndex() should be used instead. | |
| */ | | */ | |
| bool matchPossible() const; | | bool matchPossible() const; | |
| /** | | /** | |
| * @return true if a match could be possible given the value of _si
ngleKey | | * @return true if a match could be possible given the value of _si
ngleKey | |
| * and index key 'keyPattern'. | | * and index key 'keyPattern'. | |
| * @param keyPattern May be {} or {$natural:1} for a non index scan
. | | * @param keyPattern May be {} or {$natural:1} for a non index scan
. | |
| */ | | */ | |
| bool matchPossibleForIndex( const BSONObj &keyPattern ) const; | | bool matchPossibleForIndex( const BSONObj &keyPattern ) const; | |
| | | | |
| const char *ns() const { return _ns; } | | const char *ns() const { return _ns; } | |
| | | | |
| /** | | /** | |
|
| * @return a simplified query from the extreme values of the nontri
vial | | * @return a simplified query from the extreme values of the non un
iversal | |
| * fields. | | * fields. | |
| * @param fields If specified, the fields of the returned object ar
e | | * @param fields If specified, the fields of the returned object ar
e | |
| * ordered to match those of 'fields'. | | * ordered to match those of 'fields'. | |
| */ | | */ | |
| BSONObj simplifiedQuery( const BSONObj &fields = BSONObj() ) const; | | BSONObj simplifiedQuery( const BSONObj &fields = BSONObj() ) const; | |
| | | | |
| QueryPattern pattern( const BSONObj &sort = BSONObj() ) const; | | QueryPattern pattern( const BSONObj &sort = BSONObj() ) const; | |
| string getSpecial() const; | | string getSpecial() const; | |
| | | | |
| /** | | /** | |
| | | | |
| skipping to change at line 191 | | skipping to change at line 191 | |
| * in 'this' but not 'other'. | | * in 'this' but not 'other'. | |
| */ | | */ | |
| const FieldRangeSet &operator-=( const FieldRangeSet &other ); | | const FieldRangeSet &operator-=( const FieldRangeSet &other ); | |
| /** @return intersection of 'this' with 'other'. */ | | /** @return intersection of 'this' with 'other'. */ | |
| const FieldRangeSet &operator&=( const FieldRangeSet &other ); | | const FieldRangeSet &operator&=( const FieldRangeSet &other ); | |
| | | | |
| /** | | /** | |
| * @return an ordered list of bounds generated using an index key p
attern | | * @return an ordered list of bounds generated using an index key p
attern | |
| * and traversal direction. | | * and traversal direction. | |
| * | | * | |
|
| | | * The value of matchPossible() should be true, otherwise this func | |
| | | tion | |
| | | * may @throw. | |
| | | * | |
| * NOTE This function is deprecated in the query optimizer and only | | * NOTE This function is deprecated in the query optimizer and only | |
|
| * currently used by the sharding code. | | * currently used by sharding code. | |
| */ | | */ | |
| BoundList indexBounds( const BSONObj &keyPattern, int direction ) c
onst; | | BoundList indexBounds( const BSONObj &keyPattern, int direction ) c
onst; | |
| | | | |
| /** | | /** | |
| * @return - A new FieldRangeSet based on this FieldRangeSet, but w
ith only | | * @return - A new FieldRangeSet based on this FieldRangeSet, but w
ith only | |
| * a subset of the fields. | | * a subset of the fields. | |
| * @param fields - Only fields which are represented as field names
in this object | | * @param fields - Only fields which are represented as field names
in this object | |
| * will be included in the returned FieldRangeSet. | | * will be included in the returned FieldRangeSet. | |
| */ | | */ | |
| FieldRangeSet *subset( const BSONObj &fields ) const; | | FieldRangeSet *subset( const BSONObj &fields ) const; | |
| | | | |
| bool singleKey() const { return _singleKey; } | | bool singleKey() const { return _singleKey; } | |
| | | | |
| BSONObj originalQuery() const { return _queries[ 0 ]; } | | BSONObj originalQuery() const { return _queries[ 0 ]; } | |
| private: | | private: | |
| void appendQueries( const FieldRangeSet &other ); | | void appendQueries( const FieldRangeSet &other ); | |
| void makeEmpty(); | | void makeEmpty(); | |
| void processQueryField( const BSONElement &e, bool optimize ); | | void processQueryField( const BSONElement &e, bool optimize ); | |
| void processOpElement( const char *fieldName, const BSONElement &f,
bool isNot, bool optimize ); | | void processOpElement( const char *fieldName, const BSONElement &f,
bool isNot, bool optimize ); | |
|
| static FieldRange *__singleKeyTrivialRange; | | static FieldRange *__singleKeyUniversalRange; | |
| static FieldRange *__multiKeyTrivialRange; | | static FieldRange *__multiKeyUniversalRange; | |
| const FieldRange &trivialRange() const; | | const FieldRange &universalRange() const; | |
| map<string,FieldRange> _ranges; | | map<string,FieldRange> _ranges; | |
| const char *_ns; | | const char *_ns; | |
| // Owns memory for FieldRange BSONElements. | | // Owns memory for FieldRange BSONElements. | |
| vector<BSONObj> _queries; | | vector<BSONObj> _queries; | |
| bool _singleKey; | | bool _singleKey; | |
| }; | | }; | |
| | | | |
| class NamespaceDetails; | | class NamespaceDetails; | |
| | | | |
| /** | | /** | |
| | | | |
| skipping to change at line 248 | | skipping to change at line 251 | |
| * @return the appropriate single or multi key FieldRangeSet for th
e specified index. | | * @return the appropriate single or multi key FieldRangeSet for th
e specified index. | |
| * @param idxNo -1 for non index scan. | | * @param idxNo -1 for non index scan. | |
| */ | | */ | |
| const FieldRangeSet &frsForIndex( const NamespaceDetails* nsd, int
idxNo ) const; | | const FieldRangeSet &frsForIndex( const NamespaceDetails* nsd, int
idxNo ) const; | |
| | | | |
| /** @return a field range in the single key FieldRangeSet. */ | | /** @return a field range in the single key FieldRangeSet. */ | |
| const FieldRange &singleKeyRange( const char *fieldName ) const { | | const FieldRange &singleKeyRange( const char *fieldName ) const { | |
| return _singleKey.range( fieldName ); | | return _singleKey.range( fieldName ); | |
| } | | } | |
| /** @return true if the range limits are equivalent to an empty que
ry. */ | | /** @return true if the range limits are equivalent to an empty que
ry. */ | |
|
| bool noNontrivialRanges() const; | | bool noNonUniversalRanges() const; | |
| /** @return false if a match is impossible regardless of index. */ | | /** @return false if a match is impossible regardless of index. */ | |
| bool matchPossible() const { return _multiKey.matchPossible(); } | | bool matchPossible() const { return _multiKey.matchPossible(); } | |
| /** | | /** | |
| * @return false if a match is impossible on the specified index. | | * @return false if a match is impossible on the specified index. | |
| * @param idxNo -1 for non index scan. | | * @param idxNo -1 for non index scan. | |
| */ | | */ | |
| bool matchPossibleForIndex( NamespaceDetails *d, int idxNo, const B
SONObj &keyPattern ) const; | | bool matchPossibleForIndex( NamespaceDetails *d, int idxNo, const B
SONObj &keyPattern ) const; | |
| | | | |
| const char *ns() const { return _singleKey.ns(); } | | const char *ns() const { return _singleKey.ns(); } | |
| | | | |
| | | | |
| skipping to change at line 291 | | skipping to change at line 294 | |
| BSONObj simplifiedQueryForIndex( NamespaceDetails *d, int idxNo, co
nst BSONObj &keyPattern ) const; | | BSONObj simplifiedQueryForIndex( NamespaceDetails *d, int idxNo, co
nst BSONObj &keyPattern ) const; | |
| FieldRangeSet _singleKey; | | FieldRangeSet _singleKey; | |
| FieldRangeSet _multiKey; | | FieldRangeSet _multiKey; | |
| friend class OrRangeGenerator; | | friend class OrRangeGenerator; | |
| friend struct QueryUtilIndexed; | | friend struct QueryUtilIndexed; | |
| }; | | }; | |
| | | | |
| class IndexSpec; | | class IndexSpec; | |
| | | | |
| /** | | /** | |
|
| * An ordered list of fields and their FieldRanges, correspoinding to v
alid | | * An ordered list of fields and their FieldRanges, corresponding to va
lid | |
| * index keys for a given index spec. | | * index keys for a given index spec. | |
| */ | | */ | |
| class FieldRangeVector { | | class FieldRangeVector { | |
| public: | | public: | |
| /** | | /** | |
|
| * @param frs The valid ranges for all fields, as defined by the qu | | * @param frs The valid ranges for all fields, as defined by the qu | |
| ery spec | | ery spec. None of the | |
| | | * fields in indexSpec may be empty() ranges of frs. | |
| * @param indexSpec The index spec (key pattern and info) | | * @param indexSpec The index spec (key pattern and info) | |
| * @param direction The direction of index traversal | | * @param direction The direction of index traversal | |
| */ | | */ | |
| FieldRangeVector( const FieldRangeSet &frs, const IndexSpec &indexS
pec, int direction ); | | FieldRangeVector( const FieldRangeSet &frs, const IndexSpec &indexS
pec, int direction ); | |
| | | | |
| /** @return the number of index ranges represented by 'this' */ | | /** @return the number of index ranges represented by 'this' */ | |
| long long size(); | | long long size(); | |
| /** @return starting point for an index traversal. */ | | /** @return starting point for an index traversal. */ | |
| BSONObj startKey() const; | | BSONObj startKey() const; | |
| /** @return end point for an index traversal. */ | | /** @return end point for an index traversal. */ | |
| BSONObj endKey() const; | | BSONObj endKey() const; | |
| /** @return a client readable representation of 'this' */ | | /** @return a client readable representation of 'this' */ | |
| BSONObj obj() const; | | BSONObj obj() const; | |
| | | | |
|
| | | const IndexSpec& getSpec(){ return _indexSpec; } | |
| | | | |
| /** | | /** | |
| * @return true iff the provided document matches valid ranges on a
ll | | * @return true iff the provided document matches valid ranges on a
ll | |
| * of this FieldRangeVector's fields, which is the case iff this do
cument | | * of this FieldRangeVector's fields, which is the case iff this do
cument | |
| * would be returned while scanning the index corresponding to this | | * would be returned while scanning the index corresponding to this | |
| * FieldRangeVector. This function is used for $or clause deduping
. | | * FieldRangeVector. This function is used for $or clause deduping
. | |
| */ | | */ | |
| bool matches( const BSONObj &obj ) const; | | bool matches( const BSONObj &obj ) const; | |
| | | | |
| /** | | /** | |
| * @return first key of 'obj' that would be encountered by a forwar
d | | * @return first key of 'obj' that would be encountered by a forwar
d | |
| * index scan using this FieldRangeVector, BSONObj() if no such key
. | | * index scan using this FieldRangeVector, BSONObj() if no such key
. | |
| */ | | */ | |
| BSONObj firstMatch( const BSONObj &obj ) const; | | BSONObj firstMatch( const BSONObj &obj ) const; | |
| | | | |
| private: | | private: | |
| int matchingLowElement( const BSONElement &e, int i, bool direction
, bool &lowEquality ) const; | | int matchingLowElement( const BSONElement &e, int i, bool direction
, bool &lowEquality ) const; | |
| bool matchesElement( const BSONElement &e, int i, bool direction )
const; | | bool matchesElement( const BSONElement &e, int i, bool direction )
const; | |
| bool matchesKey( const BSONObj &key ) const; | | bool matchesKey( const BSONObj &key ) const; | |
| vector<FieldRange> _ranges; | | vector<FieldRange> _ranges; | |
|
| IndexSpec _indexSpec; | | const IndexSpec _indexSpec; | |
| int _direction; | | int _direction; | |
| vector<BSONObj> _queries; // make sure mem owned | | vector<BSONObj> _queries; // make sure mem owned | |
| friend class FieldRangeVectorIterator; | | friend class FieldRangeVectorIterator; | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * Helper class for iterating through an ordered representation of keys | | * Helper class for iterating through an ordered representation of keys | |
| * to find those keys that match a specified FieldRangeVector. | | * to find those keys that match a specified FieldRangeVector. | |
| */ | | */ | |
| class FieldRangeVectorIterator { | | class FieldRangeVectorIterator { | |
| | | | |
| skipping to change at line 391 | | skipping to change at line 397 | |
| | | | |
| /** | | /** | |
| * As we iterate through $or clauses this class generates a FieldRangeS
etPair | | * As we iterate through $or clauses this class generates a FieldRangeS
etPair | |
| * for the current $or clause, in some cases by excluding ranges that w
ere | | * for the current $or clause, in some cases by excluding ranges that w
ere | |
| * included in a previous clause. | | * included in a previous clause. | |
| */ | | */ | |
| class OrRangeGenerator { | | class OrRangeGenerator { | |
| public: | | public: | |
| OrRangeGenerator( const char *ns, const BSONObj &query , bool optim
ize=true ); | | OrRangeGenerator( const char *ns, const BSONObj &query , bool optim
ize=true ); | |
| | | | |
|
| /** | | /** @return true iff we are done scanning $or clauses, or if there | |
| * @return true iff we are done scanning $or clauses. if there's a | | are no $or clauses. */ | |
| * useless or clause, we won't use or index ranges to help with sca | | bool orRangesExhausted() const { return _orSets.empty(); } | |
| nning. | | | |
| */ | | | |
| bool orFinished() const { return _orFound && _orSets.empty(); } | | | |
| /** Iterates to the next $or clause by removing the current $or cla
use. */ | | /** Iterates to the next $or clause by removing the current $or cla
use. */ | |
| void popOrClause( NamespaceDetails *nsd, int idxNo, const BSONObj &
keyPattern ); | | void popOrClause( NamespaceDetails *nsd, int idxNo, const BSONObj &
keyPattern ); | |
| void popOrClauseSingleKey(); | | void popOrClauseSingleKey(); | |
| /** @return FieldRangeSetPair for the current $or clause. */ | | /** @return FieldRangeSetPair for the current $or clause. */ | |
| FieldRangeSetPair *topFrsp() const; | | FieldRangeSetPair *topFrsp() const; | |
| /** | | /** | |
| * @return original FieldRangeSetPair for the current $or clause. W
hile the | | * @return original FieldRangeSetPair for the current $or clause. W
hile the | |
| * original bounds are looser, they are composed of fewer ranges an
d it | | * original bounds are looser, they are composed of fewer ranges an
d it | |
| * is faster to do operations with them; when they can be used inst
ead of | | * is faster to do operations with them; when they can be used inst
ead of | |
| * more precise bounds, they should. | | * more precise bounds, they should. | |
| */ | | */ | |
| FieldRangeSetPair *topFrspOriginal() const; | | FieldRangeSetPair *topFrspOriginal() const; | |
| | | | |
| string getSpecial() const { return _baseSet.getSpecial(); } | | string getSpecial() const { return _baseSet.getSpecial(); } | |
|
| | | | |
| bool moreOrClauses() const { return !_orSets.empty(); } | | | |
| private: | | private: | |
| void assertMayPopOrClause(); | | void assertMayPopOrClause(); | |
|
| void popOrClause( const FieldRangeSet *toDiff, NamespaceDetails *d
= 0, int idxNo = -1, const BSONObj &keyPattern = BSONObj() ); | | void _popOrClause( const FieldRangeSet *toDiff, NamespaceDetails *d
, int idxNo, const BSONObj &keyPattern ); | |
| FieldRangeSetPair _baseSet; | | FieldRangeSetPair _baseSet; | |
| list<FieldRangeSetPair> _orSets; | | list<FieldRangeSetPair> _orSets; | |
| list<FieldRangeSetPair> _originalOrSets; | | list<FieldRangeSetPair> _originalOrSets; | |
| // ensure memory is owned | | // ensure memory is owned | |
| list<FieldRangeSetPair> _oldOrSets; | | list<FieldRangeSetPair> _oldOrSets; | |
| bool _orFound; | | bool _orFound; | |
| friend struct QueryUtilIndexed; | | friend struct QueryUtilIndexed; | |
| }; | | }; | |
| | | | |
| /** returns a string that when used as a matcher, would match a super s
et of regex() | | /** returns a string that when used as a matcher, would match a super s
et of regex() | |
| | | | |
| skipping to change at line 437 | | skipping to change at line 438 | |
| | | | |
| if purePrefix != NULL, sets it to whether the regex can be converte
d to a range query | | if purePrefix != NULL, sets it to whether the regex can be converte
d to a range query | |
| */ | | */ | |
| string simpleRegex(const char* regex, const char* flags, bool* purePref
ix=NULL); | | string simpleRegex(const char* regex, const char* flags, bool* purePref
ix=NULL); | |
| | | | |
| /** returns the upper bound of a query that matches prefix */ | | /** returns the upper bound of a query that matches prefix */ | |
| string simpleRegexEnd( string prefix ); | | string simpleRegexEnd( string prefix ); | |
| | | | |
| long long applySkipLimit( long long num , const BSONObj& cmd ); | | long long applySkipLimit( long long num , const BSONObj& cmd ); | |
| | | | |
|
| | | bool isSimpleIdQuery( const BSONObj& query ); | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
| #include "queryutil-inl.h" | | #include "queryutil-inl.h" | |
| | | | |
End of changes. 17 change blocks. |
| 33 lines changed or deleted | | 34 lines changed or added | |
|
| rs.h | | rs.h | |
| | | | |
| skipping to change at line 26 | | skipping to change at line 26 | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "../../util/concurrency/list.h" | | #include "../../util/concurrency/list.h" | |
| #include "../../util/concurrency/value.h" | | #include "../../util/concurrency/value.h" | |
| #include "../../util/concurrency/msg.h" | | #include "../../util/concurrency/msg.h" | |
| #include "../../util/net/hostandport.h" | | #include "../../util/net/hostandport.h" | |
| #include "../commands.h" | | #include "../commands.h" | |
|
| | | #include "../oplog.h" | |
| #include "../oplogreader.h" | | #include "../oplogreader.h" | |
| #include "rs_exception.h" | | #include "rs_exception.h" | |
| #include "rs_optime.h" | | #include "rs_optime.h" | |
| #include "rs_member.h" | | #include "rs_member.h" | |
| #include "rs_config.h" | | #include "rs_config.h" | |
| | | | |
| /** | | /** | |
| * Order of Events | | * Order of Events | |
| * | | * | |
| * On startup, if the --replSet option is present, startReplSets is called. | | * On startup, if the --replSet option is present, startReplSets is called. | |
| | | | |
| skipping to change at line 61 | | skipping to change at line 62 | |
| extern bool replSet; // true if using repl sets | | extern bool replSet; // true if using repl sets | |
| extern class ReplSet *theReplSet; // null until initialized | | extern class ReplSet *theReplSet; // null until initialized | |
| extern Tee *rsLog; | | extern Tee *rsLog; | |
| | | | |
| /* member of a replica set */ | | /* member of a replica set */ | |
| class Member : public List1<Member>::Base { | | class Member : public List1<Member>::Base { | |
| private: | | private: | |
| ~Member(); // intentionally unimplemented as should never be called
-- see List1<>::Base. | | ~Member(); // intentionally unimplemented as should never be called
-- see List1<>::Base. | |
| Member(const Member&); | | Member(const Member&); | |
| public: | | public: | |
|
| Member(HostAndPort h, unsigned ord, ReplSetConfig::MemberCfg *c, bo
ol self); | | Member(HostAndPort h, unsigned ord, const ReplSetConfig::MemberCfg
*c, bool self); | |
| | | | |
| string fullName() const { return h().toString(); } | | string fullName() const { return h().toString(); } | |
| const ReplSetConfig::MemberCfg& config() const { return _config; } | | const ReplSetConfig::MemberCfg& config() const { return _config; } | |
| ReplSetConfig::MemberCfg& configw() { return _config; } | | ReplSetConfig::MemberCfg& configw() { return _config; } | |
| const HeartbeatInfo& hbinfo() const { return _hbinfo; } | | const HeartbeatInfo& hbinfo() const { return _hbinfo; } | |
| HeartbeatInfo& get_hbinfo() { return _hbinfo; } | | HeartbeatInfo& get_hbinfo() { return _hbinfo; } | |
| string lhb() const { return _hbinfo.lastHeartbeatMsg; } | | string lhb() const { return _hbinfo.lastHeartbeatMsg; } | |
| MemberState state() const { return _hbinfo.hbstate; } | | MemberState state() const { return _hbinfo.hbstate; } | |
| const HostAndPort& h() const { return _h; } | | const HostAndPort& h() const { return _h; } | |
| unsigned id() const { return _hbinfo.id(); } | | unsigned id() const { return _hbinfo.id(); } | |
| | | | |
| skipping to change at line 83 | | skipping to change at line 84 | |
| bool potentiallyHot() const { return _config.potentiallyHot(); } //
not arbiter, not priority 0 | | bool potentiallyHot() const { return _config.potentiallyHot(); } //
not arbiter, not priority 0 | |
| void summarizeMember(stringstream& s) const; | | void summarizeMember(stringstream& s) const; | |
| | | | |
| private: | | private: | |
| friend class ReplSetImpl; | | friend class ReplSetImpl; | |
| ReplSetConfig::MemberCfg _config; | | ReplSetConfig::MemberCfg _config; | |
| const HostAndPort _h; | | const HostAndPort _h; | |
| HeartbeatInfo _hbinfo; | | HeartbeatInfo _hbinfo; | |
| }; | | }; | |
| | | | |
|
| | | namespace replset { | |
| | | /** | |
| | | * "Normal" replica set syncing | |
| | | */ | |
| | | class SyncTail : public Sync { | |
| | | public: | |
| | | virtual ~SyncTail() {} | |
| | | SyncTail(const string& host) : Sync(host) {} | |
| | | virtual bool syncApply(const BSONObj &o); | |
| | | }; | |
| | | | |
| | | /** | |
| | | * Initial clone and sync | |
| | | */ | |
| | | class InitialSync : public SyncTail { | |
| | | public: | |
| | | InitialSync(const string& host) : SyncTail(host) {} | |
| | | virtual ~InitialSync() {} | |
| | | bool oplogApplication(OplogReader& r, const Member* source, con | |
| | | st OpTime& applyGTE, const OpTime& minValid); | |
| | | virtual void applyOp(const BSONObj& o, const OpTime& minvalid); | |
| | | }; | |
| | | | |
| | | // TODO: move hbmsg into an error-keeping class (SERVER-4444) | |
| | | void sethbmsg(const string& s, const int logLevel=0); | |
| | | | |
| | | } // namespace replset | |
| | | | |
| class Manager : public task::Server { | | class Manager : public task::Server { | |
| ReplSetImpl *rs; | | ReplSetImpl *rs; | |
| bool busyWithElectSelf; | | bool busyWithElectSelf; | |
| int _primary; | | int _primary; | |
| | | | |
| /** @param two - if true two primaries were seen. this can happen
transiently, in addition to our | | /** @param two - if true two primaries were seen. this can happen
transiently, in addition to our | |
| polling being only occasional. in this case null
is returned, but the caller should | | polling being only occasional. in this case null
is returned, but the caller should | |
| not assume primary itself in that situation. | | not assume primary itself in that situation. | |
| */ | | */ | |
| const Member* findOtherPrimary(bool& two); | | const Member* findOtherPrimary(bool& two); | |
| | | | |
| skipping to change at line 106 | | skipping to change at line 134 | |
| void checkAuth(); | | void checkAuth(); | |
| virtual void starting(); | | virtual void starting(); | |
| public: | | public: | |
| Manager(ReplSetImpl *rs); | | Manager(ReplSetImpl *rs); | |
| virtual ~Manager(); | | virtual ~Manager(); | |
| void msgReceivedNewConfig(BSONObj); | | void msgReceivedNewConfig(BSONObj); | |
| void msgCheckNewState(); | | void msgCheckNewState(); | |
| }; | | }; | |
| | | | |
| class GhostSync : public task::Server { | | class GhostSync : public task::Server { | |
|
| struct GhostSlave { | | struct GhostSlave : boost::noncopyable { | |
| GhostSlave() : last(0), slave(0), init(false) {} | | GhostSlave() : last(0), slave(0), init(false) { } | |
| OplogReader reader; | | OplogReader reader; | |
| OpTime last; | | OpTime last; | |
| Member* slave; | | Member* slave; | |
| bool init; | | bool init; | |
| }; | | }; | |
| /** | | /** | |
| * This is a cache of ghost slaves | | * This is a cache of ghost slaves | |
| */ | | */ | |
|
| typedef map<mongo::OID,GhostSlave> MAP; | | typedef map< mongo::OID,shared_ptr<GhostSlave> > MAP; | |
| MAP _ghostCache; | | MAP _ghostCache; | |
| RWLock _lock; // protects _ghostCache | | RWLock _lock; // protects _ghostCache | |
| ReplSetImpl *rs; | | ReplSetImpl *rs; | |
| virtual void starting(); | | virtual void starting(); | |
| public: | | public: | |
| GhostSync(ReplSetImpl *_rs) : task::Server("rsGhostSync"), _lock("G
hostSync"), rs(_rs) {} | | GhostSync(ReplSetImpl *_rs) : task::Server("rsGhostSync"), _lock("G
hostSync"), rs(_rs) {} | |
| ~GhostSync() { | | ~GhostSync() { | |
| log() << "~GhostSync() called" << rsLog; | | log() << "~GhostSync() called" << rsLog; | |
| } | | } | |
| | | | |
| | | | |
| skipping to change at line 155 | | skipping to change at line 183 | |
| | | | |
| struct Target; | | struct Target; | |
| | | | |
| class Consensus { | | class Consensus { | |
| ReplSetImpl &rs; | | ReplSetImpl &rs; | |
| struct LastYea { | | struct LastYea { | |
| LastYea() : when(0), who(0xffffffff) { } | | LastYea() : when(0), who(0xffffffff) { } | |
| time_t when; | | time_t when; | |
| unsigned who; | | unsigned who; | |
| }; | | }; | |
|
| static mutex lyMutex; | | static SimpleMutex lyMutex; | |
| Guarded<LastYea,lyMutex> ly; | | Guarded<LastYea,lyMutex> ly; | |
| unsigned yea(unsigned memberId); // throws VoteException | | unsigned yea(unsigned memberId); // throws VoteException | |
| void electionFailed(unsigned meid); | | void electionFailed(unsigned meid); | |
| void _electSelf(); | | void _electSelf(); | |
| bool weAreFreshest(bool& allUp, int& nTies); | | bool weAreFreshest(bool& allUp, int& nTies); | |
| bool sleptLast; // slept last elect() pass | | bool sleptLast; // slept last elect() pass | |
| public: | | public: | |
| Consensus(ReplSetImpl *t) : rs(*t) { | | Consensus(ReplSetImpl *t) : rs(*t) { | |
| sleptLast = false; | | sleptLast = false; | |
| steppedDown = 0; | | steppedDown = 0; | |
| | | | |
| skipping to change at line 349 | | skipping to change at line 377 | |
| bool _stepDown(int secs); | | bool _stepDown(int secs); | |
| bool _freeze(int secs); | | bool _freeze(int secs); | |
| private: | | private: | |
| void assumePrimary(); | | void assumePrimary(); | |
| void loadLastOpTimeWritten(bool quiet=false); | | void loadLastOpTimeWritten(bool quiet=false); | |
| void changeState(MemberState s); | | void changeState(MemberState s); | |
| | | | |
| /** | | /** | |
| * Find the closest member (using ping time) with a higher latest o
ptime. | | * Find the closest member (using ping time) with a higher latest o
ptime. | |
| */ | | */ | |
|
| const Member* getMemberToSyncTo(); | | Member* getMemberToSyncTo(); | |
| | | void veto(const string& host, unsigned secs=10); | |
| Member* _currentSyncTarget; | | Member* _currentSyncTarget; | |
| | | | |
| bool _blockSync; | | bool _blockSync; | |
| void blockSync(bool block); | | void blockSync(bool block); | |
| | | | |
| // set of electable members' _ids | | // set of electable members' _ids | |
| set<unsigned> _electableSet; | | set<unsigned> _electableSet; | |
| protected: | | protected: | |
| // "heartbeat message" | | // "heartbeat message" | |
| // sent in requestHeartbeat respond in field "hbm" | | // sent in requestHeartbeat respond in field "hbm" | |
| | | | |
| skipping to change at line 491 | | skipping to change at line 520 | |
| void getTargets(list<Target>&, int &configVersion); | | void getTargets(list<Target>&, int &configVersion); | |
| void startThreads(); | | void startThreads(); | |
| friend class FeedbackThread; | | friend class FeedbackThread; | |
| friend class CmdReplSetElect; | | friend class CmdReplSetElect; | |
| friend class Member; | | friend class Member; | |
| friend class Manager; | | friend class Manager; | |
| friend class GhostSync; | | friend class GhostSync; | |
| friend class Consensus; | | friend class Consensus; | |
| | | | |
| private: | | private: | |
|
| /* pulling data from primary related - see rs_sync.cpp */ | | bool initialSyncOplogApplication(const OpTime& applyGTE, const OpTi | |
| bool initialSyncOplogApplication(const Member *primary, OpTime appl | | me& minValid); | |
| yGTE, OpTime minValid); | | | |
| void _syncDoInitialSync(); | | void _syncDoInitialSync(); | |
| void syncDoInitialSync(); | | void syncDoInitialSync(); | |
| void _syncThread(); | | void _syncThread(); | |
| bool tryToGoLiveAsASecondary(OpTime&); // readlocks | | bool tryToGoLiveAsASecondary(OpTime&); // readlocks | |
| void syncTail(); | | void syncTail(); | |
|
| bool syncApply(const BSONObj &o); | | | |
| unsigned _syncRollback(OplogReader& r); | | unsigned _syncRollback(OplogReader& r); | |
| void syncRollback(OplogReader& r); | | void syncRollback(OplogReader& r); | |
| void syncFixUp(HowToFixUp& h, OplogReader& r); | | void syncFixUp(HowToFixUp& h, OplogReader& r); | |
|
| bool _getOplogReader(OplogReader& r, string& hn); | | | |
| bool _isStale(OplogReader& r, const string& hn); | | // get an oplog reader for a server with an oplog entry timestamp g | |
| | | reater | |
| | | // than or equal to minTS, if set. | |
| | | Member* _getOplogReader(OplogReader& r, const OpTime& minTS); | |
| | | | |
| | | // check lastOpTimeWritten against the remote's earliest op, fillin | |
| | | g in | |
| | | // remoteOldestOp. | |
| | | bool _isStale(OplogReader& r, const OpTime& minTS, BSONObj& remoteO | |
| | | ldestOp); | |
| | | | |
| | | // keep a list of hosts that we've tried recently that didn't work | |
| | | map<string,time_t> _veto; | |
| public: | | public: | |
| void syncThread(); | | void syncThread(); | |
| const OpTime lastOtherOpTime() const; | | const OpTime lastOtherOpTime() const; | |
| }; | | }; | |
| | | | |
| class ReplSet : public ReplSetImpl { | | class ReplSet : public ReplSetImpl { | |
| public: | | public: | |
| ReplSet(ReplSetCmdline& replSetCmdline) : ReplSetImpl(replSetCmdlin
e) { } | | ReplSet(ReplSetCmdline& replSetCmdline) : ReplSetImpl(replSetCmdlin
e) { } | |
| | | | |
| // for the replSetStepDown command | | // for the replSetStepDown command | |
| | | | |
| skipping to change at line 597 | | skipping to change at line 633 | |
| errmsg = "replSet command unauthorized"; | | errmsg = "replSet command unauthorized"; | |
| return false; | | return false; | |
| } | | } | |
| } | | } | |
| return true; | | return true; | |
| } | | } | |
| | | | |
| bool check(string& errmsg, BSONObjBuilder& result) { | | bool check(string& errmsg, BSONObjBuilder& result) { | |
| if( !replSet ) { | | if( !replSet ) { | |
| errmsg = "not running with --replSet"; | | errmsg = "not running with --replSet"; | |
|
| | | if( cmdLine.configsvr ) { | |
| | | result.append("info", "configsvr"); // for shell prompt | |
| | | } | |
| return false; | | return false; | |
| } | | } | |
| | | | |
| if( theReplSet == 0 ) { | | if( theReplSet == 0 ) { | |
| result.append("startupStatus", ReplSet::startupStatus); | | result.append("startupStatus", ReplSet::startupStatus); | |
| string s; | | string s; | |
| errmsg = ReplSet::startupStatusMsg.empty() ? "replset unkno
wn error 2" : ReplSet::startupStatusMsg.get(); | | errmsg = ReplSet::startupStatusMsg.empty() ? "replset unkno
wn error 2" : ReplSet::startupStatusMsg.get(); | |
| if( ReplSet::startupStatus == 3 ) | | if( ReplSet::startupStatus == 3 ) | |
| result.append("info", "run rs.initiate(...) if not yet
done for the set"); | | result.append("info", "run rs.initiate(...) if not yet
done for the set"); | |
| return false; | | return false; | |
| | | | |
| skipping to change at line 621 | | skipping to change at line 660 | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * does local authentication | | * does local authentication | |
| * directly authorizes against AuthenticationInfo | | * directly authorizes against AuthenticationInfo | |
| */ | | */ | |
| void replLocalAuth(); | | void replLocalAuth(); | |
| | | | |
| /** inlines ----------------- */ | | /** inlines ----------------- */ | |
| | | | |
|
| inline Member::Member(HostAndPort h, unsigned ord, ReplSetConfig::Membe
rCfg *c, bool self) : | | inline Member::Member(HostAndPort h, unsigned ord, const ReplSetConfig:
:MemberCfg *c, bool self) : | |
| _config(*c), _h(h), _hbinfo(ord) { | | _config(*c), _h(h), _hbinfo(ord) { | |
| assert(c); | | assert(c); | |
| if( self ) | | if( self ) | |
| _hbinfo.health = 1.0; | | _hbinfo.health = 1.0; | |
| } | | } | |
| | | | |
| } | | } | |
| | | | |
End of changes. 12 change blocks. |
| 13 lines changed or deleted | | 56 lines changed or added | |
|
| rwlock.h | | rwlock.h | |
| | | | |
| skipping to change at line 23 | | skipping to change at line 23 | |
| * GNU Affero General Public License for more details. | | * GNU Affero General Public License for more details. | |
| * | | * | |
| * You should have received a copy of the GNU Affero General Public Lice
nse | | * You should have received a copy of the GNU Affero General Public Lice
nse | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "mutex.h" | | #include "mutex.h" | |
| #include "../time_support.h" | | #include "../time_support.h" | |
|
| | | #include "rwlockimpl.h" | |
| | | | |
|
| // this requires newer windows versions | | #if defined(_DEBUG) | |
| // it works better than sharable_mutex under high contention | | #include "mutexdebugger.h" | |
| #if defined(_WIN64) | | | |
| //#define MONGO_USE_SRW_ON_WINDOWS 1 | | | |
| #endif | | | |
| | | | |
| #if !defined(MONGO_USE_SRW_ON_WINDOWS) | | | |
| | | | |
| #if BOOST_VERSION >= 103500 | | | |
| # define BOOST_RWLOCK | | | |
| #else | | | |
| # if defined(_WIN32) | | | |
| # error need boost >= 1.35 for windows | | | |
| # endif | | | |
| # include <pthread.h> | | | |
| #endif | | | |
| | | | |
| #if defined(_WIN32) | | | |
| # include "shared_mutex_win.hpp" | | | |
| namespace mongo { | | | |
| typedef boost::modified_shared_mutex shared_mutex; | | | |
| } | | | |
| # undef assert | | | |
| # define assert MONGO_assert | | | |
| #elif defined(BOOST_RWLOCK) | | | |
| # include <boost/thread/shared_mutex.hpp> | | | |
| # undef assert | | | |
| # define assert MONGO_assert | | | |
| #endif | | | |
| | | | |
| #endif | | #endif | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| #if defined(MONGO_USE_SRW_ON_WINDOWS) && defined(_WIN32) | | /** separated out as later the implementation of this may be different | |
| | | than RWLock, | |
| // Windows RWLock implementation (requires newer versions of windows th | | depending on OS, as there is no upgrade etc. facility herein. | |
| us the above macro) | | */ | |
| class RWLock : boost::noncopyable { | | class SimpleRWLock : public RWLockBase { | |
| public: | | public: | |
|
| RWLock(const char *, int lowPriorityWaitMS=0 ) : _lowPriorityWaitMS | | explicit SimpleRWLock(const char *) { } | |
| (lowPriorityWaitMS) | | SimpleRWLock() { } | |
| { InitializeSRWLock(&_lock); } | | void lock() { RWLockBase::lock(); } | |
| ~RWLock() { } | | void unlock() { RWLockBase::unlock(); } | |
| const char * implType() const { return "WINSRW"; } | | void lock_shared() { RWLockBase::lock_shared(); } | |
| int lowPriorityWaitMS() const { return _lowPriorityWaitMS; } | | void unlock_shared() { RWLockBase::unlock_shared(); } | |
| void lock() { AcquireSRWLockExclusive(&_lock); } | | class Shared : boost::noncopyable { | |
| void unlock() { ReleaseSRWLockExclusive(&_lock); } | | SimpleRWLock& _r; | |
| void lock_shared() { AcquireSRWLockShared(&_lock); } | | public: | |
| void unlock_shared() { ReleaseSRWLockShared(&_lock); } | | Shared(SimpleRWLock& rwlock) : _r(rwlock) {_r.lock_shared(); } | |
| bool lock_shared_try( int millis ) { | | ~Shared() { _r.unlock_shared(); } | |
| if( TryAcquireSRWLockShared(&_lock) ) | | }; | |
| return true; | | class Exclusive : boost::noncopyable { | |
| if( millis == 0 ) | | SimpleRWLock& _r; | |
| return false; | | public: | |
| unsigned long long end = curTimeMicros64() + millis*1000; | | Exclusive(SimpleRWLock& rwlock) : _r(rwlock) {_r.lock(); } | |
| while( 1 ) { | | ~Exclusive() { _r.unlock(); } | |
| Sleep(1); | | }; | |
| if( TryAcquireSRWLockShared(&_lock) ) | | | |
| return true; | | | |
| if( curTimeMicros64() >= end ) | | | |
| break; | | | |
| } | | | |
| return false; | | | |
| } | | | |
| bool lock_try( int millis = 0 ) { | | | |
| if( TryAcquireSRWLockExclusive(&_lock) ) // quick check to opti | | | |
| mistically avoid calling curTimeMicros64 | | | |
| return true; | | | |
| if( millis == 0 ) | | | |
| return false; | | | |
| unsigned long long end = curTimeMicros64() + millis*1000; | | | |
| do { | | | |
| Sleep(1); | | | |
| if( TryAcquireSRWLockExclusive(&_lock) ) | | | |
| return true; | | | |
| } while( curTimeMicros64() < end ); | | | |
| return false; | | | |
| } | | | |
| private: | | | |
| SRWLOCK _lock; | | | |
| const int _lowPriorityWaitMS; | | | |
| }; | | }; | |
| | | | |
|
| #elif defined(BOOST_RWLOCK) | | class RWLock : public RWLockBase { | |
| | | enum { NilState, UpgradableState, Exclusive } x; // only bother to | |
| // Boost based RWLock implementation | | set when doing upgradable related things | |
| class RWLock : boost::noncopyable { | | | |
| shared_mutex _m; | | | |
| const int _lowPriorityWaitMS; | | | |
| public: | | public: | |
| const char * const _name; | | const char * const _name; | |
|
| | | RWLock(const char *name) : _name(name) { | |
| RWLock(const char *name, int lowPriorityWait=0) : _lowPriorityWaitM | | x = NilState; | |
| S(lowPriorityWait) , _name(name) { } | | } | |
| | | | |
| const char * implType() const { return "boost"; } | | | |
| | | | |
| int lowPriorityWaitMS() const { return _lowPriorityWaitMS; } | | | |
| | | | |
| void lock() { | | void lock() { | |
|
| _m.lock(); | | RWLockBase::lock(); | |
| DEV mutexDebugger.entering(_name); | | #if defined(_DEBUG) | |
| | | mutexDebugger.entering(_name); | |
| | | #endif | |
| } | | } | |
|
| | | | |
| /*void lock() { | | | |
| // This sequence gives us the lock semantics we want: specifica | | | |
| lly that write lock acquisition is | | | |
| // greedy EXCEPT when someone already is in upgradable state. | | | |
| lockAsUpgradable(); | | | |
| upgrade(); | | | |
| DEV mutexDebugger.entering(_name); | | | |
| }*/ | | | |
| | | | |
| void unlock() { | | void unlock() { | |
|
| DEV mutexDebugger.leaving(_name); | | #if defined(_DEBUG) | |
| _m.unlock(); | | mutexDebugger.leaving(_name); | |
| | | #endif | |
| | | RWLockBase::unlock(); | |
| } | | } | |
| | | | |
|
| void lockAsUpgradable() { | | void lock_shared() { RWLockBase::lock_shared(); } | |
| _m.lock_upgrade(); | | void unlock_shared() { RWLockBase::unlock_shared(); } | |
| } | | private: | |
| | | void lockAsUpgradable() { RWLockBase::lockAsUpgradable(); } | |
| void unlockFromUpgradable() { // upgradable -> unlocked | | void unlockFromUpgradable() { // upgradable -> unlocked | |
|
| _m.unlock_upgrade(); | | RWLockBase::unlockFromUpgradable(); | |
| } | | | |
| void upgrade() { // upgradable -> exclusive lock | | | |
| _m.unlock_upgrade_and_lock(); | | | |
| } | | | |
| | | | |
| void lock_shared() { | | | |
| _m.lock_shared(); | | | |
| } | | | |
| void unlock_shared() { | | | |
| _m.unlock_shared(); | | | |
| } | | | |
| | | | |
| bool lock_shared_try( int millis ) { | | | |
| if( _m.timed_lock_shared( boost::posix_time::milliseconds(milli | | | |
| s) ) ) { | | | |
| return true; | | | |
| } | | | |
| return false; | | | |
| } | | | |
| | | | |
| bool lock_try( int millis = 0 ) { | | | |
| if( _m.timed_lock( boost::posix_time::milliseconds(millis) ) ) | | | |
| { | | | |
| DEV mutexDebugger.entering(_name); | | | |
| return true; | | | |
| } | | | |
| return false; | | | |
| } | | } | |
|
| }; | | | |
| | | | |
| #else | | | |
| | | | |
| // Posix RWLock implementation | | | |
| class RWLock : boost::noncopyable { | | | |
| pthread_rwlock_t _lock; | | | |
| const int _lowPriorityWaitMS; | | | |
| static void check( int x ) { | | | |
| if( MONGO_likely(x == 0) ) | | | |
| return; | | | |
| log() << "pthread rwlock failed: " << x << endl; | | | |
| assert( x == 0 ); | | | |
| } | | | |
| | | | |
| public: | | public: | |
|
| const char *_name; | | void upgrade() { // upgradable -> exclusive lock | |
| RWLock(const char *name, int lowPriorityWaitMS=0) : _lowPriorityWai | | assert( x == UpgradableState ); | |
| tMS(lowPriorityWaitMS), _name(name) | | RWLockBase::upgrade(); | |
| { | | x = Exclusive; | |
| check( pthread_rwlock_init( &_lock , 0 ) ); | | | |
| } | | | |
| | | | |
| ~RWLock() { | | | |
| if ( ! StaticObserver::_destroyingStatics ) { | | | |
| wassert( pthread_rwlock_destroy( &_lock ) == 0 ); // wasser | | | |
| t as don't want to throw from a destructor | | | |
| } | | | |
| } | | | |
| | | | |
| const char * implType() const { return "posix"; } | | | |
| | | | |
| int lowPriorityWaitMS() const { return _lowPriorityWaitMS; } | | | |
| | | | |
| void lock() { | | | |
| check( pthread_rwlock_wrlock( &_lock ) ); | | | |
| DEV mutexDebugger.entering(_name); | | | |
| } | | | |
| void unlock() { | | | |
| DEV mutexDebugger.leaving(_name); | | | |
| check( pthread_rwlock_unlock( &_lock ) ); | | | |
| } | | | |
| | | | |
| void lock_shared() { | | | |
| check( pthread_rwlock_rdlock( &_lock ) ); | | | |
| } | | | |
| | | | |
| void unlock_shared() { | | | |
| check( pthread_rwlock_unlock( &_lock ) ); | | | |
| } | | } | |
| | | | |
|
| bool lock_shared_try( int millis ) { | | bool lock_shared_try( int millis ) { return RWLockBase::lock_shared | |
| return _try( millis , false ); | | _try(millis); } | |
| } | | | |
| | | | |
| bool lock_try( int millis = 0 ) { | | bool lock_try( int millis = 0 ) { | |
|
| if( _try( millis , true ) ) { | | if( RWLockBase::lock_try(millis) ) { | |
| DEV mutexDebugger.entering(_name); | | #if defined(_DEBUG) | |
| | | mutexDebugger.entering(_name); | |
| | | #endif | |
| return true; | | return true; | |
| } | | } | |
| return false; | | return false; | |
| } | | } | |
| | | | |
|
| bool _try( int millis , bool write ) { | | /** acquire upgradable state. You must be unlocked before creating | |
| while ( true ) { | | . | |
| int x = write ? | | unlocks on destruction, whether in upgradable state or upgraded | |
| pthread_rwlock_trywrlock( &_lock ) : | | to exclusive | |
| pthread_rwlock_tryrdlock( &_lock ); | | in the interim. | |
| | | */ | |
| if ( x <= 0 ) { | | class Upgradable : boost::noncopyable { | |
| return true; | | RWLock& _r; | |
| | | public: | |
| | | Upgradable(RWLock& r) : _r(r) { | |
| | | r.lockAsUpgradable(); | |
| | | assert( _r.x == NilState ); | |
| | | _r.x = RWLock::UpgradableState; | |
| | | } | |
| | | ~Upgradable() { | |
| | | if( _r.x == RWLock::UpgradableState ) { | |
| | | _r.x = NilState; | |
| | | _r.unlockFromUpgradable(); | |
| } | | } | |
|
| | | else { | |
| if ( millis-- <= 0 ) | | //TEMP assert( _r.x == Exclusive ); | |
| return false; | | // has been upgraded | |
| | | _r.x = NilState; | |
| if ( x == EBUSY ) { | | _r.unlock(); | |
| sleepmillis(1); | | | |
| continue; | | | |
| } | | } | |
|
| check(x); | | | |
| } | | } | |
|
| | | }; | |
| return false; | | | |
| } | | | |
| | | | |
| }; | | }; | |
| | | | |
|
| #endif | | | |
| | | | |
| /** throws on failure to acquire in the specified time period. */ | | /** throws on failure to acquire in the specified time period. */ | |
| class rwlock_try_write : boost::noncopyable { | | class rwlock_try_write : boost::noncopyable { | |
| public: | | public: | |
| struct exception { }; | | struct exception { }; | |
| rwlock_try_write(RWLock& l, int millis = 0) : _l(l) { | | rwlock_try_write(RWLock& l, int millis = 0) : _l(l) { | |
| if( !l.lock_try(millis) ) | | if( !l.lock_try(millis) ) | |
| throw exception(); | | throw exception(); | |
| } | | } | |
| ~rwlock_try_write() { _l.unlock(); } | | ~rwlock_try_write() { _l.unlock(); } | |
| private: | | private: | |
| | | | |
| skipping to change at line 287 | | skipping to change at line 158 | |
| | | | |
| /* scoped lock for RWLock */ | | /* scoped lock for RWLock */ | |
| class rwlock : boost::noncopyable { | | class rwlock : boost::noncopyable { | |
| public: | | public: | |
| /** | | /** | |
| * @param write acquire write lock if true sharable if false | | * @param write acquire write lock if true sharable if false | |
| * @param lowPriority if > 0, will try to get the lock non-greedily
for that many ms | | * @param lowPriority if > 0, will try to get the lock non-greedily
for that many ms | |
| */ | | */ | |
| rwlock( const RWLock& lock , bool write, /* bool alreadyHaveLock =
false , */int lowPriorityWaitMS = 0 ) | | rwlock( const RWLock& lock , bool write, /* bool alreadyHaveLock =
false , */int lowPriorityWaitMS = 0 ) | |
| : _lock( (RWLock&)lock ) , _write( write ) { | | : _lock( (RWLock&)lock ) , _write( write ) { | |
|
| | | | |
| { | | { | |
| if ( _write ) { | | if ( _write ) { | |
|
| | | _lock.lock(); | |
| if ( ! lowPriorityWaitMS && lock.lowPriorityWaitMS() ) | | | |
| lowPriorityWaitMS = lock.lowPriorityWaitMS(); | | | |
| | | | |
| if ( lowPriorityWaitMS ) { | | | |
| bool got = false; | | | |
| for ( int i=0; i<lowPriorityWaitMS; i++ ) { | | | |
| if ( _lock.lock_try(0) ) { | | | |
| got = true; | | | |
| break; | | | |
| } | | | |
| | | | |
| int sleep = 1; | | | |
| if ( i > ( lowPriorityWaitMS / 20 ) ) | | | |
| sleep = 10; | | | |
| sleepmillis(sleep); | | | |
| i += ( sleep - 1 ); | | | |
| } | | | |
| if ( ! got ) { | | | |
| log() << "couldn't get lazy rwlock" << endl; | | | |
| _lock.lock(); | | | |
| } | | | |
| } | | | |
| else { | | | |
| _lock.lock(); | | | |
| } | | | |
| | | | |
| } | | } | |
| else { | | else { | |
| _lock.lock_shared(); | | _lock.lock_shared(); | |
| } | | } | |
| } | | } | |
| } | | } | |
| ~rwlock() { | | ~rwlock() { | |
| if ( _write ) | | if ( _write ) | |
| _lock.unlock(); | | _lock.unlock(); | |
| else | | else | |
| _lock.unlock_shared(); | | _lock.unlock_shared(); | |
| } | | } | |
| private: | | private: | |
| RWLock& _lock; | | RWLock& _lock; | |
| const bool _write; | | const bool _write; | |
| }; | | }; | |
| | | | |
|
| | | // -------------------------------------------------------------------- | |
| | | -------------------- | |
| | | | |
| /** recursive on shared locks is ok for this implementation */ | | /** recursive on shared locks is ok for this implementation */ | |
|
| class RWLockRecursive : boost::noncopyable { | | class RWLockRecursive : protected RWLockBase { | |
| | | protected: | |
| ThreadLocalValue<int> _state; | | ThreadLocalValue<int> _state; | |
|
| RWLock _lk; | | void lock(); // not implemented - Lock() should be used; didn't ove | |
| friend class Exclusive; | | rload this name to avoid mistakes | |
| | | virtual void Lock() { RWLockBase::lock(); } | |
| public: | | public: | |
|
| /** @param lpwaitms lazy wait */ | | virtual ~RWLockRecursive() { } | |
| RWLockRecursive(const char *name, int lpwaitms) : _lk(name, lpwaitm | | const char * const _name; | |
| s) { } | | RWLockRecursive(const char *name) : _name(name) { } | |
| | | | |
| void assertExclusivelyLocked() { | | void assertExclusivelyLocked() { | |
|
| dassert( _state.get() < 0 ); | | assert( _state.get() < 0 ); | |
| } | | } | |
| | | | |
|
| // RWLockRecursive::Exclusive scoped lock | | | |
| class Exclusive : boost::noncopyable { | | class Exclusive : boost::noncopyable { | |
| RWLockRecursive& _r; | | RWLockRecursive& _r; | |
|
| rwlock *_scopedLock; | | | |
| public: | | public: | |
|
| Exclusive(RWLockRecursive& r) : _r(r), _scopedLock(0) { | | Exclusive(RWLockRecursive& r) : _r(r) { | |
| int s = _r._state.get(); | | int s = _r._state.get(); | |
| dassert( s <= 0 ); | | dassert( s <= 0 ); | |
| if( s == 0 ) | | if( s == 0 ) | |
|
| _scopedLock = new rwlock(_r._lk, true); | | _r.Lock(); | |
| _r._state.set(s-1); | | _r._state.set(s-1); | |
| } | | } | |
| ~Exclusive() { | | ~Exclusive() { | |
| int s = _r._state.get(); | | int s = _r._state.get(); | |
| DEV wassert( s < 0 ); // wassert: don't throw from destruct
ors | | DEV wassert( s < 0 ); // wassert: don't throw from destruct
ors | |
|
| _r._state.set(s+1); | | ++s; | |
| delete _scopedLock; | | _r._state.set(s); | |
| | | if ( s == 0 ) | |
| | | _r.unlock(); | |
| } | | } | |
| }; | | }; | |
| | | | |
|
| // RWLockRecursive::Shared scoped lock | | | |
| class Shared : boost::noncopyable { | | class Shared : boost::noncopyable { | |
| RWLockRecursive& _r; | | RWLockRecursive& _r; | |
|
| bool _alreadyExclusive; | | bool _alreadyLockedExclusiveByUs; | |
| public: | | public: | |
| Shared(RWLockRecursive& r) : _r(r) { | | Shared(RWLockRecursive& r) : _r(r) { | |
| int s = _r._state.get(); | | int s = _r._state.get(); | |
|
| _alreadyExclusive = s < 0; | | _alreadyLockedExclusiveByUs = s < 0; | |
| if( !_alreadyExclusive ) { | | if( !_alreadyLockedExclusiveByUs ) { | |
| dassert( s >= 0 ); // -1 would mean exclusive | | dassert( s >= 0 ); // -1 would mean exclusive | |
| if( s == 0 ) | | if( s == 0 ) | |
|
| _r._lk.lock_shared(); | | _r.lock_shared(); | |
| _r._state.set(s+1); | | _r._state.set(s+1); | |
| } | | } | |
| } | | } | |
| ~Shared() { | | ~Shared() { | |
|
| if( _alreadyExclusive ) { | | if( _alreadyLockedExclusiveByUs ) { | |
| DEV wassert( _r._state.get() < 0 ); | | DEV wassert( _r._state.get() < 0 ); | |
| } | | } | |
| else { | | else { | |
| int s = _r._state.get() - 1; | | int s = _r._state.get() - 1; | |
|
| if( s == 0 ) | | | |
| _r._lk.unlock_shared(); | | | |
| _r._state.set(s); | | | |
| DEV wassert( s >= 0 ); | | DEV wassert( s >= 0 ); | |
|
| | | _r._state.set(s); | |
| | | if( s == 0 ) | |
| | | _r.unlock_shared(); | |
| } | | } | |
| } | | } | |
| }; | | }; | |
| }; | | }; | |
|
| | | | |
| | | class RWLockRecursiveNongreedy : public RWLockRecursive { | |
| | | virtual void Lock() { | |
| | | bool got = false; | |
| | | for ( int i=0; i<lowPriorityWaitMS; i++ ) { | |
| | | if ( lock_try(0) ) { | |
| | | got = true; | |
| | | break; | |
| | | } | |
| | | int sleep = 1; | |
| | | if ( i > ( lowPriorityWaitMS / 20 ) ) | |
| | | sleep = 10; | |
| | | sleepmillis(sleep); | |
| | | i += ( sleep - 1 ); | |
| | | } | |
| | | if ( ! got ) { | |
| | | log() << "couldn't lazily get rwlock" << endl; | |
| | | RWLockBase::lock(); | |
| | | } | |
| | | } | |
| | | | |
| | | public: | |
| | | const int lowPriorityWaitMS; | |
| | | RWLockRecursiveNongreedy(const char *nm, int lpwaitms) : RWLockRecu | |
| | | rsive(nm), lowPriorityWaitMS(lpwaitms) { } | |
| | | const char * implType() const { return RWLockRecursive::implType(); | |
| | | } | |
| | | | |
| | | //just for testing: | |
| | | bool __lock_try( int millis ) { return RWLockRecursive::lock_try(mi | |
| | | llis); } | |
| | | }; | |
| | | | |
| } | | } | |
| | | | |
End of changes. 40 change blocks. |
| 261 lines changed or deleted | | 139 lines changed or added | |
|
| top.h | | top.h | |
| | | | |
| skipping to change at line 89 | | skipping to change at line 89 | |
| void _appendToUsageMap( BSONObjBuilder& b , const UsageMap& map ) c
onst; | | void _appendToUsageMap( BSONObjBuilder& b , const UsageMap& map ) c
onst; | |
| void _appendStatsEntry( BSONObjBuilder& b , const char * statsName
, const UsageData& map ) const; | | void _appendStatsEntry( BSONObjBuilder& b , const char * statsName
, const UsageData& map ) const; | |
| void _record( CollectionData& c , int op , int lockType , long long
micros , bool command ); | | void _record( CollectionData& c , int op , int lockType , long long
micros , bool command ); | |
| | | | |
| mutable mongo::mutex _lock; | | mutable mongo::mutex _lock; | |
| CollectionData _global; | | CollectionData _global; | |
| UsageMap _usage; | | UsageMap _usage; | |
| string _lastDropped; | | string _lastDropped; | |
| }; | | }; | |
| | | | |
|
| /* Records per namespace utilization of the mongod process. | | | |
| No two functions of this class may be called concurrently. | | | |
| */ | | | |
| class TopOld { | | | |
| typedef boost::posix_time::ptime T; | | | |
| typedef boost::posix_time::time_duration D; | | | |
| typedef boost::tuple< D, int, int, int > UsageData; | | | |
| public: | | | |
| TopOld() : _read(false), _write(false) { } | | | |
| | | | |
| /* these are used to record activity: */ | | | |
| | | | |
| void clientStart( const char *client ) { | | | |
| clientStop(); | | | |
| _currentStart = currentTime(); | | | |
| _current = client; | | | |
| } | | | |
| | | | |
| /* indicate current request is a read operation. */ | | | |
| void setRead() { _read = true; } | | | |
| | | | |
| void setWrite() { _write = true; } | | | |
| | | | |
| void clientStop() { | | | |
| if ( _currentStart == T() ) | | | |
| return; | | | |
| D d = currentTime() - _currentStart; | | | |
| | | | |
| { | | | |
| scoped_lock L(topMutex); | | | |
| recordUsage( _current, d ); | | | |
| } | | | |
| | | | |
| _currentStart = T(); | | | |
| _read = false; | | | |
| _write = false; | | | |
| } | | | |
| | | | |
| /* these are used to fetch the stats: */ | | | |
| | | | |
| struct Usage { | | | |
| string ns; | | | |
| D time; | | | |
| double pct; | | | |
| int reads, writes, calls; | | | |
| }; | | | |
| | | | |
| static void usage( vector< Usage > &res ) { | | | |
| scoped_lock L(topMutex); | | | |
| | | | |
| // Populate parent namespaces | | | |
| UsageMap snapshot; | | | |
| UsageMap totalUsage; | | | |
| fillParentNamespaces( snapshot, _snapshot ); | | | |
| fillParentNamespaces( totalUsage, _totalUsage ); | | | |
| | | | |
| multimap< D, string, more > sorted; | | | |
| for( UsageMap::iterator i = snapshot.begin(); i != snapshot.end | | | |
| (); ++i ) | | | |
| sorted.insert( make_pair( i->second.get<0>(), i->first ) ); | | | |
| for( multimap< D, string, more >::iterator i = sorted.begin(); | | | |
| i != sorted.end(); ++i ) { | | | |
| if ( trivialNs( i->second.c_str() ) ) | | | |
| continue; | | | |
| Usage u; | | | |
| u.ns = i->second; | | | |
| u.time = totalUsage[ u.ns ].get<0>(); | | | |
| u.pct = _snapshotDuration != D() ? 100.0 * i->first.ticks() | | | |
| / _snapshotDuration.ticks() : 0; | | | |
| u.reads = snapshot[ u.ns ].get<1>(); | | | |
| u.writes = snapshot[ u.ns ].get<2>(); | | | |
| u.calls = snapshot[ u.ns ].get<3>(); | | | |
| res.push_back( u ); | | | |
| } | | | |
| for( UsageMap::iterator i = totalUsage.begin(); i != totalUsage | | | |
| .end(); ++i ) { | | | |
| if ( snapshot.count( i->first ) != 0 || trivialNs( i->first | | | |
| .c_str() ) ) | | | |
| continue; | | | |
| Usage u; | | | |
| u.ns = i->first; | | | |
| u.time = i->second.get<0>(); | | | |
| u.pct = 0; | | | |
| u.reads = 0; | | | |
| u.writes = 0; | | | |
| u.calls = 0; | | | |
| res.push_back( u ); | | | |
| } | | | |
| } | | | |
| | | | |
| static void completeSnapshot() { | | | |
| scoped_lock L(topMutex); | | | |
| | | | |
| if ( &_snapshot == &_snapshotA ) { | | | |
| _snapshot = _snapshotB; | | | |
| _nextSnapshot = _snapshotA; | | | |
| } | | | |
| else { | | | |
| _snapshot = _snapshotA; | | | |
| _nextSnapshot = _snapshotB; | | | |
| } | | | |
| _snapshotDuration = currentTime() - _snapshotStart; | | | |
| _snapshotStart = currentTime(); | | | |
| _nextSnapshot.clear(); | | | |
| } | | | |
| | | | |
| private: | | | |
| static mongo::mutex topMutex; | | | |
| static bool trivialNs( const char *ns ) { | | | |
| const char *ret = strrchr( ns, '.' ); | | | |
| return ret && ret[ 1 ] == '\0'; | | | |
| } | | | |
| typedef map<string,UsageData> UsageMap; // duration, # reads, # wri | | | |
| tes, # total calls | | | |
| static T currentTime() { | | | |
| return boost::posix_time::microsec_clock::universal_time(); | | | |
| } | | | |
| void recordUsage( const string &client, D duration ) { | | | |
| recordUsageForMap( _totalUsage, client, duration ); | | | |
| recordUsageForMap( _nextSnapshot, client, duration ); | | | |
| } | | | |
| void recordUsageForMap( UsageMap &map, const string &client, D dura | | | |
| tion ) { | | | |
| UsageData& g = map[client]; | | | |
| g.get< 0 >() += duration; | | | |
| if ( _read && !_write ) | | | |
| g.get< 1 >()++; | | | |
| else if ( !_read && _write ) | | | |
| g.get< 2 >()++; | | | |
| g.get< 3 >()++; | | | |
| } | | | |
| static void fillParentNamespaces( UsageMap &to, const UsageMap &fro | | | |
| m ) { | | | |
| for( UsageMap::const_iterator i = from.begin(); i != from.end() | | | |
| ; ++i ) { | | | |
| string current = i->first; | | | |
| size_t dot = current.rfind( "." ); | | | |
| if ( dot == string::npos || dot != current.length() - 1 ) { | | | |
| inc( to[ current ], i->second ); | | | |
| } | | | |
| while( dot != string::npos ) { | | | |
| current = current.substr( 0, dot ); | | | |
| inc( to[ current ], i->second ); | | | |
| dot = current.rfind( "." ); | | | |
| } | | | |
| } | | | |
| } | | | |
| static void inc( UsageData &to, const UsageData &from ) { | | | |
| to.get<0>() += from.get<0>(); | | | |
| to.get<1>() += from.get<1>(); | | | |
| to.get<2>() += from.get<2>(); | | | |
| to.get<3>() += from.get<3>(); | | | |
| } | | | |
| struct more { bool operator()( const D &a, const D &b ) { return a | | | |
| > b; } }; | | | |
| string _current; | | | |
| T _currentStart; | | | |
| static T _snapshotStart; | | | |
| static D _snapshotDuration; | | | |
| static UsageMap _totalUsage; | | | |
| static UsageMap _snapshotA; | | | |
| static UsageMap _snapshotB; | | | |
| static UsageMap &_snapshot; | | | |
| static UsageMap &_nextSnapshot; | | | |
| bool _read; | | | |
| bool _write; | | | |
| }; | | | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 1 change blocks. |
| 168 lines changed or deleted | | 0 lines changed or added | |
|
| update.h | | update.h | |
| | | | |
| skipping to change at line 29 | | skipping to change at line 29 | |
| #include "../../pch.h" | | #include "../../pch.h" | |
| #include "../jsobj.h" | | #include "../jsobj.h" | |
| #include "../../util/embedded_builder.h" | | #include "../../util/embedded_builder.h" | |
| #include "../matcher.h" | | #include "../matcher.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| // ---------- public ------------- | | // ---------- public ------------- | |
| | | | |
| struct UpdateResult { | | struct UpdateResult { | |
|
| bool existing; // if existing objects were modified | | const bool existing; // if existing objects were modified | |
| bool mod; // was this a $ mod | | const bool mod; // was this a $ mod | |
| long long num; // how many objects touched | | const long long num; // how many objects touched | |
| OID upserted; // if something was upserted, the new _id of the obj
ect | | OID upserted; // if something was upserted, the new _id of the obj
ect | |
| | | | |
| UpdateResult( bool e, bool m, unsigned long long n , const BSONObj&
upsertedObject = BSONObj() ) | | UpdateResult( bool e, bool m, unsigned long long n , const BSONObj&
upsertedObject = BSONObj() ) | |
| : existing(e) , mod(m), num(n) { | | : existing(e) , mod(m), num(n) { | |
| upserted.clear(); | | upserted.clear(); | |
|
| | | | |
| BSONElement id = upsertedObject["_id"]; | | BSONElement id = upsertedObject["_id"]; | |
| if ( ! e && n == 1 && id.type() == jstOID ) { | | if ( ! e && n == 1 && id.type() == jstOID ) { | |
| upserted = id.OID(); | | upserted = id.OID(); | |
| } | | } | |
| } | | } | |
|
| | | | |
| }; | | }; | |
| | | | |
| class RemoveSaver; | | class RemoveSaver; | |
| | | | |
| /* returns true if an existing object was updated, false if no existing
object was found. | | /* returns true if an existing object was updated, false if no existing
object was found. | |
| multi - update multiple objects - mostly useful with things like $se
t | | multi - update multiple objects - mostly useful with things like $se
t | |
| god - allow access to system namespaces | | god - allow access to system namespaces | |
| */ | | */ | |
|
| UpdateResult updateObjects(const char *ns, const BSONObj& updateobj, BS
ONObj pattern, bool upsert, bool multi , bool logop , OpDebug& debug, bool
hintIdElseNatural = false ); | | UpdateResult updateObjects(const char *ns, const BSONObj& updateobj, BS
ONObj pattern, bool upsert, bool multi , bool logop , OpDebug& debug ); | |
| UpdateResult _updateObjects(bool god, const char *ns, const BSONObj& up
dateobj, BSONObj pattern, | | UpdateResult _updateObjects(bool god, const char *ns, const BSONObj& up
dateobj, BSONObj pattern, | |
|
| bool upsert, bool multi , bool logop , OpDe | | bool upsert, bool multi , bool logop , OpDe | |
| bug& debug , RemoveSaver * rs = 0, | | bug& debug , RemoveSaver * rs = 0 ); | |
| bool hintIdElseNatural = false); | | | |
| | | | |
| // ---------- private ------------- | | // ---------- private ------------- | |
| | | | |
| class ModState; | | class ModState; | |
| class ModSetState; | | class ModSetState; | |
| | | | |
| /* Used for modifiers such as $inc, $set, $push, ... | | /* Used for modifiers such as $inc, $set, $push, ... | |
| * stores the info about a single operation | | * stores the info about a single operation | |
| * once created should never be modified | | * once created should never be modified | |
| */ | | */ | |
| | | | |
| skipping to change at line 186 | | skipping to change at line 183 | |
| } | | } | |
| | | | |
| bool isIndexed( const set<string>& idxKeys ) const { | | bool isIndexed( const set<string>& idxKeys ) const { | |
| string fullName = fieldName; | | string fullName = fieldName; | |
| | | | |
| if ( isIndexed( fullName , idxKeys ) ) | | if ( isIndexed( fullName , idxKeys ) ) | |
| return true; | | return true; | |
| | | | |
| if ( strstr( fieldName , "." ) ) { | | if ( strstr( fieldName , "." ) ) { | |
| // check for a.0.1 | | // check for a.0.1 | |
|
| StringBuilder buf( fullName.size() + 1 ); | | StringBuilder buf; | |
| for ( size_t i=0; i<fullName.size(); i++ ) { | | for ( size_t i=0; i<fullName.size(); i++ ) { | |
| char c = fullName[i]; | | char c = fullName[i]; | |
| | | | |
| if ( c == '$' && | | if ( c == '$' && | |
| i > 0 && fullName[i-1] == '.' && | | i > 0 && fullName[i-1] == '.' && | |
| i+1<fullName.size() && | | i+1<fullName.size() && | |
| fullName[i+1] == '.' ) { | | fullName[i+1] == '.' ) { | |
| i++; | | i++; | |
| continue; | | continue; | |
| } | | } | |
| | | | |
| skipping to change at line 580 | | skipping to change at line 577 | |
| void appendNewFromMod( ModState& ms , Builder& b ) { | | void appendNewFromMod( ModState& ms , Builder& b ) { | |
| if ( ms.dontApply ) { | | if ( ms.dontApply ) { | |
| return; | | return; | |
| } | | } | |
| | | | |
| //const Mod& m = *(ms.m); // HACK | | //const Mod& m = *(ms.m); // HACK | |
| Mod& m = *((Mod*)(ms.m)); // HACK | | Mod& m = *((Mod*)(ms.m)); // HACK | |
| | | | |
| switch ( m.op ) { | | switch ( m.op ) { | |
| | | | |
|
| case Mod::PUSH: | | case Mod::PUSH: { | |
| | | if ( m.isEach() ) { | |
| | | b.appendArray( m.shortFieldName, m.getEach() ); | |
| | | } else { | |
| | | BSONObjBuilder arr( b.subarrayStart( m.shortFieldName ) | |
| | | ); | |
| | | arr.appendAs( m.elt, "0" ); | |
| | | arr.done(); | |
| | | } | |
| | | break; | |
| | | } | |
| case Mod::ADDTOSET: { | | case Mod::ADDTOSET: { | |
| if ( m.isEach() ) { | | if ( m.isEach() ) { | |
|
| b.appendArray( m.shortFieldName , m.getEach() ); | | // Remove any duplicates in given array | |
| | | BSONObjBuilder arr( b.subarrayStart( m.shortFieldName ) | |
| | | ); | |
| | | BSONElementSet toadd; | |
| | | m.parseEach( toadd ); | |
| | | BSONObjIterator i( m.getEach() ); | |
| | | int n = 0; | |
| | | while ( i.more() ) { | |
| | | BSONElement e = i.next(); | |
| | | if ( toadd.count(e) ) { | |
| | | arr.appendAs( e , BSONObjBuilder::numStr( n++ ) | |
| | | ); | |
| | | toadd.erase( e ); | |
| | | } | |
| | | } | |
| | | arr.done(); | |
| } | | } | |
| else { | | else { | |
| BSONObjBuilder arr( b.subarrayStart( m.shortFieldName )
); | | BSONObjBuilder arr( b.subarrayStart( m.shortFieldName )
); | |
| arr.appendAs( m.elt, "0" ); | | arr.appendAs( m.elt, "0" ); | |
| arr.done(); | | arr.done(); | |
| } | | } | |
| break; | | break; | |
| } | | } | |
| | | | |
| case Mod::PUSH_ALL: { | | case Mod::PUSH_ALL: { | |
| | | | |
| skipping to change at line 623 | | skipping to change at line 642 | |
| ms.handleRename( b, m.shortFieldName ); | | ms.handleRename( b, m.shortFieldName ); | |
| break; | | break; | |
| default: | | default: | |
| stringstream ss; | | stringstream ss; | |
| ss << "unknown mod in appendNewFromMod: " << m.op; | | ss << "unknown mod in appendNewFromMod: " << m.op; | |
| throw UserException( 9015, ss.str() ); | | throw UserException( 9015, ss.str() ); | |
| } | | } | |
| | | | |
| } | | } | |
| | | | |
|
| /** @return true iff the elements aren't eoo(), are distinct, and s | | | |
| hare a field name. */ | | | |
| static bool duplicateFieldName( const BSONElement &a, const BSONEle | | | |
| ment &b ); | | | |
| | | | |
| public: | | public: | |
| | | | |
| bool canApplyInPlace() const { | | bool canApplyInPlace() const { | |
| return _inPlacePossible; | | return _inPlacePossible; | |
| } | | } | |
| | | | |
| /** | | /** | |
| * modified underlying _obj | | * modified underlying _obj | |
| * @param isOnDisk - true means this is an on disk object, and this
update needs to be made durable | | * @param isOnDisk - true means this is an on disk object, and this
update needs to be made durable | |
| */ | | */ | |
| | | | |
End of changes. 9 change blocks. |
| 17 lines changed or deleted | | 34 lines changed or added | |
|
| v8_db.h | | v8_db.h | |
| | | | |
| skipping to change at line 48 | | skipping to change at line 48 | |
| mongo::DBClientBase * getConnection( const v8::Arguments& args ); | | mongo::DBClientBase * getConnection( const v8::Arguments& args ); | |
| | | | |
| // Mongo members | | // Mongo members | |
| v8::Handle<v8::Value> mongoConsLocal(V8Scope* scope, const v8::Argument
s& args); | | v8::Handle<v8::Value> mongoConsLocal(V8Scope* scope, const v8::Argument
s& args); | |
| v8::Handle<v8::Value> mongoConsExternal(V8Scope* scope, const v8::Argum
ents& args); | | v8::Handle<v8::Value> mongoConsExternal(V8Scope* scope, const v8::Argum
ents& args); | |
| | | | |
| v8::Handle<v8::Value> mongoFind(V8Scope* scope, const v8::Arguments& ar
gs); | | v8::Handle<v8::Value> mongoFind(V8Scope* scope, const v8::Arguments& ar
gs); | |
| v8::Handle<v8::Value> mongoInsert(V8Scope* scope, const v8::Arguments&
args); | | v8::Handle<v8::Value> mongoInsert(V8Scope* scope, const v8::Arguments&
args); | |
| v8::Handle<v8::Value> mongoRemove(V8Scope* scope, const v8::Arguments&
args); | | v8::Handle<v8::Value> mongoRemove(V8Scope* scope, const v8::Arguments&
args); | |
| v8::Handle<v8::Value> mongoUpdate(V8Scope* scope, const v8::Arguments&
args); | | v8::Handle<v8::Value> mongoUpdate(V8Scope* scope, const v8::Arguments&
args); | |
|
| | | v8::Handle<v8::Value> mongoAuth(V8Scope* scope, const v8::Arguments& ar
gs); | |
| | | | |
| v8::Handle<v8::Value> internalCursorCons(V8Scope* scope, const v8::Argu
ments& args); | | v8::Handle<v8::Value> internalCursorCons(V8Scope* scope, const v8::Argu
ments& args); | |
| v8::Handle<v8::Value> internalCursorNext(V8Scope* scope, const v8::Argu
ments& args); | | v8::Handle<v8::Value> internalCursorNext(V8Scope* scope, const v8::Argu
ments& args); | |
| v8::Handle<v8::Value> internalCursorHasNext(V8Scope* scope, const v8::A
rguments& args); | | v8::Handle<v8::Value> internalCursorHasNext(V8Scope* scope, const v8::A
rguments& args); | |
| v8::Handle<v8::Value> internalCursorObjsLeftInBatch(V8Scope* scope, con
st v8::Arguments& args); | | v8::Handle<v8::Value> internalCursorObjsLeftInBatch(V8Scope* scope, con
st v8::Arguments& args); | |
|
| | | v8::Handle<v8::Value> internalCursorReadOnly(V8Scope* scope, const v8::
Arguments& args); | |
| | | | |
| // DB members | | // DB members | |
| | | | |
| v8::Handle<v8::Value> dbInit(V8Scope* scope, const v8::Arguments& args)
; | | v8::Handle<v8::Value> dbInit(V8Scope* scope, const v8::Arguments& args)
; | |
| v8::Handle<v8::Value> collectionInit(V8Scope* scope, const v8::Argument
s& args ); | | v8::Handle<v8::Value> collectionInit(V8Scope* scope, const v8::Argument
s& args ); | |
| v8::Handle<v8::Value> objectIdInit( V8Scope* scope, const v8::Arguments
& args ); | | v8::Handle<v8::Value> objectIdInit( V8Scope* scope, const v8::Arguments
& args ); | |
| | | | |
| v8::Handle<v8::Value> dbRefInit( V8Scope* scope, const v8::Arguments& a
rgs ); | | v8::Handle<v8::Value> dbRefInit( V8Scope* scope, const v8::Arguments& a
rgs ); | |
| v8::Handle<v8::Value> dbPointerInit( V8Scope* scope, const v8::Argument
s& args ); | | v8::Handle<v8::Value> dbPointerInit( V8Scope* scope, const v8::Argument
s& args ); | |
| v8::Handle<v8::Value> dbTimestampInit( V8Scope* scope, const v8::Argume
nts& args ); | | v8::Handle<v8::Value> dbTimestampInit( V8Scope* scope, const v8::Argume
nts& args ); | |
| | | | |
| skipping to change at line 84 | | skipping to change at line 86 | |
| v8::Handle<v8::Value> numberLongToNumber(V8Scope* scope, const v8::Argu
ments& args); | | v8::Handle<v8::Value> numberLongToNumber(V8Scope* scope, const v8::Argu
ments& args); | |
| v8::Handle<v8::Value> numberLongValueOf(V8Scope* scope, const v8::Argum
ents& args); | | v8::Handle<v8::Value> numberLongValueOf(V8Scope* scope, const v8::Argum
ents& args); | |
| v8::Handle<v8::Value> numberLongToString(V8Scope* scope, const v8::Argu
ments& args); | | v8::Handle<v8::Value> numberLongToString(V8Scope* scope, const v8::Argu
ments& args); | |
| | | | |
| v8::Handle<v8::Value> numberIntInit( V8Scope* scope, const v8::Argument
s& args ); | | v8::Handle<v8::Value> numberIntInit( V8Scope* scope, const v8::Argument
s& args ); | |
| v8::Handle<v8::Value> numberIntToNumber(V8Scope* scope, const v8::Argum
ents& args); | | v8::Handle<v8::Value> numberIntToNumber(V8Scope* scope, const v8::Argum
ents& args); | |
| v8::Handle<v8::Value> numberIntValueOf(V8Scope* scope, const v8::Argume
nts& args); | | v8::Handle<v8::Value> numberIntValueOf(V8Scope* scope, const v8::Argume
nts& args); | |
| v8::Handle<v8::Value> numberIntToString(V8Scope* scope, const v8::Argum
ents& args); | | v8::Handle<v8::Value> numberIntToString(V8Scope* scope, const v8::Argum
ents& args); | |
| | | | |
| v8::Handle<v8::Value> dbQueryInit( V8Scope* scope, const v8::Arguments&
args ); | | v8::Handle<v8::Value> dbQueryInit( V8Scope* scope, const v8::Arguments&
args ); | |
|
| v8::Handle<v8::Value> dbQueryIndexAccess( uint32_t index , const v8::Ac
cessorInfo& info ); | | v8::Handle<v8::Value> dbQueryIndexAccess( ::uint32_t index , const v8::
AccessorInfo& info ); | |
| | | | |
|
| v8::Handle<v8::Value> collectionFallback( v8::Local<v8::String> name, c | | v8::Handle<v8::Value> collectionGetter( v8::Local<v8::String> name, con | |
| onst v8::AccessorInfo &info); | | st v8::AccessorInfo &info); | |
| | | v8::Handle<v8::Value> collectionSetter( Local<v8::String> name, Local<V | |
| | | alue> value, const AccessorInfo& info ); | |
| | | | |
| v8::Handle<v8::Value> bsonsize( V8Scope* scope, const v8::Arguments& ar
gs ); | | v8::Handle<v8::Value> bsonsize( V8Scope* scope, const v8::Arguments& ar
gs ); | |
| | | | |
|
| // call with v8 mutex: | | | |
| void enableV8Interrupt(); | | | |
| void disableV8Interrupt(); | | | |
| | | | |
| } | | } | |
| | | | |
End of changes. 5 change blocks. |
| 7 lines changed or deleted | | 7 lines changed or added | |
|
| value.h | | value.h | |
|
| /* @file value.h | | | |
| concurrency helpers DiagStr, Guarded | | | |
| */ | | | |
| | | | |
| /** | | /** | |
|
| * Copyright (C) 2008 10gen Inc. | | * Copyright (c) 2011 10gen Inc. | |
| * | | * | |
| * This program is free software: you can redistribute it and/or modify | | * 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 | | * it under the terms of the GNU Affero General Public License, version 3, | |
| , | | * as published by the Free Software Foundation. | |
| * as published by the Free Software Foundation. | | * | |
| * | | * This program is distributed in the hope that it will be useful, | |
| * This program is distributed in the hope that it will be useful,b | | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | * GNU Affero General Public License for more details. | |
| * GNU Affero General Public License for more details. | | * | |
| * | | * You should have received a copy of the GNU Affero General Public License | |
| * You should have received a copy of the GNU Affero General Public Licen | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| se | | */ | |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. | | | |
| */ | | | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
|
| #include "mutex.h" | | #include "pch.h" | |
| #include "spin_lock.h" | | #include "bson/bsontypes.h" | |
| | | #include "bson/oid.h" | |
| | | #include "util/intrusive_counter.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
|
| | | class BSONElement; | |
| | | class Builder; | |
| | | class Document; | |
| | | class Value; | |
| | | | |
|
| /** declare that a variable that is "guarded" by a mutex. | | class ValueIterator : | |
| | | public IntrusiveCounterUnsigned { | |
| | | public: | |
| | | virtual ~ValueIterator(); | |
| | | | |
|
| The decl documents the rule. For example "counta and countb are gu | | /* | |
| arded by xyzMutex": | | Ask if there are more fields to return. | |
| | | | |
|
| Guarded<int, xyzMutex> counta; | | @returns true if there are more fields, false otherwise | |
| Guarded<int, xyzMutex> countb; | | */ | |
| | | virtual bool more() const = 0; | |
| | | | |
|
| Upon use, specify the scoped_lock object. This makes it hard for s | | /* | |
| omeone | | Move the iterator to point to the next field and return it. | |
| later to forget to be in the lock. Check is made that it is the ri | | | |
| ght lock in _DEBUG | | | |
| builds at runtime. | | | |
| */ | | | |
| template <typename T, mutex& BY> | | | |
| class Guarded : boost::noncopyable { | | | |
| T _val; | | | |
| public: | | | |
| T& ref(const scoped_lock& lk) { | | | |
| dassert( lk._mut == &BY ); | | | |
| return _val; | | | |
| } | | | |
| }; | | | |
| | | | |
|
| class DiagStr { | | @returns the next field's <name, Value> | |
| string _s; | | */ | |
| mutable SpinLock m; | | virtual intrusive_ptr<const Value> next() = 0; | |
| public: | | | |
| DiagStr(const DiagStr& r) : _s(r.get()) { } | | | |
| DiagStr() { } | | | |
| bool empty() const { | | | |
| scoped_spinlock lk(m); | | | |
| return _s.empty(); | | | |
| } | | | |
| string get() const { | | | |
| scoped_spinlock lk(m); | | | |
| return _s; | | | |
| } | | | |
| | | | |
| void set(const char *s) { | | | |
| scoped_spinlock lk(m); | | | |
| _s = s; | | | |
| } | | | |
| void set(const string& s) { | | | |
| scoped_spinlock lk(m); | | | |
| _s = s; | | | |
| } | | | |
| operator string() const { return get(); } | | | |
| void operator=(const string& s) { set(s); } | | | |
| void operator=(const DiagStr& rhs) { | | | |
| scoped_spinlock lk(m); | | | |
| _s = rhs.get(); | | | |
| } | | | |
| }; | | }; | |
| | | | |
|
| #if 0 // not including in 2.0 | | /* | |
| | | Values are immutable, so these are passed around as | |
| | | intrusive_ptr<const Value>. | |
| | | */ | |
| | | class Value : | |
| | | public IntrusiveCounterUnsigned { | |
| | | public: | |
| | | ~Value(); | |
| | | | |
|
| /** Thread safe map. | | /* | |
| Be careful not to use this too much or it could make things slow; | | Construct a Value from a BSONElement. | |
| if not a hot code path no problem. | | | |
| | | | |
|
| Examples: | | This ignores the name of the element, and only uses the value, | |
| | | whatever type it is. | |
| | | | |
|
| mapsf<int,int> mp; | | @returns a new Value initialized from the bsonElement | |
| | | */ | |
| | | static intrusive_ptr<const Value> createFromBsonElement( | |
| | | BSONElement *pBsonElement); | |
| | | | |
| | | /* | |
| | | Construct an integer-valued Value. | |
| | | | |
| | | For commonly used values, consider using one of the singleton | |
| | | instances defined below. | |
| | | | |
| | | @param value the value | |
| | | @returns a Value with the given value | |
| | | */ | |
| | | static intrusive_ptr<const Value> createInt(int value); | |
| | | | |
| | | /* | |
| | | Construct an long(long)-valued Value. | |
| | | | |
| | | For commonly used values, consider using one of the singleton | |
| | | instances defined below. | |
| | | | |
| | | @param value the value | |
| | | @returns a Value with the given value | |
| | | */ | |
| | | static intrusive_ptr<const Value> createLong(long long value); | |
| | | | |
| | | /* | |
| | | Construct a double-valued Value. | |
| | | | |
| | | @param value the value | |
| | | @returns a Value with the given value | |
| | | */ | |
| | | static intrusive_ptr<const Value> createDouble(double value); | |
| | | | |
| | | /* | |
| | | Construct a string-valued Value. | |
| | | | |
| | | @param value the value | |
| | | @returns a Value with the given value | |
| | | */ | |
| | | static intrusive_ptr<const Value> createString(const string &value) | |
| | | ; | |
| | | | |
| | | /* | |
| | | Construct a date-valued Value. | |
| | | | |
| | | @param value the value | |
| | | @returns a Value with the given value | |
| | | */ | |
| | | static intrusive_ptr<const Value> createDate(const Date_t &value); | |
| | | | |
| | | /* | |
| | | Construct a document-valued Value. | |
| | | | |
| | | @param value the value | |
| | | @returns a Value with the given value | |
| | | */ | |
| | | static intrusive_ptr<const Value> createDocument( | |
| | | const intrusive_ptr<Document> &pDocument); | |
| | | | |
| | | /* | |
| | | Construct an array-valued Value. | |
| | | | |
| | | @param value the value | |
| | | @returns a Value with the given value | |
| | | */ | |
| | | static intrusive_ptr<const Value> createArray( | |
| | | const vector<intrusive_ptr<const Value> > &vpValue); | |
| | | | |
| | | /* | |
| | | Get the BSON type of the field. | |
| | | | |
| | | If the type is jstNULL, no value getter will work. | |
| | | | |
| | | @return the BSON type of the field. | |
| | | */ | |
| | | BSONType getType() const; | |
| | | | |
| | | /* | |
| | | Getters. | |
| | | | |
| | | @returns the Value's value; asserts if the requested value type i | |
| | | s | |
| | | incorrect. | |
| | | */ | |
| | | double getDouble() const; | |
| | | string getString() const; | |
| | | intrusive_ptr<Document> getDocument() const; | |
| | | intrusive_ptr<ValueIterator> getArray() const; | |
| | | OID getOid() const; | |
| | | bool getBool() const; | |
| | | Date_t getDate() const; | |
| | | string getRegex() const; | |
| | | string getSymbol() const; | |
| | | int getInt() const; | |
| | | unsigned long long getTimestamp() const; | |
| | | long long getLong() const; | |
| | | | |
| | | /* | |
| | | Get the length of an array value. | |
| | | | |
| | | @returns the length of the array, if this is array-valued; otherw | |
| | | ise | |
| | | throws an error | |
| | | */ | |
| | | size_t getArrayLength() const; | |
| | | | |
| | | /* | |
| | | Add this value to the BSON object under construction. | |
| | | */ | |
| | | void addToBsonObj(BSONObjBuilder *pBuilder, string fieldName) const | |
| | | ; | |
| | | | |
| | | /* | |
| | | Add this field to the BSON array under construction. | |
| | | | |
| | | As part of an array, the Value's name will be ignored. | |
| | | */ | |
| | | void addToBsonArray(BSONArrayBuilder *pBuilder) const; | |
| | | | |
| | | /* | |
| | | Get references to singleton instances of commonly used field valu | |
| | | es. | |
| | | */ | |
| | | static intrusive_ptr<const Value> getUndefined(); | |
| | | static intrusive_ptr<const Value> getNull(); | |
| | | static intrusive_ptr<const Value> getTrue(); | |
| | | static intrusive_ptr<const Value> getFalse(); | |
| | | static intrusive_ptr<const Value> getMinusOne(); | |
| | | static intrusive_ptr<const Value> getZero(); | |
| | | static intrusive_ptr<const Value> getOne(); | |
| | | | |
| | | /* | |
| | | Coerce (cast) a value to a native bool, using JSON rules. | |
| | | | |
| | | @returns the bool value | |
| | | */ | |
| | | bool coerceToBool() const; | |
| | | | |
| | | /* | |
| | | Coerce (cast) a value to a Boolean Value, using JSON rules. | |
| | | | |
| | | @returns the Boolean Value value | |
| | | */ | |
| | | intrusive_ptr<const Value> coerceToBoolean() const; | |
| | | | |
| | | /* | |
| | | Coerce (cast) a value to an int, using JSON rules. | |
| | | | |
| | | @returns the int value | |
| | | */ | |
| | | int coerceToInt() const; | |
| | | | |
| | | /* | |
| | | Coerce (cast) a value to a long long, using JSON rules. | |
| | | | |
| | | @returns the long value | |
| | | */ | |
| | | long long coerceToLong() const; | |
| | | | |
| | | /* | |
| | | Coerce (cast) a value to a double, using JSON rules. | |
| | | | |
| | | @returns the double value | |
| | | */ | |
| | | double coerceToDouble() const; | |
| | | | |
| | | /* | |
| | | Coerce (cast) a value to a date, using JSON rules. | |
| | | | |
| | | @returns the date value | |
| | | */ | |
| | | Date_t coerceToDate() const; | |
| | | | |
| | | /* | |
| | | Coerce (cast) a value to a string, using JSON rules. | |
| | | | |
| | | @returns the date value | |
| | | */ | |
| | | string coerceToString() const; | |
| | | | |
| | | /* | |
| | | Compare two Values. | |
| | | | |
| | | @param rL left value | |
| | | @param rR right value | |
| | | @returns an integer less than zero, zero, or an integer greater t | |
| | | han | |
| | | zero, depending on whether rL < rR, rL == rR, or rL > rR | |
| | | */ | |
| | | static int compare(const intrusive_ptr<const Value> &rL, | |
| | | const intrusive_ptr<const Value> &rR); | |
| | | | |
| | | /* | |
| | | Figure out what the widest of two numeric types is. | |
| | | | |
| | | Widest can be thought of as "most capable," or "able to hold the | |
| | | largest or most precise value." The progression is Int, Long, Do | |
| | | uble. | |
| | | | |
| | | @param rL left value | |
| | | @param rR right value | |
| | | @returns a BSONType of NumberInt, NumberLong, or NumberDouble | |
| | | */ | |
| | | static BSONType getWidestNumeric(BSONType lType, BSONType rType); | |
| | | | |
| | | /* | |
| | | Get the approximate storage size of the value, in bytes. | |
| | | | |
| | | @returns approximate storage size of the value. | |
| | | */ | |
| | | size_t getApproximateSize() const; | |
| | | | |
| | | /* | |
| | | Calculate a hash value. | |
| | | | |
| | | Meant to be used to create composite hashes suitable for | |
| | | boost classes such as unordered_map<>. | |
| | | | |
| | | @param seed value to augment with this' hash | |
| | | */ | |
| | | void hash_combine(size_t &seed) const; | |
| | | | |
| | | /* | |
| | | struct Hash is defined to enable the use of Values as | |
| | | keys in boost::unordered_map<>. | |
| | | | |
| | | Values are always referenced as immutables in the form | |
| | | intrusive_ptr<const Value>, so these operate on that construction | |
| | | . | |
| | | */ | |
| | | struct Hash : | |
| | | unary_function<intrusive_ptr<const Value>, size_t> { | |
| | | size_t operator()(const intrusive_ptr<const Value> &rV) const; | |
| | | }; | |
| | | | |
|
| int x = mp.get(); | | protected: | |
| | | Value(); // creates null value | |
| | | Value(BSONType type); // creates an empty (unitialized value) of ty | |
| | | pe | |
| | | // mostly useful for Undefi | |
| | | ned | |
| | | Value(bool boolValue); | |
| | | Value(int intValue); | |
| | | | |
| | | private: | |
| | | Value(BSONElement *pBsonElement); | |
| | | | |
| | | Value(long long longValue); | |
| | | Value(double doubleValue); | |
| | | Value(const Date_t &dateValue); | |
| | | Value(const string &stringValue); | |
| | | Value(const intrusive_ptr<Document> &pDocument); | |
| | | Value(const vector<intrusive_ptr<const Value> > &vpValue); | |
| | | | |
| | | void addToBson(Builder *pBuilder) const; | |
| | | | |
| | | BSONType type; | |
| | | | |
| | | /* store value in one of these */ | |
| | | union { | |
| | | double doubleValue; | |
| | | bool boolValue; | |
| | | int intValue; | |
| | | unsigned long long timestampValue; | |
| | | long long longValue; | |
| | | | |
| | | } simple; // values that don't need a ctor/dtor | |
| | | OID oidValue; | |
| | | Date_t dateValue; | |
| | | string stringValue; // String, Regex, Symbol | |
| | | intrusive_ptr<Document> pDocumentValue; | |
| | | vector<intrusive_ptr<const Value> > vpValue; // for arrays | |
| | | | |
| | | /* | |
| | | These are often used as the result of boolean or comparison | |
| | | expressions. | |
| | | | |
| | | These are obtained via public static getters defined above. | |
| | | */ | |
| | | static const intrusive_ptr<const Value> pFieldUndefined; | |
| | | static const intrusive_ptr<const Value> pFieldNull; | |
| | | static const intrusive_ptr<const Value> pFieldTrue; | |
| | | static const intrusive_ptr<const Value> pFieldFalse; | |
| | | static const intrusive_ptr<const Value> pFieldMinusOne; | |
| | | static const intrusive_ptr<const Value> pFieldZero; | |
| | | static const intrusive_ptr<const Value> pFieldOne; | |
| | | | |
| | | /* this implementation is used for getArray() */ | |
| | | class vi : | |
| | | public ValueIterator { | |
| | | public: | |
| | | // virtuals from ValueIterator | |
| | | virtual ~vi(); | |
| | | virtual bool more() const; | |
| | | virtual intrusive_ptr<const Value> next(); | |
| | | | |
| | | private: | |
| | | friend class Value; | |
| | | vi(const intrusive_ptr<const Value> &pSource, | |
| | | const vector<intrusive_ptr<const Value> > *pvpValue); | |
| | | | |
| | | size_t size; | |
| | | size_t nextIndex; | |
| | | const vector<intrusive_ptr<const Value> > *pvpValue; | |
| | | }; /* class vi */ | |
| | | | |
|
| map<int,int> two; | | }; | |
| mp.swap(two); | | | |
| | | | |
|
| { | | /* | |
| mapsf<int,int>::ref r(mp); | | Equality operator for values. | |
| r[9] = 1; | | | |
| map<int,int>::iterator i = r.r.begin(); | | | |
| } | | | |
| | | | |
|
| */ | | Useful for unordered_map<>, etc. | |
| template< class K, class V > | | */ | |
| struct mapsf : boost::noncopyable { | | inline bool operator==(const intrusive_ptr<const Value> &v1, | |
| SimpleMutex m; | | const intrusive_ptr<const Value> &v2) { | |
| map<K,V> val; | | return (Value::compare(v1, v2) == 0); | |
| friend struct ref; | | } | |
| | | | |
| | | /* | |
| | | For performance reasons, there are various sharable static values | |
| | | defined in class Value, obtainable by methods such as getUndefined(), | |
| | | getTrue(), getOne(), etc. We don't want these to go away as they are | |
| | | used by a multitude of threads evaluating pipelines. In order to avo | |
| | | id | |
| | | having to use atomic integers in the intrusive reference counter, thi | |
| | | s | |
| | | class overrides the reference counting methods to do nothing, making | |
| | | it | |
| | | safe to use for static Values. | |
| | | | |
| | | At this point, only the constructors necessary for the static Values | |
| | | in | |
| | | common use have been defined. The remainder can be defined if necess | |
| | | ary. | |
| | | */ | |
| | | class ValueStatic : | |
| | | public Value { | |
| public: | | public: | |
|
| mapsf() : m("mapsf") { } | | // virtuals from IntrusiveCounterUnsigned | |
| void swap(map<K,V>& rhs) { | | virtual void addRef() const; | |
| SimpleMutex::scoped_lock lk(m); | | virtual void release() const; | |
| val.swap(rhs); | | | |
| } | | // constructors | |
| // safe as we pass by value: | | ValueStatic(); | |
| V get(K k) { | | ValueStatic(BSONType type); | |
| SimpleMutex::scoped_lock lk(m); | | ValueStatic(bool boolValue); | |
| map<K,V>::iterator i = val.find(k); | | ValueStatic(int intValue); | |
| if( i == val.end() ) | | | |
| return K(); | | | |
| return i->second; | | | |
| } | | | |
| // think about deadlocks when using ref. the other methods | | | |
| // above will always be safe as they are "leaf" operations. | | | |
| struct ref { | | | |
| SimpleMutex::scoped_lock lk; | | | |
| public: | | | |
| map<K,V> const &r; | | | |
| ref(mapsf<K,V> &m) : lk(m.m), r(m.val) { } | | | |
| V& operator[](const K& k) { return r[k]; } | | | |
| }; | | | |
| }; | | }; | |
|
| #endif | | | |
| } | | } | |
|
| | | | |
| | | /* ======================= INLINED IMPLEMENTATIONS ======================== | |
| | | == */ | |
| | | | |
| | | namespace mongo { | |
| | | | |
| | | inline BSONType Value::getType() const { | |
| | | return type; | |
| | | } | |
| | | | |
| | | inline size_t Value::getArrayLength() const { | |
| | | assert(getType() == Array); | |
| | | return vpValue.size(); | |
| | | } | |
| | | | |
| | | inline intrusive_ptr<const Value> Value::getUndefined() { | |
| | | return pFieldUndefined; | |
| | | } | |
| | | | |
| | | inline intrusive_ptr<const Value> Value::getNull() { | |
| | | return pFieldNull; | |
| | | } | |
| | | | |
| | | inline intrusive_ptr<const Value> Value::getTrue() { | |
| | | return pFieldTrue; | |
| | | } | |
| | | | |
| | | inline intrusive_ptr<const Value> Value::getFalse() { | |
| | | return pFieldFalse; | |
| | | } | |
| | | | |
| | | inline intrusive_ptr<const Value> Value::getMinusOne() { | |
| | | return pFieldMinusOne; | |
| | | } | |
| | | | |
| | | inline intrusive_ptr<const Value> Value::getZero() { | |
| | | return pFieldZero; | |
| | | } | |
| | | | |
| | | inline intrusive_ptr<const Value> Value::getOne() { | |
| | | return pFieldOne; | |
| | | } | |
| | | | |
| | | inline size_t Value::Hash::operator()( | |
| | | const intrusive_ptr<const Value> &rV) const { | |
| | | size_t seed = 0xf0afbeef; | |
| | | rV->hash_combine(seed); | |
| | | return seed; | |
| | | } | |
| | | | |
| | | inline ValueStatic::ValueStatic(): | |
| | | Value() { | |
| | | } | |
| | | | |
| | | inline ValueStatic::ValueStatic(BSONType type): | |
| | | Value(type) { | |
| | | } | |
| | | | |
| | | inline ValueStatic::ValueStatic(bool boolValue): | |
| | | Value(boolValue) { | |
| | | } | |
| | | | |
| | | inline ValueStatic::ValueStatic(int intValue): | |
| | | Value(intValue) { | |
| | | } | |
| | | | |
| | | }; | |
| | | | |
End of changes. 20 change blocks. |
| 114 lines changed or deleted | | 392 lines changed or added | |
|