Send graceful shutdown signals to daemons in Phabricator

Summary:
Fixes T5855. Adds a `--graceful N` flag to `phd stop` and `phd restart`.

`phd` will send SIGINT, wait `N` seconds, SIGTERM, wait 15 seconds, and SIGKILL. By default, `N` is 15.

Test Plan:
  - Ran `bin/phd debug ...` and used `^C` to interrupt daemons. Saw graceful shutdown behavior, and abrupt termination on multiple `^C`.
  - Ran `bin/phd start`, `bin/phd stop` and `bin/phd restart` with `--graceful` set to various things, notably `0`. Saw graceful shutdowns on the CLI and in the web UI. With `0`, abrupt shutdowns.

Reviewers: btrahan, hach-que

Reviewed By: hach-que

Subscribers: epriestley

Maniphest Tasks: T5855

Differential Revision: https://secure.phabricator.com/D10228
This commit is contained in:
epriestley
2014-08-11 20:18:31 -07:00
parent aab0ed1c50
commit 9309723ac4
13 changed files with 117 additions and 41 deletions

View File

@@ -286,7 +286,7 @@ abstract class PhabricatorDaemonManagementWorkflow
return 0;
}
protected final function executeStopCommand(array $pids) {
protected final function executeStopCommand(array $pids, $grace_period) {
$console = PhutilConsole::getConsole();
$daemons = $this->loadRunningDaemons();
@@ -325,39 +325,20 @@ abstract class PhabricatorDaemonManagementWorkflow
}
$all_daemons = $running;
foreach ($running as $key => $daemon) {
$pid = $daemon->getPID();
$name = $daemon->getName();
$console->writeErr(pht("Stopping daemon '%s' (%s)...", $name, $pid)."\n");
if (!$daemon->isRunning()) {
$console->writeErr(pht('Daemon is not running.')."\n");
unset($running[$key]);
$daemon->updateStatus(PhabricatorDaemonLog::STATUS_EXITED);
} else {
posix_kill($pid, SIGINT);
}
// If we're doing a graceful shutdown, try SIGINT first.
if ($grace_period) {
$running = $this->sendSignal($running, SIGINT, $grace_period);
}
$start = time();
do {
foreach ($running as $key => $daemon) {
$pid = $daemon->getPID();
if (!$daemon->isRunning()) {
$console->writeOut(pht('Daemon %s exited normally.', $pid)."\n");
unset($running[$key]);
}
}
if (empty($running)) {
break;
}
usleep(100000);
} while (time() < $start + 15);
// If we still have daemons, SIGTERM them.
if ($running) {
$running = $this->sendSignal($running, SIGTERM, 15);
}
foreach ($running as $key => $daemon) {
$pid = $daemon->getPID();
$console->writeErr(pht('Sending daemon %s a SIGKILL.', $pid)."\n");
posix_kill($pid, SIGKILL);
// If the overseer is still alive, SIGKILL it.
if ($running) {
$this->sendSignal($running, SIGKILL, 0);
}
foreach ($all_daemons as $daemon) {
@@ -369,6 +350,49 @@ abstract class PhabricatorDaemonManagementWorkflow
return 0;
}
private function sendSignal(array $daemons, $signo, $wait) {
$console = PhutilConsole::getConsole();
foreach ($daemons as $key => $daemon) {
$pid = $daemon->getPID();
$name = $daemon->getName();
switch ($signo) {
case SIGINT:
$message = pht("Interrupting daemon '%s' (%s)...", $name, $pid);
break;
case SIGTERM:
$message = pht("Terminating daemon '%s' (%s)...", $name, $pid);
break;
case SIGKILL:
$message = pht("Killing daemon '%s' (%s)...", $name, $pid);
break;
}
$console->writeOut("%s\n", $message);
posix_kill($pid, $signo);
}
if ($wait) {
$start = PhabricatorTime::getNow();
do {
foreach ($daemons as $key => $daemon) {
$pid = $daemon->getPID();
if (!$daemon->isRunning()) {
$console->writeOut(pht('Daemon %s exited.', $pid)."\n");
unset($daemons[$key]);
}
}
if (empty($daemons)) {
break;
}
usleep(100000);
} while (PhabricatorTime::getNow() < $start + $wait);
}
return $daemons;
}
private function freeActiveLeases() {
$task_table = id(new PhabricatorWorkerActiveTask());
$conn_w = $task_table->establishConnection('w');