Created
December 18, 2012 18:23
-
-
Save jmikola/4330559 to your computer and use it in GitHub Desktop.
MongoDB replica set administration using the PHP driver
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Testing replica set step down and node removal | |
* | |
* Assumes a three-member replica set is running: | |
* | |
* mongod --port 2000 --dbpath /data/rs0-db0 --replSet rs0 | |
* mongod --port 2001 --dbpath /data/rs0-db1 --replSet rs0 | |
* mongod --port 2002 --dbpath /data/rs0-db2 --replSet rs0 | |
*/ | |
$m = new MongoClient('mongodb://honeydew:2000,honeydew:2001,honeydew:2002', array('replicaSet' => 'rs0')); | |
for (;;) { | |
$rs = $m->admin->command(array('isMaster'=>1)); | |
printf("RS members: %s; primary: %s\n", implode(',', $rs['hosts']), isset($rs['primary']) ? $rs['primary'] : 'not found'); | |
if (1 === count($rs['hosts'])) { | |
printf("RS has only one member; aborting…\n"); | |
break; | |
} | |
if (isset($rs['primary'])) { | |
stepDownAndRemove($m, $rs['primary']); | |
} | |
printf("Sleeping for five seconds…\n"); | |
sleep(5); | |
} | |
/** | |
* Step down the given old primary and remove it from the replica set | |
* | |
* @param MongoClient $m | |
* @param string $oldPrimary | |
*/ | |
function stepDownAndRemove(MongoClient $m, $oldPrimary) | |
{ | |
printf("Stepping down %s…\n", $oldPrimary); | |
rsStepDown($m); | |
printf("Sleeping for five seconds…\n"); | |
sleep(5); | |
printf("Polling for new primary…\n"); | |
for ($i = 0; $oldPrimary === ($newPrimary = rsPollForPrimary($m)); $i++) { | |
/* If the previous step down request was too soon after an failover, it | |
* may not have been honored. Retry every five polls. | |
*/ | |
if (0 == $i % 5) { | |
printf("Retry: stepping down %s…\n", $oldPrimary); | |
rsStepDown($m); | |
} | |
// Sleep to prevent spamming of isMaster queries | |
sleep(1); | |
} | |
printf("Found new primary %s; removing old primary %s…\n", $newPrimary, $oldPrimary); | |
rsRemove($m, $oldPrimary); | |
} | |
/** | |
* Poll the server with isMaster queries until a primary is found | |
* | |
* @param MongoClient $m | |
*/ | |
function rsPollForPrimary(MongoClient $m) | |
{ | |
for (;;) { | |
try { | |
$rs = $m->admin->command(array('isMaster' => 1)); | |
} catch (MongoConnectionException $e) { | |
// Driver may not find a candidate if an election is in progress | |
} | |
if (isset($rs['primary'])) { | |
return $rs['primary']; | |
} | |
sleep(1); | |
} | |
} | |
/** | |
* Ported from the rs.stepDown() helper function in the MongoDB shell | |
* | |
* @param MongoClient $m | |
* @param integer $sec | |
*/ | |
function rsStepDown(MongoClient $m, $sec = 60) | |
{ | |
try { | |
$m->admin->command(array('replSetStepDown' => $sec)); | |
} catch (MongoCursorException $e) { | |
// Driver will drop the connection | |
} | |
} | |
/** | |
* Ported from the rs.remove() helper function in the MongoDB shell | |
* | |
* @param MongoClient $m | |
* @param string $host | |
*/ | |
function rsRemove(MongoClient $m, $host) | |
{ | |
$collection = $m->selectCollection('local', 'system.replset'); | |
if (1 !== $collection->count()) { | |
throw new UnexpectedValueException('local.system.replset has unexpected contents'); | |
} | |
$config = $collection->findOne(); | |
if (empty($config)) { | |
throw new UnexpectedValueException('no config object retrievable from local.system.replset'); | |
} | |
$config['version']++; | |
foreach ($config['members'] as $i => $_) { | |
if ($host === $config['members'][$i]['host']) { | |
array_splice($config['members'], $i, 1); | |
try { | |
$m->admin->command(array('replSetReconfig' => $config)); | |
} catch (MongoCursorException $e) { | |
// Driver will drop the connection | |
} | |
return; | |
} | |
} | |
throw new UnexpectedValueException(sprintf("couldn't find %s in %s", $host, json_encode($config['members']))); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Console log: