Skip to content

Instantly share code, notes, and snippets.

@jmikola
Created December 18, 2012 18:23
Show Gist options
  • Save jmikola/4330559 to your computer and use it in GitHub Desktop.
Save jmikola/4330559 to your computer and use it in GitHub Desktop.
MongoDB replica set administration using the PHP driver
<?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'])));
}
@jmikola
Copy link
Author

jmikola commented Dec 19, 2012

Console log:

$ php rs_remove.php 
RS members: honeydew:2000,honeydew:2002,honeydew:2001; primary: honeydew:2000
Stepping down honeydew:2000…
Sleeping for five seconds…
Polling for new primary…
Found new primary honeydew:2002; removing old primary honeydew:2000…
Sleeping for five seconds…
RS members: honeydew:2002,honeydew:2001; primary: honeydew:2002
Stepping down honeydew:2002…
Sleeping for five seconds…
Polling for new primary…
Retry: stepping down honeydew:2002…
Found new primary honeydew:2001; removing old primary honeydew:2002…
Sleeping for five seconds…
RS members: honeydew:2001; primary: honeydew:2001
RS has only one member; aborting…

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment