0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-11-30 00:56:44 +01:00

maintenance mode for commands SERVER-3427

This commit is contained in:
Kristina 2011-07-26 16:43:21 -04:00
parent 2035656b20
commit c84f98ab80
7 changed files with 93 additions and 6 deletions

View File

@ -95,6 +95,11 @@ namespace mongo {
*/
virtual bool requiresAuth() { return true; }
/* Return true if a replica set secondary should go into "recovering"
(unreadable) state while running this command.
*/
virtual bool maintenanceMode() const { return false; }
/** @param webUI expose the command in the web ui as localhost:28017/<name>
@param oldName an optional old, deprecated name for the command
*/

View File

@ -263,6 +263,7 @@ namespace mongo {
virtual LockType locktype() const { return NONE; }
virtual bool adminOnly() const { return false; }
virtual bool slaveOk() const { return true; }
virtual bool maintenanceMode() const { return true; }
virtual bool logTheOp() { return false; }
virtual void help( stringstream& help ) const {
help << "compact collection\n"

View File

@ -349,6 +349,7 @@ namespace mongo {
virtual bool slaveOk() const {
return true;
}
virtual bool maintenanceMode() const { return true; }
virtual void help( stringstream& help ) const {
help << "repair database. also compacts. note: slow.";
}
@ -1792,6 +1793,10 @@ namespace mongo {
if ( c->adminOnly() )
log( 2 ) << "command: " << cmdObj << endl;
if (c->maintenanceMode() && theReplSet && theReplSet->isSecondary()) {
theReplSet->setMaintenanceMode(true);
}
if ( c->locktype() == Command::NONE ) {
// we also trust that this won't crash
client.curop()->ensureStarted();
@ -1799,6 +1804,11 @@ namespace mongo {
int ok = c->run( dbname , cmdObj , queryOptions, errmsg , result , fromRepl );
if ( ! ok )
result.append( "errmsg" , errmsg );
if (c->maintenanceMode() && theReplSet) {
theReplSet->setMaintenanceMode(false);
}
return ok;
}
@ -1812,11 +1822,13 @@ namespace mongo {
client.curop()->ensureStarted();
Client::Context ctx( dbname , dbpath , &lk , c->requiresAuth() );
bool retval = true;
try {
string errmsg;
if ( ! c->run(dbname, cmdObj, queryOptions, errmsg, result, fromRepl ) ) {
result.append( "errmsg" , errmsg );
return false;
retval = false;
}
}
catch ( DBException& e ) {
@ -1824,14 +1836,18 @@ namespace mongo {
ss << "exception: " << e.what();
result.append( "errmsg" , ss.str() );
result.append( "code" , e.getCode() );
return false;
retval = false;
}
if ( c->logTheOp() && ! fromRepl ) {
if ( retval && c->logTheOp() && ! fromRepl ) {
logOp("c", cmdns, cmdObj);
}
return true;
if (c->maintenanceMode() && theReplSet) {
theReplSet->setMaintenanceMode(false);
}
return retval;
}

View File

@ -70,10 +70,27 @@ namespace mongo {
void ReplSetImpl::changeState(MemberState s) { box.change(s, _self); }
void ReplSetImpl::setMaintenanceMode(const bool inc) {
lock lk(this);
if (inc) {
log() << "replSet going into maintenance mode (" << _maintenanceMode << " other tasks)" << rsLog;
_maintenanceMode++;
changeState(MemberState::RS_RECOVERING);
}
else {
_maintenanceMode--;
// no need to change state, syncTail will try to go live as a secondary soon
log() << "leaving maintenance mode (" << _maintenanceMode << " other tasks)" << rsLog;
}
}
Member* ReplSetImpl::getMostElectable() {
lock lk(this);
Member *max = 0;
Member *max = 0;
for (set<unsigned>::iterator it = _electableSet.begin(); it != _electableSet.end(); it++) {
const Member *temp = findById(*it);
@ -298,8 +315,10 @@ namespace mongo {
_currentSyncTarget(0),
_hbmsgTime(0),
_self(0),
_maintenanceMode(0),
mgr( new Manager(this) ),
ghost( new GhostSync(this) ) {
_cfg = 0;
memset(_hbmsg, 0, sizeof(_hbmsg));
strcpy( _hbmsg , "initial startup" );

View File

@ -446,11 +446,20 @@ namespace mongo {
List1<Member> _members; // all members of the set EXCEPT _self.
ReplSetConfig::MemberCfg _config; // config of _self
unsigned _id; // _id of _self
int _maintenanceMode; // if we should stay in recovering state
public:
// this is called from within a writelock in logOpRS
unsigned selfId() const { return _id; }
Manager *mgr;
GhostSync *ghost;
/**
* This forces a secondary to go into recovering state and stay there
* until this is called again, passing in "false". Multiple threads can
* call this and it will leave maintenance mode once all of the callers
* have called it again, passing in false.
*/
void setMaintenanceMode(const bool inc);
private:
Member* head() const { return _members.head(); }
public:

View File

@ -188,6 +188,16 @@ namespace mongo {
*/
bool ReplSetImpl::tryToGoLiveAsASecondary(OpTime& /*out*/ minvalid) {
bool golive = false;
{
lock lk( this );
if (_maintenanceMode > 0) {
// we're not actually going live
return true;
}
}
{
readlock lk("local.replset.minvalid");
BSONObj mv;

View File

@ -0,0 +1,27 @@
var replTest = new ReplSetTest( {name: 'unicomplex', nodes: 3} );
var conns = replTest.startSet();
replTest.initiate();
// Make sure we have a master
var master = replTest.getMaster();
for (i=0;i<10000; i++) { master.getDB("bar").foo.insert({x:1,y:i,abc:123,str:"foo bar baz"}); }
for (i=0;i<1000; i++) { master.getDB("bar").foo.update({y:i},{$push :{foo : "barrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr"}}); }
replTest.awaitReplication();
assert.soon(function() { return conns[2].getDB("admin").isMaster().secondary; });
join = startParallelShell( "db.getSisterDB('bar').runCommand({compact : 'foo'});", replTest.ports[2] );
print("check secondary goes to recovering");
assert.soon(function() { return !conns[2].getDB("admin").isMaster().secondary; });
print("joining");
join();
print("check secondary becomes a secondary again");
assert.soon(function() { return conns[2].getDB("admin").isMaster().secondary; });