| client.h | | client.h | |
| | | | |
| skipping to change at line 167 | | skipping to change at line 167 | |
| }; | | }; | |
| | | | |
| private: | | private: | |
| CurOp * _curOp; | | CurOp * _curOp; | |
| Context * _context; | | Context * _context; | |
| bool _shutdown; | | bool _shutdown; | |
| list<string> _tempCollections; | | list<string> _tempCollections; | |
| const char *_desc; | | const char *_desc; | |
| bool _god; | | bool _god; | |
| AuthenticationInfo _ai; | | AuthenticationInfo _ai; | |
|
| | | OpTime _lastOp; | |
| | | BSONObj _handshake; | |
| | | BSONObj _remoteId; | |
| | | | |
| public: | | public: | |
| | | | |
| AuthenticationInfo * getAuthenticationInfo(){ return &_ai; } | | AuthenticationInfo * getAuthenticationInfo(){ return &_ai; } | |
| bool isAdmin() { return _ai.isAuthorized( "admin" ); } | | bool isAdmin() { return _ai.isAuthorized( "admin" ); } | |
| | | | |
| CurOp* curop() { return _curOp; } | | CurOp* curop() { return _curOp; } | |
| | | | |
| Context* getContext(){ return _context; } | | Context* getContext(){ return _context; } | |
| Database* database() { return _context ? _context->db() : 0; } | | Database* database() { return _context ? _context->db() : 0; } | |
| | | | |
| skipping to change at line 188 | | skipping to change at line 191 | |
| | | | |
| Client(const char *desc); | | Client(const char *desc); | |
| ~Client(); | | ~Client(); | |
| | | | |
| const char *desc() const { return _desc; } | | const char *desc() const { return _desc; } | |
| | | | |
| void addTempCollection( const string& ns ){ | | void addTempCollection( const string& ns ){ | |
| _tempCollections.push_back( ns ); | | _tempCollections.push_back( ns ); | |
| } | | } | |
| | | | |
|
| | | void setLastOp( const OpTime& op ){ | |
| | | _lastOp = op; | |
| | | } | |
| | | | |
| | | OpTime getLastOp() const { | |
| | | return _lastOp; | |
| | | } | |
| | | | |
| | | void appendLastOp( BSONObjBuilder& b ){ | |
| | | if ( ! _lastOp.isNull() ) | |
| | | b.appendTimestamp( "lastOp" , _lastOp.asDate() ); | |
| | | } | |
| | | | |
| /* each thread which does db operations has a Client object in TLS. | | /* each thread which does db operations has a Client object in TLS. | |
| call this when your thread starts. | | call this when your thread starts. | |
| */ | | */ | |
| static void initThread(const char *desc); | | static void initThread(const char *desc); | |
| | | | |
| /* | | /* | |
| this has to be called as the client goes away, but before thread
termination | | this has to be called as the client goes away, but before thread
termination | |
| @return true if anything was done | | @return true if anything was done | |
| */ | | */ | |
| bool shutdown(); | | bool shutdown(); | |
| | | | |
| bool isGod() const { return _god; } | | bool isGod() const { return _god; } | |
| | | | |
| friend class CurOp; | | friend class CurOp; | |
| | | | |
| string toString() const; | | string toString() const; | |
|
| | | | |
| | | void gotHandshake( const BSONObj& o ); | |
| | | | |
| | | BSONObj getRemoteID() const { return _remoteId; } | |
| | | BSONObj getHandshake() const { return _handshake; } | |
| }; | | }; | |
| | | | |
| inline Client& cc() { | | inline Client& cc() { | |
| return *currentClient.get(); | | return *currentClient.get(); | |
| } | | } | |
| | | | |
| /* each thread which does db operations has a Client object in TLS. | | /* each thread which does db operations has a Client object in TLS. | |
| call this when your thread starts. | | call this when your thread starts. | |
| */ | | */ | |
| inline void Client::initThread(const char *desc) { | | inline void Client::initThread(const char *desc) { | |
| | | | |
End of changes. 3 change blocks. |
| 0 lines changed or deleted | | 21 lines changed or added | |
|
| clientcursor.h | | clientcursor.h | |
| | | | |
| skipping to change at line 34 | | skipping to change at line 34 | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "../stdafx.h" | | #include "../stdafx.h" | |
| #include "cursor.h" | | #include "cursor.h" | |
| #include "jsobj.h" | | #include "jsobj.h" | |
| #include "../util/message.h" | | #include "../util/message.h" | |
| #include "diskloc.h" | | #include "diskloc.h" | |
| #include "dbhelpers.h" | | #include "dbhelpers.h" | |
| #include "matcher.h" | | #include "matcher.h" | |
|
| | | #include "../client/dbclient.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| typedef long long CursorId; /* passed to the client so it can send back
on getMore */ | | typedef long long CursorId; /* passed to the client so it can send back
on getMore */ | |
| class Cursor; /* internal server cursor base class */ | | class Cursor; /* internal server cursor base class */ | |
| class ClientCursor; | | class ClientCursor; | |
| | | | |
| /* todo: make this map be per connection. this will prevent cursor hij
acking security attacks perhaps. | | /* todo: make this map be per connection. this will prevent cursor hij
acking security attacks perhaps. | |
| */ | | */ | |
| typedef map<CursorId, ClientCursor*> CCById; | | typedef map<CursorId, ClientCursor*> CCById; | |
| | | | |
| skipping to change at line 107 | | skipping to change at line 108 | |
| release(); | | release(); | |
| } | | } | |
| }; | | }; | |
| | | | |
| /*const*/ CursorId cursorid; | | /*const*/ CursorId cursorid; | |
| string ns; | | string ns; | |
| auto_ptr<CoveredIndexMatcher> matcher; | | auto_ptr<CoveredIndexMatcher> matcher; | |
| auto_ptr<Cursor> c; | | auto_ptr<Cursor> c; | |
| int pos; // # objects into the curs
or so far | | int pos; // # objects into the curs
or so far | |
| BSONObj query; | | BSONObj query; | |
|
| | | int _queryOptions; | |
| | | OpTime _slaveReadTill; | |
| | | | |
|
| ClientCursor(auto_ptr<Cursor>& _c, const char *_ns, bool okToTimeou
t) : | | ClientCursor(int queryOptions, auto_ptr<Cursor>& _c, const char *_n
s) : | |
| _idleAgeMillis(0), _pinValue(0), | | _idleAgeMillis(0), _pinValue(0), | |
| _doingDeletes(false), | | _doingDeletes(false), | |
| ns(_ns), c(_c), | | ns(_ns), c(_c), | |
|
| pos(0) | | pos(0), _queryOptions(queryOptions) | |
| { | | { | |
|
| if( !okToTimeout ) | | if( queryOptions & QueryOption_NoCursorTimeout ) | |
| noTimeout(); | | noTimeout(); | |
| recursive_scoped_lock lock(ccmutex); | | recursive_scoped_lock lock(ccmutex); | |
| cursorid = allocCursorId_inlock(); | | cursorid = allocCursorId_inlock(); | |
| clientCursorsById.insert( make_pair(cursorid, this) ); | | clientCursorsById.insert( make_pair(cursorid, this) ); | |
| } | | } | |
| ~ClientCursor(); | | ~ClientCursor(); | |
| | | | |
| DiskLoc lastLoc() const { | | DiskLoc lastLoc() const { | |
| return _lastLoc; | | return _lastLoc; | |
| } | | } | |
| | | | |
| skipping to change at line 144 | | skipping to change at line 147 | |
| /** | | /** | |
| * do a dbtemprelease | | * do a dbtemprelease | |
| * note: caller should check matcher.docMatcher().atomic() first an
d not yield if atomic - | | * note: caller should check matcher.docMatcher().atomic() first an
d not yield if atomic - | |
| * we don't do herein as this->matcher (above) is only initia
lized for true queries/getmore. | | * we don't do herein as this->matcher (above) is only initia
lized for true queries/getmore. | |
| * (ie not set for remote/update) | | * (ie not set for remote/update) | |
| * @return if the cursor is still valid. | | * @return if the cursor is still valid. | |
| * if false is returned, then this ClientCursor should be c
onsidered deleted - | | * if false is returned, then this ClientCursor should be c
onsidered deleted - | |
| * in fact, the whole database could be gone. | | * in fact, the whole database could be gone. | |
| */ | | */ | |
| bool yield(); | | bool yield(); | |
|
| | | | |
| | | struct YieldLock { | |
| | | YieldLock( ClientCursor * cc ) | |
| | | : _cc( cc ) , _id( cc->cursorid ) , _doingDeletes( cc->_doi | |
| | | ngDeletes ) { | |
| | | cc->updateLocation(); | |
| | | _unlock = new dbtempreleasecond(); | |
| | | } | |
| | | ~YieldLock(){ | |
| | | assert( ! _unlock ); | |
| | | } | |
| | | | |
| | | bool stillOk(){ | |
| | | delete _unlock; | |
| | | _unlock = 0; | |
| | | | |
| | | if ( ClientCursor::find( _id , false ) == 0 ){ | |
| | | // i was deleted | |
| | | return false; | |
| | | } | |
| | | | |
| | | _cc->_doingDeletes = _doingDeletes; | |
| | | return true; | |
| | | } | |
| | | | |
| | | ClientCursor * _cc; | |
| | | CursorId _id; | |
| | | bool _doingDeletes; | |
| | | | |
| | | dbtempreleasecond * _unlock; | |
| | | | |
| | | }; | |
| | | | |
| | | YieldLock yieldHold(){ | |
| | | return YieldLock( this ); | |
| | | } | |
| | | | |
| | | // --- some pass through helpers for Cursor --- | |
| | | | |
| | | BSONObj indexKeyPattern() { | |
| | | return c->indexKeyPattern(); | |
| | | } | |
| | | | |
| | | bool ok(){ | |
| | | return c->ok(); | |
| | | } | |
| | | | |
| | | bool advance(){ | |
| | | return c->advance(); | |
| | | } | |
| | | | |
| | | bool currentMatches(){ | |
| | | if ( ! matcher.get() ) | |
| | | return true; | |
| | | return matcher->matchesCurrent( c.get() ); | |
| | | } | |
| | | | |
| | | BSONObj current(){ | |
| | | return c->current(); | |
| | | } | |
| | | | |
| private: | | private: | |
| void setLastLoc_inlock(DiskLoc); | | void setLastLoc_inlock(DiskLoc); | |
| | | | |
| static ClientCursor* find_inlock(CursorId id, bool warn = true) { | | static ClientCursor* find_inlock(CursorId id, bool warn = true) { | |
| CCById::iterator it = clientCursorsById.find(id); | | CCById::iterator it = clientCursorsById.find(id); | |
| if ( it == clientCursorsById.end() ) { | | if ( it == clientCursorsById.end() ) { | |
| if ( warn ) | | if ( warn ) | |
| OCCASIONALLY out() << "ClientCursor::find(): cursor not
found in map " << id << " (ok after a drop)\n"; | | OCCASIONALLY out() << "ClientCursor::find(): cursor not
found in map " << id << " (ok after a drop)\n"; | |
| return 0; | | return 0; | |
| } | | } | |
| | | | |
| skipping to change at line 201 | | skipping to change at line 264 | |
| } | | } | |
| | | | |
| /** | | /** | |
| * @param millis amount of idle passed time since last call | | * @param millis amount of idle passed time since last call | |
| */ | | */ | |
| bool shouldTimeout( unsigned millis ){ | | bool shouldTimeout( unsigned millis ){ | |
| _idleAgeMillis += millis; | | _idleAgeMillis += millis; | |
| return _idleAgeMillis > 600000 && _pinValue == 0; | | return _idleAgeMillis > 600000 && _pinValue == 0; | |
| } | | } | |
| | | | |
|
| | | void storeOpForSlave( DiskLoc last ); | |
| | | void updateSlaveLocation( CurOp& curop ); | |
| | | | |
| unsigned idleTime(){ | | unsigned idleTime(){ | |
| return _idleAgeMillis; | | return _idleAgeMillis; | |
| } | | } | |
| | | | |
| static void idleTimeReport(unsigned millis); | | static void idleTimeReport(unsigned millis); | |
| private: | | private: | |
| // cursors normally timeout after an inactivy period to prevent exc
ess memory use | | // cursors normally timeout after an inactivy period to prevent exc
ess memory use | |
| // setting this prevents timeout of the cursor in question. | | // setting this prevents timeout of the cursor in question. | |
| void noTimeout() { | | void noTimeout() { | |
| _pinValue++; | | _pinValue++; | |
| | | | |
End of changes. 7 change blocks. |
| 3 lines changed or deleted | | 70 lines changed or added | |
|
| concurrency.h | | concurrency.h | |
| | | | |
| skipping to change at line 32 | | skipping to change at line 32 | |
| name level | | name level | |
| Logstream::mutex 1 | | Logstream::mutex 1 | |
| ClientCursor::ccmutex 2 | | ClientCursor::ccmutex 2 | |
| dblock 3 | | dblock 3 | |
| | | | |
| End func name with _inlock to indicate "caller must lock before callin
g". | | End func name with _inlock to indicate "caller must lock before callin
g". | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
|
| #if BOOST_VERSION >= 103500 | | #include "../util/locks.h" | |
| #include <boost/thread/shared_mutex.hpp> | | | |
| #undef assert | | | |
| #define assert xassert | | | |
| #define HAVE_READLOCK | | | |
| #else | | | |
| #warning built with boost version 1.34 or older - limited concurrency | | | |
| #endif | | | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| inline bool readLockSupported(){ | | inline bool readLockSupported(){ | |
|
| #ifdef HAVE_READLOCK | | | |
| return true; | | return true; | |
|
| #else | | | |
| return false; | | | |
| #endif | | | |
| } | | } | |
| | | | |
| string sayClientState(); | | string sayClientState(); | |
| bool haveClient(); | | bool haveClient(); | |
| | | | |
| void curopWaitingForLock( int type ); | | void curopWaitingForLock( int type ); | |
| void curopGotLock(); | | void curopGotLock(); | |
| | | | |
| /* mutex time stats */ | | /* mutex time stats */ | |
| class MutexInfo { | | class MutexInfo { | |
| | | | |
| skipping to change at line 90 | | skipping to change at line 79 | |
| } | | } | |
| void getTimingInfo(unsigned long long &s, unsigned long long &tl) c
onst { | | void getTimingInfo(unsigned long long &s, unsigned long long &tl) c
onst { | |
| s = start; | | s = start; | |
| tl = timeLocked; | | tl = timeLocked; | |
| } | | } | |
| unsigned long long getTimeLocked() const { | | unsigned long long getTimeLocked() const { | |
| return timeLocked; | | return timeLocked; | |
| } | | } | |
| }; | | }; | |
| | | | |
|
| #ifdef HAVE_READLOCK | | | |
| //#if 0 | | | |
| class MongoMutex { | | class MongoMutex { | |
| MutexInfo _minfo; | | MutexInfo _minfo; | |
|
| boost::shared_mutex _m; | | RWLock _m; | |
| ThreadLocalValue<int> _state; | | ThreadLocalValue<int> _state; | |
| | | | |
| /* 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; | |
| public: | | public: | |
| /** | | /** | |
| * @return | | * @return | |
| * > 0 write lock | | * > 0 write lock | |
| | | | |
| skipping to change at line 116 | | skipping to change at line 103 | |
| * < 0 read lock | | * < 0 read lock | |
| */ | | */ | |
| int getState(){ return _state.get(); } | | int getState(){ return _state.get(); } | |
| void assertWriteLocked() { | | void assertWriteLocked() { | |
| assert( getState() > 0 ); | | assert( getState() > 0 ); | |
| DEV assert( !_releasedEarly.get() ); | | DEV assert( !_releasedEarly.get() ); | |
| } | | } | |
| bool atLeastReadLocked() { return _state.get() != 0; } | | bool atLeastReadLocked() { return _state.get() != 0; } | |
| void assertAtLeastReadLocked() { assert(atLeastReadLocked()); } | | void assertAtLeastReadLocked() { assert(atLeastReadLocked()); } | |
| | | | |
|
| bool _checkWriteLockAlready(){ | | void lock() { | |
| //DEV cout << "LOCK" << endl; | | //DEV cout << "LOCK" << endl; | |
| DEV assert( haveClient() ); | | DEV assert( haveClient() ); | |
| | | | |
| 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; | |
| } | | } | |
|
| | | | |
| massert( 10293 , (string)"internal error: locks are not upgrade
able: " + sayClientState() , s == 0 ); | | massert( 10293 , (string)"internal error: locks are not upgrade
able: " + sayClientState() , s == 0 ); | |
|
| | | | |
| return false; | | | |
| } | | | |
| | | | |
| void lock() { | | | |
| | | | |
| if ( _checkWriteLockAlready() ) | | | |
| return; | | | |
| | | | |
| _state.set(1); | | _state.set(1); | |
| | | | |
| curopWaitingForLock( 1 ); | | curopWaitingForLock( 1 ); | |
| _m.lock(); | | _m.lock(); | |
| curopGotLock(); | | curopGotLock(); | |
| | | | |
| _minfo.entered(); | | _minfo.entered(); | |
| } | | } | |
|
| | | | |
| bool lock_try() { | | | |
| if ( _checkWriteLockAlready() ) | | | |
| return true; | | | |
| | | | |
| curopWaitingForLock( 1 ); | | | |
| | | | |
| boost::system_time until = get_system_time(); | | | |
| until += boost::posix_time::milliseconds(0); | | | |
| bool got = _m.timed_lock( until ); | | | |
| curopGotLock(); | | | |
| | | | |
| if ( got ){ | | | |
| _minfo.entered(); | | | |
| _state.set(1); | | | |
| } | | | |
| | | | |
| return got; | | | |
| } | | | |
| | | | |
| void unlock() { | | void unlock() { | |
| //DEV cout << "UNLOCK" << endl; | | //DEV cout << "UNLOCK" << endl; | |
| int s = _state.get(); | | int s = _state.get(); | |
| if( s > 1 ) { | | if( s > 1 ) { | |
| _state.set(s-1); | | _state.set(s-1); | |
| return; | | return; | |
| } | | } | |
| if( s != 1 ) { | | if( s != 1 ) { | |
| if( _releasedEarly.get() ) { | | if( _releasedEarly.get() ) { | |
| _releasedEarly.set(false); | | _releasedEarly.set(false); | |
| | | | |
| skipping to change at line 222 | | skipping to change at line 179 | |
| } | | } | |
| | | | |
| bool lock_shared_try( int millis ) { | | bool lock_shared_try( int millis ) { | |
| int s = _state.get(); | | int s = _state.get(); | |
| if ( s ){ | | if ( s ){ | |
| // we already have a lock, so no need to try | | // we already have a lock, so no need to try | |
| lock_shared(); | | lock_shared(); | |
| return true; | | return true; | |
| } | | } | |
| | | | |
|
| boost::system_time until = get_system_time(); | | bool got = _m.lock_shared_try( millis ); | |
| until += boost::posix_time::milliseconds(2); | | | |
| bool got = _m.timed_lock_shared( until ); | | | |
| if ( got ) | | if ( got ) | |
| _state.set(-1); | | _state.set(-1); | |
| return got; | | return got; | |
| } | | } | |
| | | | |
| void unlock_shared() { | | void unlock_shared() { | |
| //DEV cout << " UNLOCKSHARED" << endl; | | //DEV cout << " UNLOCKSHARED" << endl; | |
| 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 */ | | assert( s > 1 ); /* we must have done a lock write first to
have s > 1 */ | |
| | | | |
| skipping to change at line 249 | | skipping to change at line 204 | |
| _state.set(s+1); | | _state.set(s+1); | |
| return; | | return; | |
| } | | } | |
| assert( s == -1 ); | | assert( s == -1 ); | |
| _state.set(0); | | _state.set(0); | |
| _m.unlock_shared(); | | _m.unlock_shared(); | |
| } | | } | |
| | | | |
| MutexInfo& info() { return _minfo; } | | MutexInfo& info() { return _minfo; } | |
| }; | | }; | |
|
| #else | | | |
| /* this will be for old versions of boost */ | | | |
| class MongoMutex { | | | |
| MutexInfo _minfo; | | | |
| boost::recursive_mutex m; | | | |
| ThreadLocalValue<bool> _releasedEarly; | | | |
| public: | | | |
| MongoMutex() { } | | | |
| void lock() { | | | |
| #ifdef HAVE_READLOCK | | | |
| m.lock(); | | | |
| #error this should be impossible? | | | |
| #else | | | |
| boost::detail::thread::lock_ops<boost::recursive_mutex>::lock(m | | | |
| ); | | | |
| #endif | | | |
| _minfo.entered(); | | | |
| } | | | |
| | | | |
| bool lock_try(){ | | | |
| lock(); | | | |
| return true; | | | |
| } | | | |
| | | | |
| void releaseEarly() { | | | |
| assertWriteLocked(); // aso must not be recursive, although we | | | |
| don't verify that in the old boost version | | | |
| assert( !_releasedEarly.get() ); | | | |
| _releasedEarly.set(true); | | | |
| _unlock(); | | | |
| } | | | |
| | | | |
| void _unlock() { | | | |
| _minfo.leaving(); | | | |
| #ifdef HAVE_READLOCK | | | |
| m.unlock(); | | | |
| #else | | | |
| boost::detail::thread::lock_ops<boost::recursive_mutex>::unlock | | | |
| (m); | | | |
| #endif | | | |
| } | | | |
| void unlock() { | | | |
| if( _releasedEarly.get() ) { | | | |
| _releasedEarly.set(false); | | | |
| return; | | | |
| } | | | |
| _unlock(); | | | |
| } | | | |
| | | | |
| void lock_shared() { lock(); } | | | |
| bool lock_shared_try( int millis ) { | | | |
| while ( millis-- ){ | | | |
| if ( getState() ){ | | | |
| sleepmillis(1); | | | |
| continue; | | | |
| } | | | |
| lock_shared(); | | | |
| return true; | | | |
| } | | | |
| return false; | | | |
| } | | | |
| | | | |
| void unlock_shared() { unlock(); } | | | |
| MutexInfo& info() { return _minfo; } | | | |
| void assertWriteLocked() { | | | |
| assert( info().isLocked() ); | | | |
| } | | | |
| void assertAtLeastReadLocked() { | | | |
| assert( info().isLocked() ); | | | |
| } | | | |
| bool atLeastReadLocked() { return info().isLocked(); } | | | |
| int getState(){ return info().isLocked() ? 1 : 0; } | | | |
| }; | | | |
| #endif | | | |
| | | | |
| extern MongoMutex &dbMutex; | | extern MongoMutex &dbMutex; | |
| | | | |
| void dbunlocking_write(); | | void dbunlocking_write(); | |
| void dbunlocking_read(); | | void dbunlocking_read(); | |
| | | | |
| struct writelock { | | struct writelock { | |
| writelock(const string& ns) { | | writelock(const string& ns) { | |
| dbMutex.lock(); | | dbMutex.lock(); | |
| } | | } | |
| | | | |
| skipping to change at line 366 | | skipping to change at line 250 | |
| dbunlocking_read(); | | dbunlocking_read(); | |
| dbMutex.unlock_shared(); | | dbMutex.unlock_shared(); | |
| } | | } | |
| } | | } | |
| bool got(){ | | bool got(){ | |
| return _got; | | return _got; | |
| } | | } | |
| bool _got; | | bool _got; | |
| }; | | }; | |
| | | | |
|
| struct writelocktry { | | | |
| writelocktry( const string&ns ){ | | | |
| _got = dbMutex.lock_try(); | | | |
| } | | | |
| ~writelocktry() { | | | |
| if ( _got ){ | | | |
| dbunlocking_write(); | | | |
| dbMutex.unlock(); | | | |
| } | | | |
| } | | | |
| bool got(){ | | | |
| return _got; | | | |
| } | | | |
| bool _got; | | | |
| }; | | | |
| | | | |
| struct atleastreadlock { | | struct atleastreadlock { | |
| atleastreadlock( const string& ns ){ | | atleastreadlock( const string& ns ){ | |
| _prev = dbMutex.getState(); | | _prev = dbMutex.getState(); | |
| if ( _prev == 0 ) | | if ( _prev == 0 ) | |
| dbMutex.lock_shared(); | | dbMutex.lock_shared(); | |
| } | | } | |
| ~atleastreadlock(){ | | ~atleastreadlock(){ | |
| if ( _prev == 0 ) | | if ( _prev == 0 ) | |
| dbMutex.unlock_shared(); | | dbMutex.unlock_shared(); | |
| } | | } | |
| | | | |
End of changes. 13 change blocks. |
| 140 lines changed or deleted | | 5 lines changed or added | |
|
| curop.h | | curop.h | |
| | | | |
| skipping to change at line 57 | | skipping to change at line 57 | |
| 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; | |
| char _ns[Namespace::MaxNsLen+2]; | | char _ns[Namespace::MaxNsLen+2]; | |
|
| struct sockaddr_in _remote; | | struct SockAddr _remote; | |
| | | | |
| char _queryBuf[256]; | | char _queryBuf[256]; | |
| | | | |
| void resetQuery(int x=0) { *((int *)_queryBuf) = x; } | | void resetQuery(int x=0) { *((int *)_queryBuf) = x; } | |
| | | | |
| OpDebug _debug; | | OpDebug _debug; | |
| | | | |
| ThreadSafeString _message; | | ThreadSafeString _message; | |
| ProgressMeter _progressMeter; | | ProgressMeter _progressMeter; | |
| | | | |
| | | | |
| skipping to change at line 121 | | skipping to change at line 121 | |
| void reset(){ | | void reset(){ | |
| _reset(); | | _reset(); | |
| _start = _checkpoint = 0; | | _start = _checkpoint = 0; | |
| _active = true; | | _active = true; | |
| _opNum = _nextOpNum++; | | _opNum = _nextOpNum++; | |
| _ns[0] = '?'; // just in case not set later | | _ns[0] = '?'; // just in case not set later | |
| _debug.reset(); | | _debug.reset(); | |
| resetQuery(); | | resetQuery(); | |
| } | | } | |
| | | | |
|
| void reset( const sockaddr_in & remote, int op ) { | | void reset( const SockAddr & remote, int op ) { | |
| reset(); | | reset(); | |
| _remote = remote; | | _remote = remote; | |
| _op = op; | | _op = op; | |
| } | | } | |
| | | | |
| void markCommand(){ | | void markCommand(){ | |
| _command = true; | | _command = true; | |
| } | | } | |
| | | | |
| void waitingForLock( int type ){ | | void waitingForLock( int type ){ | |
| | | | |
| skipping to change at line 207 | | skipping to change at line 207 | |
| } | | } | |
| | | | |
| void setQuery(const BSONObj& query) { | | void setQuery(const BSONObj& query) { | |
| if( query.objsize() > (int) sizeof(_queryBuf) ) { | | if( query.objsize() > (int) sizeof(_queryBuf) ) { | |
| resetQuery(1); // flag as too big and return | | resetQuery(1); // flag as too big and return | |
| return; | | return; | |
| } | | } | |
| memcpy(_queryBuf, query.objdata(), query.objsize()); | | memcpy(_queryBuf, query.objdata(), query.objsize()); | |
| } | | } | |
| | | | |
|
| | | Client * getClient() const { | |
| | | return _client; | |
| | | } | |
| | | | |
| CurOp( Client * client , CurOp * wrapped = 0 ) { | | CurOp( Client * client , CurOp * wrapped = 0 ) { | |
| _client = client; | | _client = client; | |
| _wrapped = wrapped; | | _wrapped = wrapped; | |
| if ( _wrapped ){ | | if ( _wrapped ){ | |
| _client->_curOp = this; | | _client->_curOp = this; | |
| } | | } | |
| _start = _checkpoint = 0; | | _start = _checkpoint = 0; | |
| _active = false; | | _active = false; | |
| _reset(); | | _reset(); | |
| _op = 0; | | _op = 0; | |
| | | | |
| skipping to change at line 240 | | skipping to change at line 244 | |
| if( ! cc().getAuthenticationInfo()->isAuthorized("admin") ) { | | if( ! cc().getAuthenticationInfo()->isAuthorized("admin") ) { | |
| BSONObjBuilder b; | | BSONObjBuilder b; | |
| b.append("err", "unauthorized"); | | b.append("err", "unauthorized"); | |
| return b.obj(); | | return b.obj(); | |
| } | | } | |
| return infoNoauth(); | | return infoNoauth(); | |
| } | | } | |
| | | | |
| BSONObj infoNoauth(); | | BSONObj infoNoauth(); | |
| | | | |
|
| string getRemoteString(){ | | string getRemoteString( bool incPort = true ){ | |
| stringstream ss; | | return _remote.toString(incPort); | |
| ss << inet_ntoa( _remote.sin_addr ) << ":" << ntohs( _remote.si | | | |
| n_port ); | | | |
| return ss.str(); | | | |
| } | | } | |
| | | | |
| ProgressMeter& setMessage( const char * msg , long long progressMet
erTotal = 0 , int secondsBetween = 3 ){ | | ProgressMeter& setMessage( const char * msg , long long progressMet
erTotal = 0 , int secondsBetween = 3 ){ | |
|
| _message = msg; | | | |
| if ( progressMeterTotal ){ | | if ( progressMeterTotal ){ | |
|
| assert( ! _progressMeter.isActive() ); | | if ( _progressMeter.isActive() ){ | |
| | | cout << "about to assert, old _message: " << _message < | |
| | | < endl; | |
| | | assert( ! _progressMeter.isActive() ); | |
| | | } | |
| _progressMeter.reset( progressMeterTotal , secondsBetween )
; | | _progressMeter.reset( progressMeterTotal , secondsBetween )
; | |
| } | | } | |
| else { | | else { | |
| _progressMeter.finished(); | | _progressMeter.finished(); | |
| } | | } | |
|
| | | | |
| | | _message = msg; | |
| | | | |
| return _progressMeter; | | return _progressMeter; | |
| } | | } | |
| | | | |
| string getMessage() const { return _message; } | | string getMessage() const { return _message; } | |
| ProgressMeter getProgressMeter() { return _progressMeter; } | | ProgressMeter getProgressMeter() { return _progressMeter; } | |
| | | | |
| friend class Client; | | friend class Client; | |
| }; | | }; | |
| | | | |
| /* 0 = ok | | /* 0 = ok | |
| | | | |
End of changes. 7 change blocks. |
| 9 lines changed or deleted | | 17 lines changed or added | |
|
| database.h | | database.h | |
| | | | |
| skipping to change at line 139 | | skipping to change at line 139 | |
| files.push_back(0); | | files.push_back(0); | |
| p = files[n]; | | p = files[n]; | |
| } | | } | |
| if ( p == 0 ) { | | if ( p == 0 ) { | |
| boost::filesystem::path fullName = fileName( n ); | | boost::filesystem::path fullName = fileName( n ); | |
| string fullNameString = fullName.string(); | | string fullNameString = fullName.string(); | |
| p = new MongoDataFile(n); | | p = new MongoDataFile(n); | |
| int minSize = 0; | | int minSize = 0; | |
| if ( n != 0 && files[ n - 1 ] ) | | if ( n != 0 && files[ n - 1 ] ) | |
| minSize = files[ n - 1 ]->getHeader()->fileLength; | | minSize = files[ n - 1 ]->getHeader()->fileLength; | |
|
| if ( sizeNeeded + MDFHeader::headerSize() > minSize ) | | if ( sizeNeeded + DataFileHeader::HeaderSize > minSize ) | |
| minSize = sizeNeeded + MDFHeader::headerSize(); | | minSize = sizeNeeded + DataFileHeader::HeaderSize; | |
| try { | | try { | |
| p->open( fullNameString.c_str(), minSize, preallocateOn
ly ); | | p->open( fullNameString.c_str(), minSize, preallocateOn
ly ); | |
| } | | } | |
| catch ( AssertionException& ) { | | catch ( AssertionException& ) { | |
| delete p; | | delete p; | |
| throw; | | throw; | |
| } | | } | |
| if ( preallocateOnly ) | | if ( preallocateOnly ) | |
| delete p; | | delete p; | |
| else | | else | |
| files[n] = p; | | files[n] = p; | |
| } | | } | |
| return preallocateOnly ? 0 : p; | | return preallocateOnly ? 0 : p; | |
| } | | } | |
| | | | |
|
| MongoDataFile* addAFile( int sizeNeeded, bool preallocateNextFile )
{ | | MongoDataFile* addAFile( int sizeNeeded = 0, bool preallocateNextFi
le = false ) { | |
| int n = (int) files.size(); | | int n = (int) files.size(); | |
| MongoDataFile *ret = getFile( n, sizeNeeded ); | | MongoDataFile *ret = getFile( n, sizeNeeded ); | |
| if ( preallocateNextFile ) | | if ( preallocateNextFile ) | |
| preallocateAFile(); | | preallocateAFile(); | |
| return ret; | | return ret; | |
| } | | } | |
| | | | |
| // 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 | |
| void preallocateAFile() { | | void preallocateAFile() { | |
| int n = (int) files.size(); | | int n = (int) files.size(); | |
| getFile( n, 0, true ); | | getFile( n, 0, true ); | |
| } | | } | |
| | | | |
|
| MongoDataFile* suitableFile( int sizeNeeded, bool preallocate ) { | | MongoDataFile* suitableFile( int sizeNeeded ) { | |
| MongoDataFile* f = newestFile(); | | MongoDataFile* f = newestFile(); | |
|
| if ( !f ) { | | | |
| f = addAFile( sizeNeeded, preallocate ); | | | |
| } | | | |
| for ( int i = 0; i < 8; i++ ) { | | for ( int i = 0; i < 8; i++ ) { | |
| if ( f->getHeader()->unusedLength >= sizeNeeded ) | | if ( f->getHeader()->unusedLength >= sizeNeeded ) | |
| break; | | break; | |
|
| f = addAFile( sizeNeeded, preallocate ); | | f = addAFile( sizeNeeded ); | |
| if ( f->getHeader()->fileLength >= MongoDataFile::maxSize()
) // this is as big as they get so might as well stop | | if ( f->getHeader()->fileLength >= MongoDataFile::maxSize()
) // this is as big as they get so might as well stop | |
| break; | | break; | |
| } | | } | |
| return f; | | return f; | |
| } | | } | |
| | | | |
| Extent* allocExtent( const char *ns, int size, bool capped ) { | | Extent* allocExtent( const char *ns, int size, bool capped ) { | |
| Extent *e = DataFileMgr::allocFromFreeList( ns, size, capped ); | | Extent *e = DataFileMgr::allocFromFreeList( ns, size, capped ); | |
| if( e ) return e; | | if( e ) return e; | |
|
| return suitableFile( size, !capped )->createExtent( ns, size, c
apped ); | | return suitableFile( size )->createExtent( ns, size, capped ); | |
| } | | } | |
| | | | |
| MongoDataFile* newestFile() { | | MongoDataFile* newestFile() { | |
| int n = (int) files.size(); | | int n = (int) files.size(); | |
|
| if ( n > 0 ) { | | if ( n > 0 ) n--; | |
| n--; | | | |
| } else { | | | |
| return 0; | | | |
| } | | | |
| return getFile(n); | | return getFile(n); | |
| } | | } | |
| | | | |
| /** | | /** | |
| * @return true if success, false otherwise | | * @return true if success, false otherwise | |
| */ | | */ | |
| bool setProfilingLevel( int newLevel , string& errmsg ); | | bool setProfilingLevel( int newLevel , string& errmsg ); | |
| | | | |
| void finishInit(); | | void finishInit(); | |
| | | | |
| | | | |
End of changes. 7 change blocks. |
| 14 lines changed or deleted | | 7 lines changed or added | |
|
| dbclient.h | | dbclient.h | |
| | | | |
| skipping to change at line 755 | | skipping to change at line 755 | |
| DBClientPaired *clientPaired; | | DBClientPaired *clientPaired; | |
| auto_ptr<MessagingPort> p; | | auto_ptr<MessagingPort> p; | |
| auto_ptr<SockAddr> server; | | auto_ptr<SockAddr> server; | |
| bool failed; // true if some sort of fatal error has ever happened | | bool failed; // true if some sort of fatal error has ever happened | |
| bool autoReconnect; | | bool autoReconnect; | |
| time_t lastReconnectTry; | | time_t lastReconnectTry; | |
| string serverAddress; // remember for reconnects | | string serverAddress; // remember for reconnects | |
| void _checkConnection(); | | void _checkConnection(); | |
| void checkConnection() { if( failed ) _checkConnection(); } | | void checkConnection() { if( failed ) _checkConnection(); } | |
| map< string, pair<string,string> > authCache; | | map< string, pair<string,string> > authCache; | |
|
| | | int _timeout; | |
| public: | | public: | |
| | | | |
| /** | | /** | |
| @param _autoReconnect if true, automatically reconnect on a conn
ection failure | | @param _autoReconnect if true, automatically reconnect on a conn
ection failure | |
| @param cp used by DBClientPaired. You do not need to specify th
is parameter | | @param cp used by DBClientPaired. You do not need to specify th
is parameter | |
|
| | | @param timeout tcp timeout in seconds - this is for read/write,
not connect | |
| */ | | */ | |
|
| DBClientConnection(bool _autoReconnect=false,DBClientPaired* cp=0) | | DBClientConnection(bool _autoReconnect=false,DBClientPaired* cp=0,i | |
| : | | nt timeout=0) : | |
| clientPaired(cp), failed(false), autoReconnect(_autoReconne | | clientPaired(cp), failed(false), autoReconnect(_autoReconne | |
| ct), lastReconnectTry(0) { } | | ct), lastReconnectTry(0), _timeout(timeout) { } | |
| | | | |
| /** Connect to a Mongo database server. | | /** Connect to a Mongo database server. | |
| | | | |
| If autoReconnect is true, you can try to use the DBClientConnect
ion even when | | If autoReconnect is true, you can try to use the DBClientConnect
ion even when | |
| 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 ) | |
|
| | | If you use IPv6 you must add a port number
( ::1:27017 ) | |
| @param errmsg any relevant error message will appended to the st
ring | | @param errmsg any relevant error message will appended to the st
ring | |
| @return false if fails to connect. | | @return false if fails to connect. | |
| */ | | */ | |
| virtual bool connect(const string &serverHostname, string& errmsg); | | virtual bool connect(const string &serverHostname, string& errmsg); | |
| | | | |
| /** Connect to a Mongo database server. Exception throwing version
. | | /** Connect to a Mongo database server. Exception throwing version
. | |
| Throws a UserException if cannot connect. | | Throws a UserException if cannot connect. | |
| | | | |
| If autoReconnect is true, you can try to use the DBClientConnect
ion even when | | If autoReconnect is true, you can try to use the DBClientConnect
ion even when | |
| false was returned -- it will try to connect again. | | false was returned -- it will try to connect again. | |
| | | | |
| skipping to change at line 910 | | skipping to change at line 913 | |
| /* 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() { | |
| master = ( ( master == Left ) ? NotSetR : NotSetL ); | | master = ( ( master == Left ) ? NotSetR : NotSetL ); | |
| } | | } | |
| | | | |
| string getServerAddress() const { | | string getServerAddress() const { | |
| return left.getServerAddress() + "," + right.getServerAddress()
; | | return left.getServerAddress() + "," + right.getServerAddress()
; | |
| } | | } | |
| | | | |
|
| DBClientConnection& masterConn(); | | | |
| DBClientConnection& slaveConn(); | | DBClientConnection& slaveConn(); | |
| | | | |
| /* TODO - not yet implemented. mongos may need these. */ | | /* TODO - not yet implemented. mongos may need these. */ | |
| virtual bool call( Message &toSend, Message &response, bool assertO
k=true ) { assert(false); return false; } | | virtual bool call( Message &toSend, Message &response, bool assertO
k=true ) { assert(false); return false; } | |
| virtual void say( Message &toSend ) { assert(false); } | | virtual void say( Message &toSend ) { assert(false); } | |
| virtual void sayPiggyBack( Message &toSend ) { assert(false); } | | virtual void sayPiggyBack( Message &toSend ) { assert(false); } | |
| virtual void checkResponse( const char *data, int nReturned ) { ass
ert(false); } | | virtual void checkResponse( const char *data, int nReturned ) { ass
ert(false); } | |
| | | | |
| bool isFailed() const { | | bool isFailed() const { | |
| // TODO: this really should check isFailed on current master as
well | | // TODO: this really should check isFailed on current master as
well | |
|
| return master < Left; | | return master > NotSetR; | |
| } | | } | |
| }; | | }; | |
| | | | |
|
| | | /** pings server to check if it's up | |
| | | */ | |
| | | bool serverAlive( const string &uri ); | |
| | | | |
| DBClientBase * createDirectClient(); | | DBClientBase * createDirectClient(); | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 7 change blocks. |
| 6 lines changed or deleted | | 12 lines changed or added | |
|
| goodies.h | | goodies.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 | |
| | | | |
|
| #if defined(_WIN32) | | | |
| # include <windows.h> | | | |
| #endif | | | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| #if !defined(_WIN32) && !defined(NOEXECINFO) && !defined(__freebsd__) && !d
efined(__sun__) | | #if !defined(_WIN32) && !defined(NOEXECINFO) && !defined(__freebsd__) && !d
efined(__sun__) | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
| #include <pthread.h> | | #include <pthread.h> | |
| #include <execinfo.h> | | #include <execinfo.h> | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| skipping to change at line 220 | | skipping to change at line 216 | |
| t.tv_nsec = 0; | | t.tv_nsec = 0; | |
| if ( nanosleep( &t , 0 ) ){ | | if ( nanosleep( &t , 0 ) ){ | |
| cout << "nanosleep failed" << endl; | | cout << "nanosleep failed" << endl; | |
| } | | } | |
| } | | } | |
| inline void sleepmicros(int s) { | | inline void sleepmicros(int s) { | |
| if ( s <= 0 ) | | if ( s <= 0 ) | |
| return; | | return; | |
| struct timespec t; | | struct timespec t; | |
| t.tv_sec = (int)(s / 1000000); | | t.tv_sec = (int)(s / 1000000); | |
|
| t.tv_nsec = s % 1000000; | | t.tv_nsec = 1000 * ( s % 1000000 ); | |
| if ( nanosleep( &t , 0 ) ){ | | struct timespec out; | |
| | | if ( nanosleep( &t , &out ) ){ | |
| cout << "nanosleep failed" << endl; | | cout << "nanosleep failed" << endl; | |
| } | | } | |
| } | | } | |
| inline void sleepmillis(int s) { | | inline void sleepmillis(int s) { | |
| sleepmicros( s * 1000 ); | | sleepmicros( s * 1000 ); | |
| } | | } | |
| #endif | | #endif | |
| | | | |
| // note this wraps | | // note this wraps | |
| inline int tdiff(unsigned told, unsigned tnew) { | | inline int tdiff(unsigned told, unsigned tnew) { | |
| | | | |
| skipping to change at line 289 | | skipping to change at line 286 | |
| class StaticObserver : boost::noncopyable { | | class StaticObserver : boost::noncopyable { | |
| public: | | public: | |
| ~StaticObserver() { __destroyingStatics = true; } | | ~StaticObserver() { __destroyingStatics = true; } | |
| }; | | }; | |
| | | | |
| // On pthread systems, it is an error to destroy a mutex while held. S
tatic global | | // On pthread systems, it is an error to destroy a mutex while held. S
tatic global | |
| // mutexes may be held upon shutdown in our implementation, and this wa
y we avoid | | // mutexes may be held upon shutdown in our implementation, and this wa
y we avoid | |
| // destroying them. | | // destroying them. | |
| class mutex : boost::noncopyable { | | class mutex : boost::noncopyable { | |
| public: | | public: | |
|
| mutex() { new (_buf) boost::mutex(); } | | mutex() { _m = new boost::mutex(); } | |
| ~mutex() { | | ~mutex() { | |
| if( !__destroyingStatics ) { | | if( !__destroyingStatics ) { | |
|
| boost().boost::mutex::~mutex(); | | delete _m; | |
| } | | } | |
| } | | } | |
| class scoped_lock : boost::noncopyable { | | class scoped_lock : boost::noncopyable { | |
| public: | | public: | |
| scoped_lock( mongo::mutex &m ) : _l( m.boost() ) {} | | scoped_lock( mongo::mutex &m ) : _l( m.boost() ) {} | |
| boost::mutex::scoped_lock &boost() { return _l; } | | boost::mutex::scoped_lock &boost() { return _l; } | |
| private: | | private: | |
| boost::mutex::scoped_lock _l; | | boost::mutex::scoped_lock _l; | |
| }; | | }; | |
| private: | | private: | |
|
| boost::mutex &boost() { return *( boost::mutex * )( _buf ); } | | boost::mutex &boost() { return *_m; } | |
| char _buf[ sizeof( boost::mutex ) ]; | | boost::mutex *_m; | |
| }; | | }; | |
| | | | |
| typedef mongo::mutex::scoped_lock scoped_lock; | | typedef mongo::mutex::scoped_lock scoped_lock; | |
| typedef boost::recursive_mutex::scoped_lock recursive_scoped_lock; | | typedef boost::recursive_mutex::scoped_lock recursive_scoped_lock; | |
| | | | |
| // simple scoped timer | | // simple scoped timer | |
| class Timer { | | class Timer { | |
| public: | | public: | |
| Timer() { | | Timer() { | |
| reset(); | | reset(); | |
| | | | |
End of changes. 5 change blocks. |
| 10 lines changed or deleted | | 7 lines changed or added | |
|
| hashtab.h | | hashtab.h | |
| | | | |
| skipping to change at line 39 | | skipping to change at line 39 | |
| | | | |
| #pragma pack(1) | | #pragma pack(1) | |
| | | | |
| /* you should define: | | /* you should define: | |
| | | | |
| int Key::hash() return > 0 always. | | int Key::hash() return > 0 always. | |
| */ | | */ | |
| | | | |
| template < | | template < | |
| class Key, | | class Key, | |
|
| class Type | | class Type, | |
| | | class PTR | |
| > | | > | |
| class HashTable : boost::noncopyable { | | class HashTable : boost::noncopyable { | |
| public: | | public: | |
| const char *name; | | const char *name; | |
| struct Node { | | struct Node { | |
| int hash; | | int hash; | |
| Key k; | | Key k; | |
| Type value; | | Type value; | |
| bool inUse() { | | bool inUse() { | |
| return hash != 0; | | return hash != 0; | |
| } | | } | |
| void setUnused() { | | void setUnused() { | |
| hash = 0; | | hash = 0; | |
| } | | } | |
|
| } *nodes; | | }; | |
| | | PTR _buf; | |
| int n; | | int n; | |
| int maxChain; | | int maxChain; | |
| | | | |
|
| | | Node& nodes(int i) { | |
| | | return *((Node*) _buf.at(i * sizeof(Node), sizeof(Node))); | |
| | | } | |
| | | | |
| int _find(const Key& k, bool& found) { | | int _find(const Key& k, bool& found) { | |
| found = false; | | found = false; | |
| int h = k.hash(); | | int h = k.hash(); | |
| int i = h % n; | | int i = h % n; | |
| int start = i; | | int start = i; | |
| int chain = 0; | | int chain = 0; | |
| int firstNonUsed = -1; | | int firstNonUsed = -1; | |
| while ( 1 ) { | | while ( 1 ) { | |
|
| if ( !nodes[i].inUse() ) { | | if ( !nodes(i).inUse() ) { | |
| if ( firstNonUsed < 0 ) | | if ( firstNonUsed < 0 ) | |
| firstNonUsed = i; | | firstNonUsed = i; | |
| } | | } | |
| | | | |
|
| if ( nodes[i].hash == h && nodes[i].k == k ) { | | if ( nodes(i).hash == h && nodes(i).k == k ) { | |
| if ( chain >= 200 ) | | if ( chain >= 200 ) | |
| out() << "warning: hashtable " << name << " long ch
ain " << endl; | | out() << "warning: hashtable " << name << " long ch
ain " << endl; | |
| found = true; | | found = true; | |
| return i; | | return i; | |
| } | | } | |
| chain++; | | chain++; | |
| i = (i+1) % n; | | i = (i+1) % n; | |
| if ( i == start ) { | | if ( i == start ) { | |
| // shouldn't get here / defensive for infinite loops | | // shouldn't get here / defensive for infinite loops | |
| out() << "error: hashtable " << name << " is full n:" <
< n << endl; | | out() << "error: hashtable " << name << " is full n:" <
< n << endl; | |
| | | | |
| skipping to change at line 95 | | skipping to change at line 101 | |
| if ( firstNonUsed >= 0 ) | | if ( firstNonUsed >= 0 ) | |
| return firstNonUsed; | | return firstNonUsed; | |
| out() << "error: hashtable " << name << " max chain n:"
<< n << endl; | | out() << "error: hashtable " << name << " max chain n:"
<< n << endl; | |
| return -1; | | return -1; | |
| } | | } | |
| } | | } | |
| } | | } | |
| | | | |
| public: | | public: | |
| /* buf must be all zeroes on initialization. */ | | /* buf must be all zeroes on initialization. */ | |
|
| HashTable(void *buf, int buflen, const char *_name) : name(_name) { | | HashTable(PTR buf, int buflen, const char *_name) : name(_name) { | |
| int m = sizeof(Node); | | int m = sizeof(Node); | |
| // out() << "hashtab init, buflen:" << buflen << " m:" << m <<
endl; | | // out() << "hashtab init, buflen:" << buflen << " m:" << m <<
endl; | |
| n = buflen / m; | | n = buflen / m; | |
| if ( (n & 1) == 0 ) | | if ( (n & 1) == 0 ) | |
| n--; | | n--; | |
| maxChain = (int) (n * 0.05); | | maxChain = (int) (n * 0.05); | |
|
| nodes = (Node *) buf; | | _buf = buf; | |
| | | //nodes = (Node *) buf; | |
| | | | |
| assert( sizeof(Node) == 628 ); | | assert( sizeof(Node) == 628 ); | |
| //out() << "HashTable() " << _name << " sizeof(node):" << sizeo
f(Node) << " n:" << n << endl; | | //out() << "HashTable() " << _name << " sizeof(node):" << sizeo
f(Node) << " n:" << n << endl; | |
| } | | } | |
| | | | |
| Type* get(const Key& k) { | | Type* get(const Key& k) { | |
| bool found; | | bool found; | |
| int i = _find(k, found); | | int i = _find(k, found); | |
| if ( found ) | | if ( found ) | |
|
| return &nodes[i].value; | | return &nodes(i).value; | |
| return 0; | | return 0; | |
| } | | } | |
| | | | |
| void kill(const Key& k) { | | void kill(const Key& k) { | |
| bool found; | | bool found; | |
| int i = _find(k, found); | | int i = _find(k, found); | |
| if ( i >= 0 && found ) { | | if ( i >= 0 && found ) { | |
|
| nodes[i].k.kill(); | | Node& n = nodes(i); | |
| nodes[i].setUnused(); | | n.k.kill(); | |
| | | n.setUnused(); | |
| } | | } | |
| } | | } | |
| /* | | /* | |
| void drop(const Key& k) { | | void drop(const Key& k) { | |
| bool found; | | bool found; | |
| int i = _find(k, found); | | int i = _find(k, found); | |
| if ( i >= 0 && found ) { | | if ( i >= 0 && found ) { | |
| nodes[i].setUnused(); | | nodes[i].setUnused(); | |
| } | | } | |
| } | | } | |
| */ | | */ | |
| /** returns false if too full */ | | /** returns false if too full */ | |
| bool put(const Key& k, const Type& value) { | | bool put(const Key& k, const Type& value) { | |
| bool found; | | bool found; | |
| int i = _find(k, found); | | int i = _find(k, found); | |
| if ( i < 0 ) | | if ( i < 0 ) | |
| return false; | | return false; | |
|
| | | Node& n = nodes(i); | |
| if ( !found ) { | | if ( !found ) { | |
|
| nodes[i].k = k; | | n.k = k; | |
| nodes[i].hash = k.hash(); | | n.hash = k.hash(); | |
| } | | } | |
| else { | | else { | |
|
| assert( nodes[i].hash == k.hash() ); | | assert( n.hash == k.hash() ); | |
| } | | } | |
|
| nodes[i].value = value; | | n.value = value; | |
| return true; | | return true; | |
| } | | } | |
| | | | |
| typedef void (*IteratorCallback)( const Key& k , Type& v ); | | typedef void (*IteratorCallback)( const Key& k , Type& v ); | |
| | | | |
| void iterAll( IteratorCallback callback ){ | | void iterAll( IteratorCallback callback ){ | |
| for ( int i=0; i<n; i++ ){ | | for ( int i=0; i<n; i++ ){ | |
|
| if ( ! nodes[i].inUse() ) | | if ( ! nodes(i).inUse() ) | |
| continue; | | continue; | |
|
| callback( nodes[i].k , nodes[i].value ); | | callback( nodes(i).k , nodes(i).value ); | |
| } | | } | |
| } | | } | |
| | | | |
| }; | | }; | |
| | | | |
| #pragma pack() | | #pragma pack() | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 15 change blocks. |
| 15 lines changed or deleted | | 24 lines changed or added | |
|
| jsobj.h | | jsobj.h | |
| | | | |
| skipping to change at line 532 | | skipping to change at line 532 | |
| /** Retrieve the regex flags (options) for a Regex element */ | | /** Retrieve the regex flags (options) for a Regex element */ | |
| const char *regexFlags() const { | | const char *regexFlags() const { | |
| const char *p = regex(); | | const char *p = regex(); | |
| return p + strlen(p) + 1; | | return p + strlen(p) + 1; | |
| } | | } | |
| | | | |
| /** like operator== but doesn't check the fieldname, | | /** like operator== but doesn't check the fieldname, | |
| just the value. | | just the value. | |
| */ | | */ | |
| bool valuesEqual(const BSONElement& r) const { | | bool valuesEqual(const BSONElement& r) const { | |
|
| switch( type() ) { | | return woCompare( r , false ) == 0; | |
| case NumberLong: | | | |
| return _numberLong() == r.numberLong() && r.isNumber(); | | | |
| case NumberDouble: | | | |
| return _numberDouble() == r.number() && r.isNumber(); | | | |
| case NumberInt: | | | |
| return _numberInt() == r.numberInt() && r.isNumber(); | | | |
| default: | | | |
| ; | | | |
| } | | | |
| bool match= valuesize() == r.valuesize() && | | | |
| memcmp(value(),r.value(),valuesize()) == 0; | | | |
| return match && canonicalType() == r.canonicalType(); | | | |
| } | | } | |
| | | | |
| /** Returns true if elements are equal. */ | | /** Returns true if elements are equal. */ | |
| bool operator==(const BSONElement& r) const { | | bool operator==(const BSONElement& r) const { | |
|
| if ( strcmp(fieldName(), r.fieldName()) != 0 ) | | return woCompare( r , true ) == 0; | |
| return false; | | | |
| return valuesEqual(r); | | | |
| } | | } | |
| | | | |
| /** Well ordered comparison. | | /** Well ordered comparison. | |
| @return <0: l<r. 0:l==r. >0:l>r | | @return <0: l<r. 0:l==r. >0:l>r | |
| order by type, field name, and field value. | | order by type, field name, and field value. | |
| If considerFieldName is true, pay attention to the field name. | | If considerFieldName is true, pay attention to the field name. | |
| */ | | */ | |
| int woCompare( const BSONElement &e, bool considerFieldName = true
) const; | | int woCompare( const BSONElement &e, bool considerFieldName = true
) const; | |
| | | | |
| const char * rawdata() const { | | const char * rawdata() const { | |
| | | | |
| skipping to change at line 597 | | skipping to change at line 583 | |
| bool isABSONObj() const { | | bool isABSONObj() const { | |
| switch( type() ){ | | switch( type() ){ | |
| case Object: | | case Object: | |
| case Array: | | case Array: | |
| return true; | | return true; | |
| default: | | default: | |
| return false; | | return false; | |
| } | | } | |
| } | | } | |
| | | | |
|
| | | OpTime optime() const { | |
| | | return OpTime( *reinterpret_cast< const unsigned long long* >( | |
| | | value() ) ); | |
| | | } | |
| | | | |
| Date_t timestampTime() const{ | | Date_t timestampTime() const{ | |
| unsigned long long t = ((unsigned int*)(value() + 4 ))[0]; | | unsigned long long t = ((unsigned int*)(value() + 4 ))[0]; | |
| return t * 1000; | | return t * 1000; | |
| } | | } | |
| unsigned int timestampInc() const{ | | unsigned int timestampInc() const{ | |
| return ((unsigned int*)(value() ))[0]; | | return ((unsigned int*)(value() ))[0]; | |
| } | | } | |
| | | | |
| const char * dbrefNS() const { | | const char * dbrefNS() const { | |
| uassert( 10063 , "not a dbref" , type() == DBRef ); | | uassert( 10063 , "not a dbref" , type() == DBRef ); | |
| | | | |
| skipping to change at line 759 | | skipping to change at line 749 | |
| ~BSONObj() { _objdata = 0; } | | ~BSONObj() { _objdata = 0; } | |
| | | | |
| void appendSelfToBufBuilder(BufBuilder& b) const { | | void appendSelfToBufBuilder(BufBuilder& b) const { | |
| assert( objsize() ); | | assert( objsize() ); | |
| b.append(reinterpret_cast<const void *>( objdata() ), objsize()
); | | b.append(reinterpret_cast<const void *>( objdata() ), objsize()
); | |
| } | | } | |
| | | | |
| /** Readable representation of a BSON object in an extended JSON-st
yle notation. | | /** Readable representation of a BSON object in an extended JSON-st
yle notation. | |
| This is an abbreviated representation which might be used for l
ogging. | | This is an abbreviated representation which might be used for l
ogging. | |
| */ | | */ | |
|
| string toString() const; | | string toString( bool isArray = false ) const; | |
| operator string() const { return toString(); } | | operator string() const { return toString(); } | |
| | | | |
| /** Properly formatted JSON string. */ | | /** Properly formatted JSON string. */ | |
| string jsonString( JsonStringFormat format = Strict ) const; | | string jsonString( JsonStringFormat format = Strict ) const; | |
| | | | |
| /** note: addFields always adds _id even if not specified */ | | /** note: addFields always adds _id even if not specified */ | |
| int addFields(BSONObj& from, set<string>& fields); /* returns n add
ed */ | | int addFields(BSONObj& from, set<string>& fields); /* returns n add
ed */ | |
| | | | |
| /** returns # of top level fields in the object | | /** returns # of top level fields in the object | |
| note: iterates to count the fields | | note: iterates to count the fields | |
| | | | |
| skipping to change at line 998 | | skipping to change at line 988 | |
| opALL = 0x0B, | | opALL = 0x0B, | |
| NIN = 0x0C, | | NIN = 0x0C, | |
| opEXISTS = 0x0D, | | opEXISTS = 0x0D, | |
| opMOD = 0x0E, | | opMOD = 0x0E, | |
| opTYPE = 0x0F, | | opTYPE = 0x0F, | |
| opREGEX = 0x10, | | opREGEX = 0x10, | |
| opOPTIONS = 0x11, | | opOPTIONS = 0x11, | |
| opELEM_MATCH = 0x12, | | opELEM_MATCH = 0x12, | |
| opNEAR = 0x13, | | opNEAR = 0x13, | |
| opWITHIN = 0x14, | | opWITHIN = 0x14, | |
|
| | | opMAX_DISTANCE=0x15 | |
| }; | | }; | |
| }; | | }; | |
| ostream& operator<<( ostream &s, const BSONObj &o ); | | ostream& operator<<( ostream &s, const BSONObj &o ); | |
| ostream& operator<<( ostream &s, const BSONElement &e ); | | ostream& operator<<( ostream &s, const BSONElement &e ); | |
| | | | |
| struct BSONArray: BSONObj { | | struct BSONArray: BSONObj { | |
| // Don't add anything other than forwarding constructors!!! | | // Don't add anything other than forwarding constructors!!! | |
| BSONArray(): BSONObj() {} | | BSONArray(): BSONObj() {} | |
| explicit BSONArray(const BSONObj& obj): BSONObj(obj) {} | | explicit BSONArray(const BSONObj& obj): BSONObj(obj) {} | |
| }; | | }; | |
| | | | |
| skipping to change at line 1661 | | skipping to change at line 1652 | |
| BSONArrayBuilder() : _i(0), _b() {} | | BSONArrayBuilder() : _i(0), _b() {} | |
| BSONArrayBuilder( BufBuilder &b ) : _i(0), _b(b) {} | | BSONArrayBuilder( BufBuilder &b ) : _i(0), _b(b) {} | |
| | | | |
| template <typename T> | | template <typename T> | |
| BSONArrayBuilder& append(const T& x){ | | BSONArrayBuilder& append(const T& x){ | |
| _b.append(num().c_str(), x); | | _b.append(num().c_str(), x); | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| BSONArrayBuilder& append(const BSONElement& e){ | | BSONArrayBuilder& append(const BSONElement& e){ | |
|
| _b.appendAs(e, num().c_str()); | | _b.appendAs(e, num()); | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| template <typename T> | | template <typename T> | |
| BSONArrayBuilder& operator<<(const T& x){ | | BSONArrayBuilder& operator<<(const T& x){ | |
| return append(x); | | return append(x); | |
| } | | } | |
| | | | |
| void appendNull() { | | void appendNull() { | |
| _b.appendNull(num().c_str()); | | _b.appendNull(num().c_str()); | |
| | | | |
| skipping to change at line 1899 | | skipping to change at line 1890 | |
| BSONObjIterator it(x); | | BSONObjIterator it(x); | |
| while ( it.moreWithEOO() ) { | | while ( it.moreWithEOO() ) { | |
| BSONElement e = it.next(); | | BSONElement e = it.next(); | |
| if ( e.eoo() ) break; | | if ( e.eoo() ) break; | |
| append(e); | | append(e); | |
| } | | } | |
| return *this; | | return *this; | |
| } | | } | |
| | | | |
| inline bool BSONObj::isValid(){ | | inline bool BSONObj::isValid(){ | |
|
| return objsize() > 0 && objsize() <= 1024 * 1024 * 8; | | int x = objsize(); | |
| | | return x > 0 && x <= 1024 * 1024 * 8; | |
| } | | } | |
| | | | |
| inline bool BSONObj::getObjectID(BSONElement& e) const { | | inline bool BSONObj::getObjectID(BSONElement& e) const { | |
| BSONElement f = getField("_id"); | | BSONElement f = getField("_id"); | |
| if( !f.eoo() ) { | | if( !f.eoo() ) { | |
| e = f; | | e = f; | |
| return true; | | return true; | |
| } | | } | |
| return false; | | return false; | |
| } | | } | |
| | | | |
End of changes. 7 change blocks. |
| 19 lines changed or deleted | | 12 lines changed or added | |
|
| message.h | | message.h | |
| | | | |
| skipping to change at line 25 | | skipping to change at line 25 | |
| * limitations under the License. | | * limitations under the License. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "../util/sock.h" | | #include "../util/sock.h" | |
| #include "../util/atomic_int.h" | | #include "../util/atomic_int.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| | | extern bool noUnixSocket; | |
| | | | |
| class Message; | | class Message; | |
| class MessagingPort; | | class MessagingPort; | |
| class PiggyBackData; | | class PiggyBackData; | |
| typedef AtomicUInt MSGID; | | typedef AtomicUInt MSGID; | |
| | | | |
| class Listener { | | class Listener { | |
| public: | | public: | |
|
| Listener(const string &_ip, int p) : ip(_ip), port(p) { } | | Listener(const string &ip, int p, bool logConnect=true ) : _ip(ip),
_port(p), _logConnect(logConnect) { } | |
| virtual ~Listener() {} | | virtual ~Listener() {} | |
|
| bool init(); // set up socket | | void initAndListen(); // never returns unless error (start a thread | |
| int socket() const { return sock; } | | ) | |
| void listen(); // never returns (start a thread) | | | |
| | | | |
| /* spawn a thread, etc., then return */ | | /* spawn a thread, etc., then return */ | |
|
| virtual void accepted(MessagingPort *mp) = 0; | | virtual void accepted(int sock, const SockAddr& from); | |
| | | virtual void accepted(MessagingPort *mp){ | |
| | | assert(!"You must overwrite one of the accepted methods"); | |
| | | } | |
| | | | |
| private: | | private: | |
|
| string ip; | | string _ip; | |
| int port; | | int _port; | |
| int sock; | | bool _logConnect; | |
| }; | | }; | |
| | | | |
| class AbstractMessagingPort { | | class AbstractMessagingPort { | |
| public: | | public: | |
| virtual ~AbstractMessagingPort() { } | | virtual ~AbstractMessagingPort() { } | |
| virtual void reply(Message& received, Message& response, MSGID resp
onseTo) = 0; // like the reply below, but doesn't rely on received.data sti
ll being available | | virtual void reply(Message& received, Message& response, MSGID resp
onseTo) = 0; // like the reply below, but doesn't rely on received.data sti
ll being available | |
| virtual void reply(Message& received, Message& response) = 0; | | virtual void reply(Message& received, Message& response) = 0; | |
| | | | |
| virtual unsigned remotePort() = 0 ; | | virtual unsigned remotePort() = 0 ; | |
| }; | | }; | |
| | | | |
| class MessagingPort : public AbstractMessagingPort { | | class MessagingPort : public AbstractMessagingPort { | |
| public: | | public: | |
|
| MessagingPort(int sock, SockAddr& farEnd); | | MessagingPort(int sock, const SockAddr& farEnd); | |
| MessagingPort(); | | | |
| | | // in some cases the timeout will actually be 2x this value - eg we | |
| | | do a partial send, | |
| | | // then the timeout fires, then we try to send again, then the time | |
| | | out fires again with | |
| | | // no data sent, then we detect that the other side is down | |
| | | MessagingPort(int timeout = 0); | |
| | | | |
| virtual ~MessagingPort(); | | virtual ~MessagingPort(); | |
| | | | |
| void shutdown(); | | void shutdown(); | |
| | | | |
| bool connect(SockAddr& farEnd); | | bool connect(SockAddr& farEnd); | |
| | | | |
| /* it's assumed if you reuse a message object, that it doesn't cros
s MessagingPort's. | | /* it's assumed if you reuse a message object, that it doesn't cros
s MessagingPort's. | |
| also, the Message data will go out of scope on the subsequent re
cv call. | | also, the Message data will go out of scope on the subsequent re
cv call. | |
| */ | | */ | |
| bool recv(Message& m); | | bool recv(Message& m); | |
| void reply(Message& received, Message& response, MSGID responseTo); | | void reply(Message& received, Message& response, MSGID responseTo); | |
| void reply(Message& received, Message& response); | | void reply(Message& received, Message& response); | |
| bool call(Message& toSend, Message& response); | | bool call(Message& toSend, Message& response); | |
| void say(Message& toSend, int responseTo = -1); | | void say(Message& toSend, int responseTo = -1); | |
| | | | |
| void piggyBack( Message& toSend , int responseTo = -1 ); | | void piggyBack( Message& toSend , int responseTo = -1 ); | |
| | | | |
| virtual unsigned remotePort(); | | virtual unsigned remotePort(); | |
| | | | |
|
| int send( const char * data , const int len ); | | // send len or throw SocketException | |
| int recv( char * data , int max ); | | void send( const char * data , int len, const char *context ); | |
| | | // recv len or throw SocketException | |
| | | void recv( char * data , int len ); | |
| | | | |
| | | int unsafe_recv( char *buf, int max ); | |
| private: | | private: | |
| int sock; | | int sock; | |
| PiggyBackData * piggyBackData; | | PiggyBackData * piggyBackData; | |
| public: | | public: | |
| SockAddr farEnd; | | SockAddr farEnd; | |
|
| | | int _timeout; | |
| | | | |
| friend class PiggyBackData; | | friend class PiggyBackData; | |
| }; | | }; | |
| | | | |
| //#pragma pack() | | //#pragma pack() | |
| #pragma pack(1) | | #pragma pack(1) | |
| | | | |
| enum Operations { | | enum Operations { | |
| opReply = 1, /* reply. responseTo is set. */ | | opReply = 1, /* reply. responseTo is set. */ | |
| dbMsg = 1000, /* generic msg command followed by a string */ | | dbMsg = 1000, /* generic msg command followed by a string */ | |
| | | | |
End of changes. 8 change blocks. |
| 12 lines changed or deleted | | 29 lines changed or added | |
|
| mmap.h | | mmap.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. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| class MemoryMappedFile { | | /* the administrative-ish stuff here */ | |
| | | class MongoFile { | |
| | | protected: | |
| | | virtual void close() = 0; | |
| | | virtual void flush(bool sync) = 0; | |
| | | | |
| | | void created(); /* subclass must call after create */ | |
| | | void destroyed(); /* subclass must call in destructor */ | |
| public: | | public: | |
|
| | | virtual long length() = 0; | |
| | | | |
| enum Options { | | enum Options { | |
|
| SEQUENTIAL = 1 | | SEQUENTIAL = 1 // hint - e.g. FILE_FLAG_SEQUENTIAL_SCAN on wind | |
| | | ows | |
| | | }; | |
| | | | |
| | | virtual ~MongoFile() {} | |
| | | | |
| | | static int flushAll( bool sync ); // returns n flushed | |
| | | static long long totalMappedLength(); | |
| | | static void closeAllFiles( stringstream &message ); | |
| | | | |
| | | /* can be "overriden" if necessary */ | |
| | | static bool exists(boost::filesystem::path p) { | |
| | | return boost::filesystem::exists(p); | |
| | | } | |
| | | }; | |
| | | | |
| | | class MFTemplate : public MongoFile { | |
| | | protected: | |
| | | virtual void close(); | |
| | | virtual void flush(bool sync); | |
| | | public: | |
| | | virtual long length(); | |
| | | | |
| | | class Pointer { | |
| | | public: | |
| | | void* at(int offset, int len); | |
| | | void grow(int offset, int len); | |
| | | bool isNull() const; | |
| | | }; | |
| | | | |
| | | Pointer map( const char *filename ); | |
| | | Pointer map(const char *_filename, long &length, int options=0); | |
| | | }; | |
| | | | |
| | | class MemoryMappedFile : public MongoFile { | |
| | | public: | |
| | | class Pointer { | |
| | | char *_base; | |
| | | public: | |
| | | Pointer() : _base(0) { } | |
| | | Pointer(void *p) : _base((char*) p) { } | |
| | | void* at(int offset, int maxLen) { return _base + offset; } | |
| | | void grow(int offset, int len) { /* no action requir | |
| | | ed with mem mapped file */ } | |
| | | bool isNull() const { return _base == 0; } | |
| }; | | }; | |
| | | | |
| MemoryMappedFile(); | | MemoryMappedFile(); | |
|
| ~MemoryMappedFile(); /* closes the file if open */ | | ~MemoryMappedFile() { | |
| | | destroyed(); | |
| | | close(); | |
| | | } | |
| void close(); | | void close(); | |
| | | | |
|
| // Throws exception if file doesn't exist. | | // 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 ); | |
|
| | | /*Pointer pmap( const char *filename ) { | |
| | | void *p = map(filename); | |
| | | uassert(13077, "couldn't open/map file", p); | |
| | | return Pointer(p); | |
| | | }*/ | |
| | | | |
| /* Creates with length if DNE, otherwise uses existing file length, | | /* Creates with length if DNE, otherwise uses existing file length, | |
| passed length. | | passed length. | |
| */ | | */ | |
| void* map(const char *filename, long &length, int options = 0 ); | | void* map(const char *filename, long &length, int options = 0 ); | |
| | | | |
| void flush(bool sync); | | void flush(bool sync); | |
| | | | |
|
| void* viewOfs() { | | /*void* viewOfs() { | |
| return view; | | return view; | |
|
| } | | }*/ | |
| | | | |
| long length() { | | long length() { | |
| return len; | | return len; | |
| } | | } | |
| | | | |
|
| static void updateLength( const char *filename, long &length ); | | | |
| | | | |
| static long long totalMappedLength(); | | | |
| static void closeAllFiles( stringstream &message ); | | | |
| static int flushAll( bool sync ); | | | |
| | | | |
| private: | | private: | |
|
| void created(); | | static void updateLength( const char *filename, long &length ); | |
| | | | |
| HANDLE fd; | | HANDLE fd; | |
| HANDLE maphandle; | | HANDLE maphandle; | |
| void *view; | | void *view; | |
| long len; | | long len; | |
|
| string _filename; | | | |
| }; | | }; | |
| | | | |
| void printMemInfo( const char * where ); | | void printMemInfo( const char * where ); | |
| | | | |
|
| | | #include "ramstore.h" | |
| | | | |
| | | //#define _RAMSTORE | |
| | | #if defined(_RAMSTORE) | |
| | | typedef RamStoreFile MMF; | |
| | | #else | |
| | | typedef MemoryMappedFile MMF; | |
| | | #endif | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 12 change blocks. |
| 14 lines changed or deleted | | 76 lines changed or added | |
|
| namespace.h | | namespace.h | |
| | | | |
| skipping to change at line 604 | | skipping to change at line 605 | |
| add_ns( ns, details ); | | add_ns( ns, details ); | |
| } | | } | |
| | | | |
| void add_ns( const char *ns, const NamespaceDetails &details
) { | | void add_ns( const char *ns, const NamespaceDetails &details
) { | |
| init(); | | init(); | |
| Namespace n(ns); | | Namespace n(ns); | |
| uassert( 10081 , "too many namespaces/collections", ht->put(n,
details)); | | uassert( 10081 , "too many namespaces/collections", ht->put(n,
details)); | |
| } | | } | |
| | | | |
| /* just for diagnostics */ | | /* just for diagnostics */ | |
|
| size_t detailsOffset(NamespaceDetails *d) { | | /*size_t detailsOffset(NamespaceDetails *d) { | |
| if ( !ht ) | | if ( !ht ) | |
| return -1; | | return -1; | |
| return ((char *) d) - (char *) ht->nodes; | | return ((char *) d) - (char *) ht->nodes; | |
|
| } | | }*/ | |
| | | | |
| /* extra space for indexes when more than 10 */ | | /* extra space for indexes when more than 10 */ | |
| NamespaceDetails::Extra* allocExtra(const char *ns) { | | NamespaceDetails::Extra* allocExtra(const char *ns) { | |
| Namespace n(ns); | | Namespace n(ns); | |
| Namespace extra(n.extraName().c_str()); // throws userexception
if ns name too long | | Namespace extra(n.extraName().c_str()); // throws userexception
if ns name too long | |
| NamespaceDetails *d = details(ns); | | NamespaceDetails *d = details(ns); | |
| massert( 10350 , "allocExtra: base ns missing?", d ); | | massert( 10350 , "allocExtra: base ns missing?", d ); | |
| assert( d->extraOffset == 0 ); | | assert( d->extraOffset == 0 ); | |
| massert( 10351 , "allocExtra: extra already exists", ht->get(e
xtra) == 0 ); | | massert( 10351 , "allocExtra: extra already exists", ht->get(e
xtra) == 0 ); | |
| NamespaceDetails::Extra temp; | | NamespaceDetails::Extra temp; | |
| | | | |
| skipping to change at line 667 | | skipping to change at line 668 | |
| } | | } | |
| | | | |
| bool allocated() const { | | bool allocated() const { | |
| return ht != 0; | | return ht != 0; | |
| } | | } | |
| | | | |
| private: | | private: | |
| boost::filesystem::path path() const; | | boost::filesystem::path path() const; | |
| void maybeMkdir() const; | | void maybeMkdir() const; | |
| | | | |
|
| MemoryMappedFile f; | | MMF f; | |
| HashTable<Namespace,NamespaceDetails> *ht; | | HashTable<Namespace,NamespaceDetails,MMF::Pointer> *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; | |
|
| | | extern string lockfilepath; // --lockfilepath param | |
| | | | |
| // 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 ); | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 4 change blocks. |
| 4 lines changed or deleted | | 5 lines changed or added | |
|
| pdfile.h | | pdfile.h | |
| | | | |
| skipping to change at line 37 | | skipping to change at line 37 | |
| | | | |
| #include "../stdafx.h" | | #include "../stdafx.h" | |
| #include "../util/mmap.h" | | #include "../util/mmap.h" | |
| #include "diskloc.h" | | #include "diskloc.h" | |
| #include "jsobjmanipulator.h" | | #include "jsobjmanipulator.h" | |
| #include "namespace.h" | | #include "namespace.h" | |
| #include "client.h" | | #include "client.h" | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
|
| class MDFHeader; | | class DataFileHeader; | |
| class Extent; | | class Extent; | |
| class Record; | | class Record; | |
| class Cursor; | | class Cursor; | |
| class OpDebug; | | class OpDebug; | |
| | | | |
| void dropDatabase(const char *ns); | | void dropDatabase(const char *ns); | |
| bool repairDatabase(const char *ns, string &errmsg, bool preserveCloned
FilesOnFailure = false, bool backupOriginalFiles = false); | | bool repairDatabase(const char *ns, string &errmsg, bool preserveCloned
FilesOnFailure = false, bool backupOriginalFiles = false); | |
| | | | |
| /* low level - only drops this ns */ | | /* low level - only drops this ns */ | |
| void dropNS(const string& dropNs); | | void dropNS(const string& dropNs); | |
| | | | |
| skipping to change at line 59 | | skipping to change at line 59 | |
| /* deletes this ns, indexes and cursors */ | | /* deletes this ns, indexes and cursors */ | |
| void dropCollection( const string &name, string &errmsg, BSONObjBuilder
&result ); | | void dropCollection( const string &name, string &errmsg, BSONObjBuilder
&result ); | |
| bool userCreateNS(const char *ns, BSONObj j, string& err, bool logForRe
plication); | | bool userCreateNS(const char *ns, BSONObj j, string& err, bool logForRe
plication); | |
| auto_ptr<Cursor> findTableScan(const char *ns, const BSONObj& order, co
nst DiskLoc &startLoc=DiskLoc()); | | auto_ptr<Cursor> findTableScan(const char *ns, const BSONObj& order, co
nst DiskLoc &startLoc=DiskLoc()); | |
| | | | |
| // -1 if library unavailable. | | // -1 if library unavailable. | |
| boost::intmax_t freeSpace(); | | boost::intmax_t freeSpace(); | |
| | | | |
| /*---------------------------------------------------------------------
*/ | | /*---------------------------------------------------------------------
*/ | |
| | | | |
|
| class MDFHeader; | | | |
| class MongoDataFile { | | class MongoDataFile { | |
| friend class DataFileMgr; | | friend class DataFileMgr; | |
| friend class BasicCursor; | | friend class BasicCursor; | |
| public: | | public: | |
| MongoDataFile(int fn) : fileNo(fn) { } | | MongoDataFile(int fn) : fileNo(fn) { } | |
| 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); | |
| | | | |
|
| MDFHeader *getHeader() { | | DataFileHeader *getHeader() { | |
| return header; | | return header; | |
| } | | } | |
| | | | |
| /* return max size an extent may be */ | | /* return max size an extent may be */ | |
| static int maxSize(); | | static int maxSize(); | |
| | | | |
| private: | | private: | |
| int defaultSize( const char *filename ) const; | | int defaultSize( const char *filename ) const; | |
| | | | |
| Extent* getExtent(DiskLoc loc); | | Extent* getExtent(DiskLoc loc); | |
| Extent* _getExtent(DiskLoc loc); | | Extent* _getExtent(DiskLoc loc); | |
| Record* recordAt(DiskLoc dl); | | Record* recordAt(DiskLoc dl); | |
|
| | | Record* makeRecord(DiskLoc dl, int size); | |
| | | void grow(DiskLoc dl, int size); | |
| | | | |
|
| MemoryMappedFile mmf; | | MMF mmf; | |
| MDFHeader *header; | | MMF::Pointer _p; | |
| | | DataFileHeader *header; | |
| int fileNo; | | int fileNo; | |
| }; | | }; | |
| | | | |
| class DataFileMgr { | | class DataFileMgr { | |
| friend class BasicCursor; | | friend class BasicCursor; | |
| public: | | public: | |
| void init(const string& path ); | | void init(const string& path ); | |
| | | | |
| /* see if we can find an extent of the right size in the freelist.
*/ | | /* see if we can find an extent of the right size in the freelist.
*/ | |
| static Extent* allocFromFreeList(const char *ns, int approxSize, bo
ol capped = false); | | static Extent* allocFromFreeList(const char *ns, int approxSize, bo
ol capped = false); | |
| | | | |
| skipping to change at line 122 | | skipping to change at line 124 | |
| static auto_ptr<Cursor> findAll(const char *ns, const DiskLoc &star
tLoc = DiskLoc()); | | static auto_ptr<Cursor> findAll(const char *ns, const DiskLoc &star
tLoc = DiskLoc()); | |
| | | | |
| /* special version of insert for transaction logging -- streamlined
a bit. | | /* special version of insert for transaction logging -- streamlined
a bit. | |
| assumes ns is capped and no indexes | | assumes ns is capped and no indexes | |
| no _id field check | | no _id field check | |
| */ | | */ | |
| Record* fast_oplog_insert(NamespaceDetails *d, const char *ns, int
len); | | Record* fast_oplog_insert(NamespaceDetails *d, const char *ns, int
len); | |
| | | | |
| static Extent* getExtent(const DiskLoc& dl); | | static Extent* getExtent(const DiskLoc& dl); | |
| static Record* getRecord(const DiskLoc& dl); | | static Record* getRecord(const DiskLoc& dl); | |
|
| | | static DeletedRecord* makeDeletedRecord(const DiskLoc& dl, int len) | |
| | | ; | |
| | | static void grow(const DiskLoc& dl, int len); | |
| | | | |
| /* does not clean up indexes, etc. : just deletes the record in the
pdfile. */ | | /* does not clean up indexes, etc. : just deletes the record in the
pdfile. */ | |
| void _deleteRecord(NamespaceDetails *d, const char *ns, Record *tod
elete, const DiskLoc& dl); | | void _deleteRecord(NamespaceDetails *d, const char *ns, Record *tod
elete, const DiskLoc& dl); | |
| | | | |
| private: | | private: | |
| vector<MongoDataFile *> files; | | vector<MongoDataFile *> files; | |
| }; | | }; | |
| | | | |
| extern DataFileMgr theDataFileMgr; | | extern DataFileMgr theDataFileMgr; | |
| | | | |
| | | | |
| skipping to change at line 200 | | skipping to change at line 204 | |
| DiskLoc myLoc; | | DiskLoc myLoc; | |
| DiskLoc xnext, xprev; /* next/prev extent for this namespace */ | | DiskLoc xnext, xprev; /* next/prev extent for this namespace */ | |
| | | | |
| /* which namespace this extent is for. this is just for troublesho
oting really | | /* which namespace this extent is for. this is just for troublesho
oting really | |
| and won't even be correct if the collection were renamed! | | and won't even be correct if the collection were renamed! | |
| */ | | */ | |
| Namespace nsDiagnostic; | | Namespace nsDiagnostic; | |
| | | | |
| int length; /* size of the extent, including these fields */ | | int length; /* size of the extent, including these fields */ | |
| DiskLoc firstRecord, lastRecord; | | DiskLoc firstRecord, lastRecord; | |
|
| char extentData[4]; | | char _extentData[4]; | |
| | | | |
| | | static int HeaderSize() { return sizeof(Extent)-4; } | |
| | | | |
| bool validates() { | | bool validates() { | |
| return !(firstRecord.isNull() ^ lastRecord.isNull()) && | | return !(firstRecord.isNull() ^ lastRecord.isNull()) && | |
| length >= 0 && !myLoc.isNull(); | | length >= 0 && !myLoc.isNull(); | |
| } | | } | |
| | | | |
| void dump(iostream& s) { | | void dump(iostream& s) { | |
| s << " loc:" << myLoc.toString() << " xnext:" << xnext.toStr
ing() << " xprev:" << xprev.toString() << '\n'; | | s << " loc:" << myLoc.toString() << " xnext:" << xnext.toStr
ing() << " xprev:" << xprev.toString() << '\n'; | |
| s << " nsdiag:" << nsDiagnostic.buf << '\n'; | | s << " nsdiag:" << nsDiagnostic.buf << '\n'; | |
| s << " size:" << length << " firstRecord:" << firstRecord.to
String() << " lastRecord:" << lastRecord.toString() << '\n'; | | s << " size:" << length << " firstRecord:" << firstRecord.to
String() << " lastRecord:" << lastRecord.toString() << '\n'; | |
| | | | |
| skipping to change at line 257 | | skipping to change at line 263 | |
| ---------------------- | | ---------------------- | |
| Extent (for a particular namespace) | | Extent (for a particular namespace) | |
| Record | | Record | |
| ... | | ... | |
| Record (some chained for unused space) | | Record (some chained for unused space) | |
| ---------------------- | | ---------------------- | |
| more Extents... | | more Extents... | |
| ---------------------- | | ---------------------- | |
| */ | | */ | |
| | | | |
|
| /* data file header */ | | class DataFileHeader { | |
| class MDFHeader { | | | |
| public: | | public: | |
| int version; | | int version; | |
| 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]; | | char data[4]; | |
| | | | |
|
| static int headerSize() { | | enum { HeaderSize = 8192 }; | |
| return sizeof(MDFHeader) - 4; | | | |
| } | | | |
| | | | |
| bool currentVersion() const { | | bool currentVersion() const { | |
| return ( version == VERSION ) && ( versionMinor == VERSION_MINO
R ); | | return ( version == VERSION ) && ( versionMinor == VERSION_MINO
R ); | |
| } | | } | |
| | | | |
| bool uninitialized() const { | | bool uninitialized() const { | |
| if ( version == 0 ) return true; | | if ( version == 0 ) return true; | |
| return false; | | return false; | |
| } | | } | |
| | | | |
|
| Record* getRecord(DiskLoc dl) { | | /*Record* __getRecord(DiskLoc dl) { | |
| int ofs = dl.getOfs(); | | int ofs = dl.getOfs(); | |
|
| assert( ofs >= headerSize() ); | | assert( ofs >= HeaderSize ); | |
| return (Record*) (((char *) this) + ofs); | | return (Record*) (((char *) this) + ofs); | |
|
| } | | }*/ | |
| | | | |
| void init(int fileno, int filelength) { | | void init(int fileno, int filelength) { | |
| if ( uninitialized() ) { | | if ( uninitialized() ) { | |
| assert(filelength > 32768 ); | | assert(filelength > 32768 ); | |
|
| assert( headerSize() == 8192 ); | | assert( HeaderSize == 8192 ); | |
| fileLength = filelength; | | fileLength = filelength; | |
| version = VERSION; | | version = VERSION; | |
| versionMinor = VERSION_MINOR; | | versionMinor = VERSION_MINOR; | |
|
| unused.setOfs( fileno, headerSize() ); | | unused.setOfs( fileno, HeaderSize ); | |
| assert( (data-(char*)this) == headerSize() ); | | assert( (data-(char*)this) == HeaderSize ); | |
| unusedLength = fileLength - headerSize() - 16; | | unusedLength = fileLength - HeaderSize - 16; | |
| memcpy(data+unusedLength, " \nthe end\n", 16); | | //memcpy(data+unusedLength, " \nthe end\n", 16); | |
| } | | } | |
| } | | } | |
| | | | |
| bool isEmpty() const { | | bool isEmpty() const { | |
|
| return uninitialized() || ( unusedLength == fileLength - header
Size() - 16 ); | | return uninitialized() || ( unusedLength == fileLength - Header
Size - 16 ); | |
| } | | } | |
| }; | | }; | |
| | | | |
| #pragma pack() | | #pragma pack() | |
| | | | |
| inline Extent* MongoDataFile::_getExtent(DiskLoc loc) { | | inline Extent* MongoDataFile::_getExtent(DiskLoc loc) { | |
| loc.assertOk(); | | loc.assertOk(); | |
|
| Extent *e = (Extent *) (((char *)header) + loc.getOfs()); | | Extent *e = (Extent *) _p.at(loc.getOfs(), Extent::HeaderSize()); | |
| return e; | | return e; | |
| } | | } | |
| | | | |
| inline Extent* MongoDataFile::getExtent(DiskLoc loc) { | | inline Extent* MongoDataFile::getExtent(DiskLoc loc) { | |
| Extent *e = _getExtent(loc); | | Extent *e = _getExtent(loc); | |
| e->assertOk(); | | e->assertOk(); | |
| 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) { | |
|
| return header->getRecord(dl); | | int ofs = dl.getOfs(); | |
| | | assert( ofs >= DataFileHeader::HeaderSize ); | |
| | | return (Record*) _p.at(ofs, -1); | |
| | | } | |
| | | | |
| | | inline void MongoDataFile::grow(DiskLoc dl, int size) { | |
| | | int ofs = dl.getOfs(); | |
| | | _p.grow(ofs, size); | |
| | | } | |
| | | | |
| | | inline Record* MongoDataFile::makeRecord(DiskLoc dl, int size) { | |
| | | int ofs = dl.getOfs(); | |
| | | assert( ofs >= DataFileHeader::HeaderSize ); | |
| | | return (Record*) _p.at(ofs, size); | |
| } | | } | |
| | | | |
| inline DiskLoc Record::getNext(const DiskLoc& myLoc) { | | inline DiskLoc Record::getNext(const DiskLoc& myLoc) { | |
| if ( nextOfs != DiskLoc::NullOfs ) { | | if ( nextOfs != DiskLoc::NullOfs ) { | |
| /* defensive */ | | /* defensive */ | |
| if ( nextOfs >= 0 && nextOfs < 10 ) { | | if ( nextOfs >= 0 && nextOfs < 10 ) { | |
| sayDbContext("Assertion failure - Record::getNext() referen
cing a deleted record?"); | | sayDbContext("Assertion failure - Record::getNext() referen
cing a deleted record?"); | |
| return DiskLoc(); | | return DiskLoc(); | |
| } | | } | |
| | | | |
| | | | |
| skipping to change at line 450 | | skipping to change at line 466 | |
| 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); | | return cc().database()->getFile(dl.a())->recordAt(dl); | |
| } | | } | |
| | | | |
|
| | | BOOST_STATIC_ASSERT( 16 == sizeof(DeletedRecord) ); | |
| | | | |
| | | inline void DataFileMgr::grow(const DiskLoc& dl, int len) { | |
| | | assert( dl.a() != -1 ); | |
| | | cc().database()->getFile(dl.a())->grow(dl, len); | |
| | | } | |
| | | | |
| | | inline DeletedRecord* DataFileMgr::makeDeletedRecord(const DiskLoc& dl, | |
| | | int len) { | |
| | | assert( dl.a() != -1 ); | |
| | | return (DeletedRecord*) cc().database()->getFile(dl.a())->makeRecor | |
| | | d(dl, sizeof(DeletedRecord)); | |
| | | } | |
| | | | |
| void ensureHaveIdIndex(const char *ns); | | void ensureHaveIdIndex(const char *ns); | |
| | | | |
| bool dropIndexes( NamespaceDetails *d, const char *ns, const char *name
, string &errmsg, BSONObjBuilder &anObjBuilder, bool maydeleteIdIndex ); | | bool dropIndexes( NamespaceDetails *d, const char *ns, const char *name
, string &errmsg, BSONObjBuilder &anObjBuilder, bool maydeleteIdIndex ); | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 18 change blocks. |
| 22 lines changed or deleted | | 53 lines changed or added | |
|
| queryoptimizer.h | | queryoptimizer.h | |
| | | | |
| skipping to change at line 120 | | skipping to change at line 120 | |
| bool complete_; | | bool complete_; | |
| string exceptionMessage_; | | string exceptionMessage_; | |
| const QueryPlan *qp_; | | const QueryPlan *qp_; | |
| bool error_; | | bool error_; | |
| }; | | }; | |
| | | | |
| // Set of candidate query plans for a particular query. Used for runni
ng | | // Set of candidate query plans for a particular query. Used for runni
ng | |
| // a QueryOp on these plans. | | // a QueryOp on these plans. | |
| class QueryPlanSet { | | class QueryPlanSet { | |
| public: | | public: | |
|
| | | | |
| | | typedef boost::shared_ptr< QueryPlan > PlanPtr; | |
| | | typedef vector< PlanPtr > PlanSet; | |
| | | | |
| QueryPlanSet( const char *ns, | | QueryPlanSet( const char *ns, | |
| const BSONObj &query, | | const BSONObj &query, | |
| const BSONObj &order, | | const BSONObj &order, | |
| const BSONElement *hint = 0, | | const BSONElement *hint = 0, | |
| bool honorRecordedPlan = true, | | bool honorRecordedPlan = true, | |
| const BSONObj &min = BSONObj(), | | const BSONObj &min = BSONObj(), | |
| const BSONObj &max = BSONObj() ); | | const BSONObj &max = BSONObj() ); | |
| int nPlans() const { return plans_.size(); } | | int nPlans() const { return plans_.size(); } | |
| shared_ptr< QueryOp > runOp( QueryOp &op ); | | shared_ptr< QueryOp > runOp( QueryOp &op ); | |
| template< class T > | | template< class T > | |
| shared_ptr< T > runOp( T &op ) { | | shared_ptr< T > runOp( T &op ) { | |
| return dynamic_pointer_cast< T >( runOp( static_cast< QueryOp&
>( op ) ) ); | | return dynamic_pointer_cast< T >( runOp( static_cast< QueryOp&
>( op ) ) ); | |
| } | | } | |
| const FieldRangeSet &fbs() const { return fbs_; } | | const FieldRangeSet &fbs() const { return fbs_; } | |
| BSONObj explain() const; | | BSONObj explain() const; | |
| bool usingPrerecordedPlan() const { return usingPrerecordedPlan_; } | | bool usingPrerecordedPlan() const { return usingPrerecordedPlan_; } | |
|
| | | PlanPtr getBestGuess() const; | |
| private: | | private: | |
| void addOtherPlans( bool checkFirst ); | | void addOtherPlans( bool checkFirst ); | |
|
| typedef boost::shared_ptr< QueryPlan > PlanPtr; | | | |
| typedef vector< PlanPtr > PlanSet; | | | |
| void addPlan( PlanPtr plan, bool checkFirst ) { | | void addPlan( PlanPtr 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 ); | |
| struct Runner { | | struct Runner { | |
| Runner( QueryPlanSet &plans, QueryOp &op ); | | Runner( QueryPlanSet &plans, QueryOp &op ); | |
| shared_ptr< QueryOp > run(); | | shared_ptr< QueryOp > run(); | |
| | | | |
End of changes. 3 change blocks. |
| 2 lines changed or deleted | | 5 lines changed or added | |
|
| repl.h | | repl.h | |
| | | | |
| skipping to change at line 68 | | skipping to change at line 68 | |
| bool master; | | bool master; | |
| | | | |
| int opIdMem; | | int opIdMem; | |
| | | | |
| bool fastsync; | | bool fastsync; | |
| | | | |
| bool autoresync; | | bool autoresync; | |
| | | | |
| int slavedelay; | | int slavedelay; | |
| | | | |
|
| bool pretouch; | | | |
| | | | |
| ReplSettings() | | ReplSettings() | |
|
| : slave(NotSlave) , master(false) , opIdMem(100000000) , fastsy
nc() , autoresync(false), slavedelay(), pretouch(false) { | | : slave(NotSlave) , master(false) , opIdMem(100000000) , fastsy
nc() , autoresync(false), slavedelay() { | |
| } | | } | |
| | | | |
| }; | | }; | |
| | | | |
| extern ReplSettings replSettings; | | extern ReplSettings replSettings; | |
| | | | |
| bool cloneFrom(const char *masterHost, string& errmsg, const string& fr
omdb, bool logForReplication, | | bool cloneFrom(const char *masterHost, string& errmsg, const string& fr
omdb, bool logForReplication, | |
| bool slaveOk, bool useReplAuth, bool snap
shot); | | bool slaveOk, bool useReplAuth, bool snap
shot); | |
| | | | |
| /* A replication exception */ | | /* A replication exception */ | |
| | | | |
| skipping to change at line 102 | | skipping to change at line 100 | |
| | | | |
| { host: ..., source: ..., only: ..., syncedTo: ..., localLogTs: .
.., dbsNextPass: { ... }, incompleteCloneDbs: { ... } } | | { host: ..., source: ..., only: ..., syncedTo: ..., localLogTs: .
.., dbsNextPass: { ... }, incompleteCloneDbs: { ... } } | |
| | | | |
| 'source' defaults to 'main'; support for multiple source names is | | 'source' defaults to 'main'; support for multiple source names is | |
| not done (always use main for now). | | not done (always use main for now). | |
| */ | | */ | |
| class ReplSource { | | class ReplSource { | |
| bool resync(string db); | | bool resync(string db); | |
| | | | |
| /* pull some operations from the master's oplog, and apply them. */ | | /* pull some operations from the master's oplog, and apply them. */ | |
|
| bool sync_pullOpLog(int& nApplied); | | int sync_pullOpLog(int& nApplied); | |
| | | | |
| void sync_pullOpLog_applyOperation(BSONObj& op, OpTime *localLogTai
l); | | void sync_pullOpLog_applyOperation(BSONObj& op, OpTime *localLogTai
l); | |
| | | | |
| auto_ptr<DBClientConnection> conn; | | auto_ptr<DBClientConnection> conn; | |
| auto_ptr<DBClientCursor> cursor; | | auto_ptr<DBClientCursor> cursor; | |
| | | | |
| /* we only clone one database per pass, even if a lot need done. T
his helps us | | /* we only clone one database per pass, even if a lot need done. T
his helps us | |
| avoid overflowing the master's transaction log by doing too much
work before going | | avoid overflowing the master's transaction log by doing too much
work before going | |
| back to read more transactions. (Imagine a scenario of slave sta
rtup where we try to | | back to read more transactions. (Imagine a scenario of slave sta
rtup where we try to | |
| clone 100 databases in one pass.) | | clone 100 databases in one pass.) | |
| | | | |
| skipping to change at line 135 | | skipping to change at line 133 | |
| static BSONObj idForOp( const BSONObj &op, bool &mod ); | | static BSONObj idForOp( const BSONObj &op, bool &mod ); | |
| static void updateSetsWithOp( const BSONObj &op, bool mayUpdateStor
age ); | | static void updateSetsWithOp( const BSONObj &op, bool mayUpdateStor
age ); | |
| // call without the db mutex | | // call without the db mutex | |
| void syncToTailOfRemoteLog(); | | void syncToTailOfRemoteLog(); | |
| // call with the db mutex | | // call with the db mutex | |
| OpTime nextLastSavedLocalTs() const; | | OpTime nextLastSavedLocalTs() const; | |
| void setLastSavedLocalTs( const OpTime &nextLocalTs ); | | void setLastSavedLocalTs( const OpTime &nextLocalTs ); | |
| // call without the db mutex | | // call without the db mutex | |
| void resetSlave(); | | void resetSlave(); | |
| // call with the db mutex | | // call with the db mutex | |
|
| int updateSetsWithLocalOps( OpTime &localLogTail, bool mayUnlock ); | | // returns false if the slave has been reset | |
| | | bool updateSetsWithLocalOps( OpTime &localLogTail, bool mayUnlock ) | |
| | | ; | |
| string ns() const { return string( "local.oplog.$" ) + sourceName()
; } | | string ns() const { return string( "local.oplog.$" ) + sourceName()
; } | |
| unsigned _sleepAdviceTime; | | unsigned _sleepAdviceTime; | |
| | | | |
| public: | | public: | |
| static void applyOperation(const BSONObj& op); | | static void applyOperation(const BSONObj& op); | |
| bool replacing; // in "replace mode" -- see CmdReplacePeer | | bool replacing; // in "replace mode" -- see CmdReplacePeer | |
| bool paired; // --pair in use | | bool paired; // --pair in use | |
| string hostName; // ip addr or hostname plus optionally, ":<port
>" | | string hostName; // ip addr or hostname plus optionally, ":<port
>" | |
| string _sourceName; // a logical source name. | | string _sourceName; // a logical source name. | |
| string sourceName() const { | | string sourceName() const { | |
| | | | |
| skipping to change at line 166 | | skipping to change at line 165 | |
| has ABCXY, then _lastSavedLocalTs won't be greater than C until
we have reconciled | | has ABCXY, then _lastSavedLocalTs won't be greater than C until
we have reconciled | |
| the DE-XY difference.) | | the DE-XY difference.) | |
| */ | | */ | |
| OpTime _lastSavedLocalTs; | | OpTime _lastSavedLocalTs; | |
| | | | |
| int nClonedThisPass; | | int nClonedThisPass; | |
| | | | |
| typedef vector< shared_ptr< ReplSource > > SourceVector; | | typedef vector< shared_ptr< ReplSource > > SourceVector; | |
| static void loadAll(SourceVector&); | | static void loadAll(SourceVector&); | |
| explicit ReplSource(BSONObj); | | explicit ReplSource(BSONObj); | |
|
| bool sync(int& nApplied); | | | |
| | | /* -1 = error */ | |
| | | int sync(int& nApplied); | |
| | | | |
| void save(); // write ourself to local.sources | | void save(); // write ourself to local.sources | |
| void resetConnection() { | | void resetConnection() { | |
| cursor = auto_ptr<DBClientCursor>(0); | | cursor = auto_ptr<DBClientCursor>(0); | |
| conn = auto_ptr<DBClientConnection>(0); | | conn = auto_ptr<DBClientConnection>(0); | |
| } | | } | |
| | | | |
| // make a jsobj from our member fields of the form | | // make a jsobj from our member fields of the form | |
| // { host: ..., source: ..., syncedTo: ... } | | // { host: ..., source: ..., syncedTo: ... } | |
| BSONObj jsobj(); | | BSONObj jsobj(); | |
| | | | |
| | | | |
| skipping to change at line 209 | | skipping to change at line 211 | |
| "c" db cmd | | "c" db cmd | |
| "db" declares presence of a database (ns is set to the db name + '.'
) | | "db" declares presence of a database (ns is set to the db name + '.'
) | |
| */ | | */ | |
| void logOp(const char *opstr, const char *ns, const BSONObj& obj, BSONO
bj *patt = 0, bool *b = 0); | | void logOp(const char *opstr, const char *ns, const BSONObj& obj, BSONO
bj *patt = 0, bool *b = 0); | |
| | | | |
| // class for managing a set of ids in memory | | // class for managing a set of ids in memory | |
| class MemIds { | | class MemIds { | |
| public: | | public: | |
| MemIds() : size_() {} | | MemIds() : size_() {} | |
| friend class IdTracker; | | friend class IdTracker; | |
|
| void reset() { | | void reset() { imp_.clear(); } | |
| imp_.clear(); | | | |
| size_ = 0; | | | |
| } | | | |
| bool get( const char *ns, const BSONObj &id ) { return imp_[ ns ].c
ount( id ); } | | bool get( const char *ns, const BSONObj &id ) { return imp_[ ns ].c
ount( id ); } | |
| void set( const char *ns, const BSONObj &id, bool val ) { | | void set( const char *ns, const BSONObj &id, bool val ) { | |
| if ( val ) { | | if ( val ) { | |
| if ( imp_[ ns ].insert( id.getOwned() ).second ) { | | if ( imp_[ ns ].insert( id.getOwned() ).second ) { | |
| size_ += id.objsize() + sizeof( BSONObj ); | | size_ += id.objsize() + sizeof( BSONObj ); | |
| } | | } | |
| } else { | | } else { | |
| if ( imp_[ ns ].erase( id ) == 1 ) { | | if ( imp_[ ns ].erase( id ) == 1 ) { | |
| size_ -= id.objsize() + sizeof( BSONObj ); | | size_ -= id.objsize() + sizeof( BSONObj ); | |
| } | | } | |
| | | | |
| skipping to change at line 431 | | skipping to change at line 430 | |
| enum FindingStartMode { Initial, FindExtent, InExtent }; | | enum FindingStartMode { Initial, FindExtent, InExtent }; | |
| const QueryPlan &_qp; | | const QueryPlan &_qp; | |
| bool _findingStart; | | bool _findingStart; | |
| FindingStartMode _findingStartMode; | | FindingStartMode _findingStartMode; | |
| auto_ptr< CoveredIndexMatcher > _matcher; | | auto_ptr< CoveredIndexMatcher > _matcher; | |
| Timer _findingStartTimer; | | Timer _findingStartTimer; | |
| ClientCursor * _findingStartCursor; | | ClientCursor * _findingStartCursor; | |
| auto_ptr< Cursor > _c; | | auto_ptr< Cursor > _c; | |
| DiskLoc startLoc( const DiskLoc &rec ) { | | DiskLoc startLoc( const DiskLoc &rec ) { | |
| Extent *e = rec.rec()->myExtent( rec ); | | Extent *e = rec.rec()->myExtent( rec ); | |
|
| if ( !_qp.nsd()->capLooped() || ( e->myLoc != _qp.nsd()->capExt
ent ) ) | | if ( e->myLoc != _qp.nsd()->capExtent ) | |
| return e->firstRecord; | | return e->firstRecord; | |
| // Likely we are on the fresh side of capExtent, so return firs
t fresh record. | | // Likely we are on the fresh side of capExtent, so return firs
t fresh record. | |
| // If we are on the stale side of capExtent, then the collectio
n is small and it | | // If we are on the stale side of capExtent, then the collectio
n is small and it | |
| // doesn't matter if we start the extent scan with capFirstNewR
ecord. | | // doesn't matter if we start the extent scan with capFirstNewR
ecord. | |
| return _qp.nsd()->capFirstNewRecord; | | return _qp.nsd()->capFirstNewRecord; | |
| } | | } | |
| | | | |
|
| // should never have an empty extent in the oplog, so don't worry a
bout that case | | | |
| DiskLoc prevLoc( const DiskLoc &rec ) { | | DiskLoc prevLoc( const DiskLoc &rec ) { | |
| Extent *e = rec.rec()->myExtent( rec ); | | Extent *e = rec.rec()->myExtent( rec ); | |
|
| if ( _qp.nsd()->capLooped() ) { | | if ( e->xprev.isNull() ) | |
| if ( e->xprev.isNull() ) | | e = _qp.nsd()->lastExtent.ext(); | |
| e = _qp.nsd()->lastExtent.ext(); | | else | |
| else | | e = e->xprev.ext(); | |
| e = e->xprev.ext(); | | if ( e->myLoc != _qp.nsd()->capExtent ) | |
| if ( e->myLoc != _qp.nsd()->capExtent ) | | return e->firstRecord; | |
| return e->firstRecord; | | | |
| } else { | | | |
| if ( !e->xprev.isNull() ) { | | | |
| e = e->xprev.ext(); | | | |
| return e->firstRecord; | | | |
| } | | | |
| } | | | |
| return DiskLoc(); // reached beginning of collection | | return DiskLoc(); // reached beginning of collection | |
| } | | } | |
| void createClientCursor( const DiskLoc &startLoc = DiskLoc() ) { | | void createClientCursor( const DiskLoc &startLoc = DiskLoc() ) { | |
| auto_ptr<Cursor> c = _qp.newCursor( startLoc ); | | auto_ptr<Cursor> c = _qp.newCursor( startLoc ); | |
|
| _findingStartCursor = new ClientCursor(c, _qp.ns(), false); | | _findingStartCursor = new ClientCursor(QueryOption_NoCursorTime
out, c, _qp.ns()); | |
| } | | } | |
| void destroyClientCursor() { | | void destroyClientCursor() { | |
| if ( _findingStartCursor ) { | | if ( _findingStartCursor ) { | |
| ClientCursor::erase( _findingStartCursor->cursorid ); | | ClientCursor::erase( _findingStartCursor->cursorid ); | |
| _findingStartCursor = 0; | | _findingStartCursor = 0; | |
| } | | } | |
| } | | } | |
| void maybeRelease() { | | void maybeRelease() { | |
| RARELY { | | RARELY { | |
| CursorId id = _findingStartCursor->cursorid; | | CursorId id = _findingStartCursor->cursorid; | |
| | | | |
| skipping to change at line 481 | | skipping to change at line 472 | |
| { | | { | |
| dbtemprelease t; | | dbtemprelease t; | |
| } | | } | |
| _findingStartCursor = ClientCursor::find( id, false ); | | _findingStartCursor = ClientCursor::find( id, false ); | |
| } | | } | |
| } | | } | |
| void init() { | | void init() { | |
| // Use a ClientCursor here so we can release db mutex while sca
nning | | // Use a ClientCursor here so we can release db mutex while sca
nning | |
| // oplog (can take quite a while with large oplogs). | | // oplog (can take quite a while with large oplogs). | |
| auto_ptr<Cursor> c = _qp.newReverseCursor(); | | auto_ptr<Cursor> c = _qp.newReverseCursor(); | |
|
| _findingStartCursor = new ClientCursor(c, _qp.ns(), false); | | _findingStartCursor = new ClientCursor(QueryOption_NoCursorTime
out, c, _qp.ns()); | |
| _findingStartTimer.reset(); | | _findingStartTimer.reset(); | |
| _findingStartMode = Initial; | | _findingStartMode = Initial; | |
| BSONElement tsElt = _qp.query()[ "ts" ]; | | BSONElement tsElt = _qp.query()[ "ts" ]; | |
| massert( 13044, "no ts field in query", !tsElt.eoo() ); | | massert( 13044, "no ts field in query", !tsElt.eoo() ); | |
| BSONObjBuilder b; | | BSONObjBuilder b; | |
| b.append( tsElt ); | | b.append( tsElt ); | |
| BSONObj tsQuery = b.obj(); | | BSONObj tsQuery = b.obj(); | |
| _matcher.reset(new CoveredIndexMatcher(tsQuery, _qp.indexKey())
); | | _matcher.reset(new CoveredIndexMatcher(tsQuery, _qp.indexKey())
); | |
| } | | } | |
| }; | | }; | |
| | | | |
|
| | | void updateSlaveLocation( CurOp& curop, const char * ns , OpTime lastOp | |
| | | ); | |
| | | bool opReplicatedEnough( OpTime op , int w ); | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
End of changes. 12 change blocks. |
| 27 lines changed or deleted | | 23 lines changed or added | |
|
| sock.h | | sock.h | |
| | | | |
| skipping to change at line 25 | | skipping to change at line 25 | |
| * limitations under the License. | | * limitations under the License. | |
| */ | | */ | |
| | | | |
| #pragma once | | #pragma once | |
| | | | |
| #include "../stdafx.h" | | #include "../stdafx.h" | |
| | | | |
| #include <stdio.h> | | #include <stdio.h> | |
| #include <sstream> | | #include <sstream> | |
| #include "goodies.h" | | #include "goodies.h" | |
|
| | | #include "../db/jsobj.h" | |
| | | | |
|
| #ifdef _WIN32 | | #define SOCK_FAMILY_UNKNOWN_ERROR 13078 | |
| #include <windows.h> | | | |
| #include <winsock.h> | | | |
| #endif | | | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| #if defined(_WIN32) | | #if defined(_WIN32) | |
| | | | |
|
| | | typedef short sa_family_t; | |
| typedef int socklen_t; | | typedef int socklen_t; | |
| inline int getLastError() { | | inline int getLastError() { | |
| return WSAGetLastError(); | | return WSAGetLastError(); | |
| } | | } | |
|
| | | inline const char* gai_strerror(int code) { | |
| | | return ::gai_strerrorA(code); | |
| | | } | |
| inline void disableNagle(int sock) { | | inline void disableNagle(int sock) { | |
| int x = 1; | | int x = 1; | |
| if ( setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &x, sizeof
(x)) ) | | if ( setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &x, sizeof
(x)) ) | |
| out() << "ERROR: disableNagle failed" << endl; | | out() << "ERROR: disableNagle failed" << endl; | |
| } | | } | |
| inline void prebindOptions( int sock ) { | | inline void prebindOptions( int sock ) { | |
| } | | } | |
|
| | | | |
| | | // This won't actually be used on windows | |
| | | struct sockaddr_un { | |
| | | short sun_family; | |
| | | char sun_path[108]; // length from unix header | |
| | | }; | |
| | | | |
| #else | | #else | |
| | | | |
| } // namespace mongo | | } // namespace mongo | |
| | | | |
| #include <sys/socket.h> | | #include <sys/socket.h> | |
| #include <sys/types.h> | | #include <sys/types.h> | |
| #include <sys/socket.h> | | #include <sys/socket.h> | |
|
| | | #include <sys/un.h> | |
| #include <netinet/in.h> | | #include <netinet/in.h> | |
| #include <netinet/tcp.h> | | #include <netinet/tcp.h> | |
| #include <arpa/inet.h> | | #include <arpa/inet.h> | |
| #include <errno.h> | | #include <errno.h> | |
| #include <netdb.h> | | #include <netdb.h> | |
| | | | |
| namespace mongo { | | namespace mongo { | |
| | | | |
| inline void closesocket(int s) { | | inline void closesocket(int s) { | |
| close(s); | | close(s); | |
| | | | |
| skipping to change at line 89 | | skipping to change at line 99 | |
| } | | } | |
| inline void prebindOptions( int sock ) { | | inline void prebindOptions( int sock ) { | |
| DEV log() << "doing prebind option" << endl; | | DEV log() << "doing prebind option" << endl; | |
| int x = 1; | | int x = 1; | |
| if ( setsockopt( sock , SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)) <
0 ) | | if ( setsockopt( sock , SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)) <
0 ) | |
| out() << "Failed to set socket opt, SO_REUSEADDR" << endl; | | out() << "Failed to set socket opt, SO_REUSEADDR" << endl; | |
| } | | } | |
| | | | |
| #endif | | #endif | |
| | | | |
|
| inline void setSockReceiveTimeout(int sock, int secs) { | | inline string makeUnixSockPath(int port){ | |
| // todo - finish - works? | | return "/tmp/mongodb-" + BSONObjBuilder::numStr(port) + ".sock"; | |
| | | } | |
| | | | |
| | | inline void setSockTimeouts(int sock, int secs) { | |
| struct timeval tv; | | struct timeval tv; | |
|
| tv.tv_sec = 0;//secs; | | tv.tv_sec = secs; | |
| tv.tv_usec = 1000; | | tv.tv_usec = 0; | |
| int rc = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *) &tv, si | | massert( 13083, "unable to set SO_RCVTIMEO", | |
| zeof(tv)); | | setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *) &tv, siz | |
| if ( rc ) { | | eof(tv) ) == 0 ); | |
| out() << "ERROR: setsockopt RCVTIMEO failed rc:" << rc << " " < | | massert( 13084, "unable to set SO_SNDTIMEO", | |
| < OUTPUT_ERRNO << " secs:" << secs << " sock:" << sock << endl; | | setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *) &tv, siz | |
| } | | eof(tv) ) == 0 ); | |
| } | | } | |
| | | | |
| // If an ip address is passed in, just return that. If a hostname is p
assed | | // If an ip address is passed in, just return that. If a hostname is p
assed | |
| // in, look up its ip and return that. Returns "" on failure. | | // in, look up its ip and return that. Returns "" on failure. | |
| string hostbyname(const char *hostname); | | string hostbyname(const char *hostname); | |
| | | | |
|
| | | void enableIPv6(bool state=true); | |
| | | bool IPv6Enabled(); | |
| | | | |
| struct SockAddr { | | struct SockAddr { | |
| SockAddr() { | | SockAddr() { | |
|
| addressSize = sizeof(sockaddr_in); | | addressSize = sizeof(sa); | |
| memset(&sa, 0, sizeof(sa)); | | memset(&sa, 0, sizeof(sa)); | |
|
| | | sa.ss_family = AF_UNSPEC; | |
| } | | } | |
| SockAddr(int sourcePort); /* listener side */ | | SockAddr(int sourcePort); /* listener side */ | |
| SockAddr(const char *ip, int port); /* EndPoint (remote) side, or i
f you want to specify which interface locally */ | | SockAddr(const char *ip, int port); /* EndPoint (remote) side, or i
f you want to specify which interface locally */ | |
| | | | |
|
| struct sockaddr_in sa; | | template <typename T> | |
| socklen_t addressSize; | | T& as() { return *(T*)(&sa); } | |
| | | template <typename T> | |
| bool isLocalHost() const { | | const T& as() const { return *(const T*)(&sa); } | |
| #if defined(_WIN32) | | | |
| return sa.sin_addr.S_un.S_addr == 0x100007f; | | | |
| #else | | | |
| return sa.sin_addr.s_addr == 0x100007f; | | | |
| #endif | | | |
| } | | | |
| | | | |
|
| string toString() const{ | | string toString(bool includePort=true) const{ | |
| stringstream out; | | string out = getAddr(); | |
| out << inet_ntoa(sa.sin_addr) << ':' | | if (includePort && getType() != AF_UNIX && getType() != AF_UNSP | |
| << ntohs(sa.sin_port); | | EC) | |
| return out.str(); | | out += ':' + BSONObjBuilder::numStr(getPort()); | |
| | | return out; | |
| } | | } | |
| | | | |
| operator string() const{ | | operator string() const{ | |
| return toString(); | | return toString(); | |
| } | | } | |
| | | | |
|
| unsigned getPort() { | | // returns one of AF_INET, AF_INET6, or AF_UNIX | |
| return sa.sin_port; | | sa_family_t getType() const { | |
| | | return sa.ss_family; | |
| } | | } | |
| | | | |
|
| bool localhost() const { return inet_addr( "127.0.0.1" ) == sa.sin_ | | unsigned getPort() const { | |
| addr.s_addr; } | | switch (getType()){ | |
| | | case AF_INET: return ntohs(as<sockaddr_in>().sin_port); | |
| | | case AF_INET6: return ntohs(as<sockaddr_in6>().sin6_port); | |
| | | case AF_UNIX: return 0; | |
| | | case AF_UNSPEC: return 0; | |
| | | default: massert(SOCK_FAMILY_UNKNOWN_ERROR, "unsupported ad | |
| | | dress family", false); return 0; | |
| | | } | |
| | | } | |
| | | | |
| | | string getAddr() const { | |
| | | switch (getType()){ | |
| | | case AF_INET: | |
| | | case AF_INET6: { | |
| | | const int buflen=128; | |
| | | char buffer[buflen]; | |
| | | int ret = getnameinfo(raw(), addressSize, buffer, bufle | |
| | | n, NULL, 0, NI_NUMERICHOST); | |
| | | massert(13082, gai_strerror(ret), ret == 0); | |
| | | return buffer; | |
| | | } | |
| | | | |
| | | case AF_UNIX: return (addressSize > 2 ? as<sockaddr_un>(). | |
| | | sun_path : "anonymous unix socket"); | |
| | | case AF_UNSPEC: return "(NONE)"; | |
| | | default: massert(SOCK_FAMILY_UNKNOWN_ERROR, "unsupported ad | |
| | | dress family", false); return ""; | |
| | | } | |
| | | } | |
| | | | |
| | | bool isLocalHost() const; | |
| | | | |
| bool operator==(const SockAddr& r) const { | | bool operator==(const SockAddr& r) const { | |
|
| return sa.sin_addr.s_addr == r.sa.sin_addr.s_addr && | | if (getType() != r.getType()) | |
| sa.sin_port == r.sa.sin_port; | | return false; | |
| | | | |
| | | if (getPort() != r.getPort()) | |
| | | return false; | |
| | | | |
| | | switch (getType()){ | |
| | | case AF_INET: return as<sockaddr_in>().sin_addr.s_addr == | |
| | | r.as<sockaddr_in>().sin_addr.s_addr; | |
| | | case AF_INET6: return memcmp(as<sockaddr_in6>().sin6_addr.s | |
| | | 6_addr, r.as<sockaddr_in6>().sin6_addr.s6_addr, sizeof(in6_addr)) == 0; | |
| | | case AF_UNIX: return strcmp(as<sockaddr_un>().sun_path, r. | |
| | | as<sockaddr_un>().sun_path) == 0; | |
| | | case AF_UNSPEC: return true; // assume all unspecified addr | |
| | | esses are the same | |
| | | default: massert(SOCK_FAMILY_UNKNOWN_ERROR, "unsupported ad | |
| | | dress family", false); | |
| | | } | |
| } | | } | |
| bool operator!=(const SockAddr& r) const { | | bool operator!=(const SockAddr& r) const { | |
| return !(*this == r); | | return !(*this == r); | |
| } | | } | |
| bool operator<(const SockAddr& r) const { | | bool operator<(const SockAddr& r) const { | |
|
| if ( sa.sin_port >= r.sa.sin_port ) | | if (getType() < r.getType()) | |
| | | return true; | |
| | | else if (getType() > r.getType()) | |
| return false; | | return false; | |
|
| return sa.sin_addr.s_addr < r.sa.sin_addr.s_addr; | | | |
| } | | | |
| }; | | | |
| | | | |
|
| const int MaxMTU = 16384; | | if (getPort() < r.getPort()) | |
| | | return true; | |
| | | else if (getPort() > r.getPort()) | |
| | | return false; | |
| | | | |
|
| class UDPConnection { | | switch (getType()){ | |
| public: | | case AF_INET: return as<sockaddr_in>().sin_addr.s_addr < r | |
| UDPConnection() { | | .as<sockaddr_in>().sin_addr.s_addr; | |
| sock = 0; | | case AF_INET6: return memcmp(as<sockaddr_in6>().sin6_addr.s | |
| } | | 6_addr, r.as<sockaddr_in6>().sin6_addr.s6_addr, sizeof(in6_addr)) < 0; | |
| ~UDPConnection() { | | case AF_UNIX: return strcmp(as<sockaddr_un>().sun_path, r. | |
| if ( sock ) { | | as<sockaddr_un>().sun_path) < 0; | |
| closesocket(sock); | | case AF_UNSPEC: return false; | |
| sock = 0; | | default: massert(SOCK_FAMILY_UNKNOWN_ERROR, "unsupported ad | |
| | | dress family", false); | |
| } | | } | |
| } | | } | |
|
| bool init(const SockAddr& myAddr); | | | |
| int recvfrom(char *buf, int len, SockAddr& sender); | | | |
| int sendto(char *buf, int len, const SockAddr& EndPoint); | | | |
| int mtu(const SockAddr& sa) { | | | |
| return sa.isLocalHost() ? 16384 : 1480; | | | |
| } | | | |
| | | | |
| SOCKET sock; | | | |
| }; | | | |
| | | | |
|
| inline int UDPConnection::recvfrom(char *buf, int len, SockAddr& sender | | const sockaddr* raw() const {return (sockaddr*)&sa;} | |
| ) { | | sockaddr* raw() {return (sockaddr*)&sa;} | |
| return ::recvfrom(sock, buf, len, 0, (sockaddr *) &sender.sa, &send | | | |
| er.addressSize); | | | |
| } | | | |
| | | | |
| inline int UDPConnection::sendto(char *buf, int len, const SockAddr& En | | | |
| dPoint) { | | | |
| if ( 0 && rand() < (RAND_MAX>>4) ) { | | | |
| out() << " NOTSENT "; | | | |
| // out() << curTimeMillis() << " .TEST: NOT SENDING PAC | | | |
| KET" << endl; | | | |
| return 0; | | | |
| } | | | |
| return ::sendto(sock, buf, len, 0, (sockaddr *) &EndPoint.sa, EndPo | | | |
| int.addressSize); | | | |
| } | | | |
| | | | |
|
| inline bool UDPConnection::init(const SockAddr& myAddr) { | | socklen_t addressSize; | |
| sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); | | private: | |
| if ( sock == INVALID_SOCKET ) { | | struct sockaddr_storage sa; | |
| out() << "invalid socket? " << OUTPUT_ERRNO << endl; | | }; | |
| return false; | | | |
| } | | | |
| //out() << sizeof(sockaddr_in) << ' ' << myAddr.addressSize << endl | | | |
| ; | | | |
| if ( ::bind(sock, (sockaddr *) &myAddr.sa, myAddr.addressSize) != 0 | | | |
| ) { | | | |
| out() << "udp init failed" << endl; | | | |
| closesocket(sock); | | | |
| sock = 0; | | | |
| return false; | | | |
| } | | | |
| socklen_t optLen; | | | |
| int rcvbuf; | | | |
| if (getsockopt(sock, | | | |
| SOL_SOCKET, | | | |
| SO_RCVBUF, | | | |
| (char*)&rcvbuf, | | | |
| &optLen) != -1) | | | |
| out() << "SO_RCVBUF:" << rcvbuf << endl; | | | |
| return true; | | | |
| } | | | |
| | | | |
|
| inline SockAddr::SockAddr(int sourcePort) { | | extern SockAddr unknownAddress; // ( "0.0.0.0", 0 ) | |
| memset(sa.sin_zero, 0, sizeof(sa.sin_zero)); | | | |
| sa.sin_family = AF_INET; | | | |
| sa.sin_port = htons(sourcePort); | | | |
| sa.sin_addr.s_addr = htonl(INADDR_ANY); | | | |
| addressSize = sizeof(sa); | | | |
| } | | | |
| | | | |
|
| inline SockAddr::SockAddr(const char * iporhost , int port) { | | const int MaxMTU = 16384; | |
| string ip = hostbyname( iporhost ); | | | |
| memset(sa.sin_zero, 0, sizeof(sa.sin_zero)); | | | |
| sa.sin_family = AF_INET; | | | |
| sa.sin_port = htons(port); | | | |
| sa.sin_addr.s_addr = inet_addr(ip.c_str()); | | | |
| addressSize = sizeof(sa); | | | |
| } | | | |
| | | | |
| inline string getHostName() { | | inline string getHostName() { | |
| char buf[256]; | | char buf[256]; | |
| int ec = gethostname(buf, 127); | | int ec = gethostname(buf, 127); | |
| if ( ec || *buf == 0 ) { | | if ( ec || *buf == 0 ) { | |
| log() << "can't get this server's hostname " << OUTPUT_ERRNO <<
endl; | | log() << "can't get this server's hostname " << OUTPUT_ERRNO <<
endl; | |
| return ""; | | return ""; | |
| } | | } | |
| return buf; | | return buf; | |
| } | | } | |
| | | | |
End of changes. 25 change blocks. |
| 116 lines changed or deleted | | 120 lines changed or added | |
|
| update.h | | update.h | |
| | | | |
| skipping to change at line 103 | | skipping to change at line 103 | |
| switch (op){ | | switch (op){ | |
| case PUSH: | | case PUSH: | |
| case PUSH_ALL: | | case PUSH_ALL: | |
| case POP: | | case POP: | |
| return true; | | return true; | |
| default: | | default: | |
| return false; | | return false; | |
| } | | } | |
| } | | } | |
| | | | |
|
| static bool isIndexed( const string& fullName , const set<string>& | | bool isIndexed( const set<string>& idxKeys ) const { | |
| idxKeys ){ | | | |
| const char * fieldName = fullName.c_str(); | | | |
| // check if there is an index key that is a parent of mod | | // check if there is an index key that is a parent of mod | |
| for( const char *dot = strchr( fieldName, '.' ); dot; dot = str
chr( dot + 1, '.' ) ) | | for( const char *dot = strchr( fieldName, '.' ); dot; dot = str
chr( dot + 1, '.' ) ) | |
| if ( idxKeys.count( string( fieldName, dot - fieldName ) )
) | | if ( idxKeys.count( string( fieldName, dot - fieldName ) )
) | |
| return true; | | return true; | |
|
| | | string fullName = fieldName; | |
| // check if there is an index key equal to mod | | // check if there is an index key equal to mod | |
| if ( idxKeys.count(fullName) ) | | if ( idxKeys.count(fullName) ) | |
| return true; | | return true; | |
| // check if there is an index key that is a child of mod | | // check if there is an index key that is a child of mod | |
| set< string >::const_iterator j = idxKeys.upper_bound( fullName
); | | set< string >::const_iterator j = idxKeys.upper_bound( fullName
); | |
| if ( j != idxKeys.end() && j->find( fullName ) == 0 && (*j)[ful
lName.size()] == '.' ) | | if ( j != idxKeys.end() && j->find( fullName ) == 0 && (*j)[ful
lName.size()] == '.' ) | |
| return true; | | return true; | |
|
| | | | |
| return false; | | | |
| } | | | |
| | | | |
| bool isIndexed( const set<string>& idxKeys ) const { | | | |
| string fullName = fieldName; | | | |
| | | | |
| if ( isIndexed( fullName , idxKeys ) ) | | | |
| return true; | | | |
| | | | |
| if ( strstr( fieldName , "." ) ){ | | | |
| // check for a.0.1 | | | |
| StringBuilder buf( fullName.size() + 1 ); | | | |
| for ( size_t i=0; i<fullName.size(); i++ ){ | | | |
| char c = fullName[i]; | | | |
| buf << c; | | | |
| | | | |
| if ( c != '.' ) | | | |
| continue; | | | |
| | | | |
| if ( ! isdigit( fullName[i+1] ) ) | | | |
| continue; | | | |
| | | | |
| bool possible = true; | | | |
| size_t j=i+2; | | | |
| for ( ; j<fullName.size(); j++ ){ | | | |
| char d = fullName[j]; | | | |
| if ( d == '.' ) | | | |
| break; | | | |
| if ( isdigit( d ) ) | | | |
| continue; | | | |
| possible = false; | | | |
| break; | | | |
| } | | | |
| | | | |
| if ( possible ) | | | |
| i = j; | | | |
| } | | | |
| string x = buf.str(); | | | |
| if ( isIndexed( x , idxKeys ) ) | | | |
| return true; | | | |
| } | | | |
| | | | |
| return false; | | return false; | |
| } | | } | |
| | | | |
| template< class Builder > | | template< class Builder > | |
| void apply( Builder& b , BSONElement in , ModState& ms ) const; | | void apply( Builder& b , BSONElement in , ModState& ms ) const; | |
| | | | |
| /** | | /** | |
| * @return true iff toMatch should be removed from the array | | * @return true iff toMatch should be removed from the array | |
| */ | | */ | |
| bool _pullElementMatch( BSONElement& toMatch ) const; | | bool _pullElementMatch( BSONElement& toMatch ) const; | |
| | | | |
| skipping to change at line 373 | | skipping to change at line 329 | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * stores any information about a single Mod operating on a single Obje
ct | | * stores any information about a single Mod operating on a single Obje
ct | |
| */ | | */ | |
| class ModState { | | class ModState { | |
| public: | | public: | |
| const Mod * m; | | const Mod * m; | |
| BSONElement old; | | BSONElement old; | |
| | | | |
|
| const char * fixedOpName; | | const char * fixedName; | |
| BSONElement * fixed; | | BSONElement * fixed; | |
| int pushStartSize; | | int pushStartSize; | |
| | | | |
| BSONType incType; | | BSONType incType; | |
| int incint; | | int incint; | |
| double incdouble; | | double incdouble; | |
| long long inclong; | | long long inclong; | |
| | | | |
| ModState(){ | | ModState(){ | |
|
| fixedOpName = 0; | | fixedName = 0; | |
| fixed = 0; | | fixed = 0; | |
| pushStartSize = -1; | | pushStartSize = -1; | |
| incType = EOO; | | incType = EOO; | |
| } | | } | |
| | | | |
| Mod::Op op() const { | | Mod::Op op() const { | |
| return m->op; | | return m->op; | |
| } | | } | |
| | | | |
| const char * fieldName() const { | | const char * fieldName() const { | |
| return m->fieldName; | | return m->fieldName; | |
| } | | } | |
| | | | |
| bool needOpLogRewrite() const { | | bool needOpLogRewrite() const { | |
|
| if ( fixed || fixedOpName || incType ) | | if ( fixed || fixedName || incType ) | |
| return true; | | return true; | |
| | | | |
| switch( op() ){ | | switch( op() ){ | |
| case Mod::BIT: | | case Mod::BIT: | |
| case Mod::BITAND: | | case Mod::BITAND: | |
| case Mod::BITOR: | | case Mod::BITOR: | |
| // TODO: should we convert this to $set? | | // TODO: should we convert this to $set? | |
| return false; | | return false; | |
| default: | | default: | |
| return false; | | return false; | |
| } | | } | |
| } | | } | |
| | | | |
| void appendForOpLog( BSONObjBuilder& b ) const { | | void appendForOpLog( BSONObjBuilder& b ) const { | |
| if ( incType ){ | | if ( incType ){ | |
| BSONObjBuilder bb( b.subobjStart( "$set" ) ); | | BSONObjBuilder bb( b.subobjStart( "$set" ) ); | |
|
| appendIncValue( bb , true ); | | appendIncValue( bb ); | |
| bb.done(); | | bb.done(); | |
| return; | | return; | |
| } | | } | |
| | | | |
|
| const char * name = fixedOpName ? fixedOpName : Mod::modNames[o
p()]; | | const char * name = fixedName ? fixedName : Mod::modNames[op()]
; | |
| | | | |
| BSONObjBuilder bb( b.subobjStart( name ) ); | | BSONObjBuilder bb( b.subobjStart( name ) ); | |
| if ( fixed ) | | if ( fixed ) | |
| bb.appendAs( *fixed , m->fieldName ); | | bb.appendAs( *fixed , m->fieldName ); | |
| else | | else | |
|
| bb.appendAs( m->elt , m->fieldName ); | | bb.append( m->elt ); | |
| bb.done(); | | bb.done(); | |
| } | | } | |
| | | | |
| template< class Builder > | | template< class Builder > | |
| void apply( Builder& b , BSONElement in ){ | | void apply( Builder& b , BSONElement in ){ | |
| m->apply( b , in , *this ); | | m->apply( b , in , *this ); | |
| } | | } | |
| | | | |
| template< class Builder > | | template< class Builder > | |
|
| void appendIncValue( Builder& b , bool useFullName ) const { | | void appendIncValue( Builder& b ) const { | |
| const char * n = useFullName ? m->fieldName : m->shortFieldName | | | |
| ; | | | |
| | | | |
| switch ( incType ){ | | switch ( incType ){ | |
| case NumberDouble: | | case NumberDouble: | |
|
| b.append( n , incdouble ); break; | | b.append( m->shortFieldName , incdouble ); break; | |
| case NumberLong: | | case NumberLong: | |
|
| b.append( n , inclong ); break; | | b.append( m->shortFieldName , inclong ); break; | |
| case NumberInt: | | case NumberInt: | |
|
| b.append( n , incint ); break; | | b.append( m->shortFieldName , incint ); break; | |
| default: | | default: | |
| assert(0); | | assert(0); | |
| } | | } | |
| } | | } | |
| }; | | }; | |
| | | | |
| /** | | /** | |
| * this is used to hold state, meta data while applying a ModSet to a B
SONObj | | * this is used to hold state, meta data while applying a ModSet to a B
SONObj | |
| * the goal is to make ModSet const so its re-usable | | * the goal is to make ModSet const so its re-usable | |
| */ | | */ | |
| | | | |
| skipping to change at line 518 | | skipping to change at line 472 | |
| break; | | break; | |
| } | | } | |
| | | | |
| case Mod::UNSET: | | case Mod::UNSET: | |
| case Mod::PULL: | | case Mod::PULL: | |
| case Mod::PULL_ALL: | | case Mod::PULL_ALL: | |
| // no-op b/c unset/pull of nothing does nothing | | // no-op b/c unset/pull of nothing does nothing | |
| break; | | break; | |
| | | | |
| case Mod::INC: | | case Mod::INC: | |
|
| ms.fixedOpName = "$set"; | | ms.fixedName = "$set"; | |
| case Mod::SET: { | | case Mod::SET: { | |
| m._checkForAppending( m.elt ); | | m._checkForAppending( m.elt ); | |
| b.appendAs( m.elt, m.shortFieldName ); | | b.appendAs( m.elt, 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() ); | |
| } | | } | |
| | | | |
End of changes. 14 change blocks. |
| 61 lines changed or deleted | | 13 lines changed or added | |
|