1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2025-01-09 14:21:01 +01:00
phorge-arcanist/src/hgdaemon/ArcanistHgProxyServer.php

488 lines
13 KiB
PHP
Raw Normal View History

<?php
/**
* Server which @{class:ArcanistHgProxyClient} clients connect to. This
* server binds to a Mercurial working copy and creates a Mercurial process and
* a unix domain socket in that working copy. It listens for connections on
* the socket, reads commands from them, and forwards their requests to the
* Mercurial process. It then returns responses to the original clients.
*
* Note that this server understands the underlying protocol and completely
* decodes messages from both the client and server before re-encoding them
* and relaying them to their final destinations. It must do this (at least
* in part) to determine where messages begin and end. Additionally, this proxy
* sends and receives the Mercurial cmdserver protocol exactly, without
* any extensions or sneakiness.
*
* The advantage of this mechanism is that it avoids the overhead of starting
* a Mercurial process for each Mercurial command, which can exceed 100ms per
* invocation. This server can also accept connections from multiple clients
* and serve them from a single Mercurial server process.
*
* @task construct Construction
Add various flags to the HgProxy daemons Summary: - Add flags to exit after an idle time or client count. - Add flags to control daemonization. - Add flags to control output. - Add flags to skip the "hello" frame of the protocol. - Make the client launch a server if one does not exist. The one-time overhead to launch a server and run a command through it looks to be ~130% of the overhead to run the command directly with "hg", so even if we never run a second command we're not paying too much. The incremental overhead to run subsequent command appears to be less than 3% of the overhead to run the command directly with "hg" (and maybe less than 1%, I'm not sure how long the computation part of a command like 'hg log' "actually" takes). The overhead to launch a PHP client, connect to an existing server, run a command, and then print it and exit is roughly 50% of the overhead to run the command directly with "hg". So theoretically a user can achieve an amortized 2x performance increase for all 'hg' commands by aliasing 'hg' to the PHP client in their shell. Test Plan: - Ran servers with idle and client count limits, let them idle and/or hit their connection limits, saw them exit. - Ran foreground and background servers. - Ran a daemon server with redirected stdout/stderr. Verified logs appeared. - Ran with --quiet. - Ran clients and servers with and without --skip-hello, things work if they agree and break if they disagree. The throughput gain on this is fairly small (maybe 5%?) but it seems simple enough to keep for the moment. - Ran serverless clients and verified that servers launched the first time, were available subsequently, and relaunched after 15 seconds idle. Reviewers: csilvers, vrana, btrahan Reviewed By: csilvers CC: aran Differential Revision: https://secure.phabricator.com/D2680
2012-06-26 20:00:26 +02:00
* @task config Configuration
* @task server Serving Requests
* @task client Managing Clients
* @task hg Managing Mercurial
* @task internal Internals
*/
final class ArcanistHgProxyServer {
private $workingCopy;
private $socket;
private $hello;
Add various flags to the HgProxy daemons Summary: - Add flags to exit after an idle time or client count. - Add flags to control daemonization. - Add flags to control output. - Add flags to skip the "hello" frame of the protocol. - Make the client launch a server if one does not exist. The one-time overhead to launch a server and run a command through it looks to be ~130% of the overhead to run the command directly with "hg", so even if we never run a second command we're not paying too much. The incremental overhead to run subsequent command appears to be less than 3% of the overhead to run the command directly with "hg" (and maybe less than 1%, I'm not sure how long the computation part of a command like 'hg log' "actually" takes). The overhead to launch a PHP client, connect to an existing server, run a command, and then print it and exit is roughly 50% of the overhead to run the command directly with "hg". So theoretically a user can achieve an amortized 2x performance increase for all 'hg' commands by aliasing 'hg' to the PHP client in their shell. Test Plan: - Ran servers with idle and client count limits, let them idle and/or hit their connection limits, saw them exit. - Ran foreground and background servers. - Ran a daemon server with redirected stdout/stderr. Verified logs appeared. - Ran with --quiet. - Ran clients and servers with and without --skip-hello, things work if they agree and break if they disagree. The throughput gain on this is fairly small (maybe 5%?) but it seems simple enough to keep for the moment. - Ran serverless clients and verified that servers launched the first time, were available subsequently, and relaunched after 15 seconds idle. Reviewers: csilvers, vrana, btrahan Reviewed By: csilvers CC: aran Differential Revision: https://secure.phabricator.com/D2680
2012-06-26 20:00:26 +02:00
private $quiet;
private $clientLimit;
private $lifetimeClientCount;
private $idleLimit;
private $idleSince;
private $skipHello;
private $doNotDaemonize;
/* -( Construction )------------------------------------------------------- */
/**
* Build a new server. This server is bound to a working copy. The server
* is inactive until you @{method:start} it.
*
* @param string Path to a Mercurial working copy.
*
* @task construct
*/
public function __construct($working_copy) {
$this->workingCopy = Filesystem::resolvePath($working_copy);
}
Add various flags to the HgProxy daemons Summary: - Add flags to exit after an idle time or client count. - Add flags to control daemonization. - Add flags to control output. - Add flags to skip the "hello" frame of the protocol. - Make the client launch a server if one does not exist. The one-time overhead to launch a server and run a command through it looks to be ~130% of the overhead to run the command directly with "hg", so even if we never run a second command we're not paying too much. The incremental overhead to run subsequent command appears to be less than 3% of the overhead to run the command directly with "hg" (and maybe less than 1%, I'm not sure how long the computation part of a command like 'hg log' "actually" takes). The overhead to launch a PHP client, connect to an existing server, run a command, and then print it and exit is roughly 50% of the overhead to run the command directly with "hg". So theoretically a user can achieve an amortized 2x performance increase for all 'hg' commands by aliasing 'hg' to the PHP client in their shell. Test Plan: - Ran servers with idle and client count limits, let them idle and/or hit their connection limits, saw them exit. - Ran foreground and background servers. - Ran a daemon server with redirected stdout/stderr. Verified logs appeared. - Ran with --quiet. - Ran clients and servers with and without --skip-hello, things work if they agree and break if they disagree. The throughput gain on this is fairly small (maybe 5%?) but it seems simple enough to keep for the moment. - Ran serverless clients and verified that servers launched the first time, were available subsequently, and relaunched after 15 seconds idle. Reviewers: csilvers, vrana, btrahan Reviewed By: csilvers CC: aran Differential Revision: https://secure.phabricator.com/D2680
2012-06-26 20:00:26 +02:00
/* -( Configuration )------------------------------------------------------ */
/**
* Disable status messages to stdout. Controlled with `--quiet`.
*
* @param bool True to disable status messages.
* @return this
*
* @task config
*/
public function setQuiet($quiet) {
$this->quiet = $quiet;
return $this;
}
/**
* Configure a client limit. After serving this many clients, the server
* will exit. Controlled with `--client-limit`.
*
* You can use `--client-limit 1` with `--xprofile` and `--do-not-daemonize`
* to profile the server.
*
* @param int Client limit, or 0 to disable limit.
* @return this
*
* @task config
*/
public function setClientLimit($limit) {
$this->clientLimit = $limit;
return $this;
}
/**
* Configure an idle time limit. After this many seconds idle, the server
* will exit. Controlled with `--idle-limit`.
*
* @param int Idle limit, or 0 to disable limit.
* @return this
*
* @task config
*/
public function setIdleLimit($limit) {
$this->idleLimit = $limit;
return $this;
}
/**
* When clients connect, do not send the "capabilities" message expected by
* the Mercurial protocol. This deviates from the protocol and will only work
* if the clients are also configured not to expect the message, but slightly
* improves performance. Controlled with --skip-hello.
*
* @param bool True to skip the "capabilities" message.
* @return this
*
* @task config
*/
public function setSkipHello($skip) {
$this->skipHello = $skip;
return $this;
}
/**
* Configure whether the server runs in the foreground or daemonizes.
* Controlled by --do-not-daemonize. Primarily useful for debugging.
*
* @param bool True to run in the foreground.
* @return this
*
* @task config
*/
public function setDoNotDaemonize($do_not_daemonize) {
$this->doNotDaemonize = $do_not_daemonize;
return $this;
}
/* -( Serving Requests )--------------------------------------------------- */
/**
Add various flags to the HgProxy daemons Summary: - Add flags to exit after an idle time or client count. - Add flags to control daemonization. - Add flags to control output. - Add flags to skip the "hello" frame of the protocol. - Make the client launch a server if one does not exist. The one-time overhead to launch a server and run a command through it looks to be ~130% of the overhead to run the command directly with "hg", so even if we never run a second command we're not paying too much. The incremental overhead to run subsequent command appears to be less than 3% of the overhead to run the command directly with "hg" (and maybe less than 1%, I'm not sure how long the computation part of a command like 'hg log' "actually" takes). The overhead to launch a PHP client, connect to an existing server, run a command, and then print it and exit is roughly 50% of the overhead to run the command directly with "hg". So theoretically a user can achieve an amortized 2x performance increase for all 'hg' commands by aliasing 'hg' to the PHP client in their shell. Test Plan: - Ran servers with idle and client count limits, let them idle and/or hit their connection limits, saw them exit. - Ran foreground and background servers. - Ran a daemon server with redirected stdout/stderr. Verified logs appeared. - Ran with --quiet. - Ran clients and servers with and without --skip-hello, things work if they agree and break if they disagree. The throughput gain on this is fairly small (maybe 5%?) but it seems simple enough to keep for the moment. - Ran serverless clients and verified that servers launched the first time, were available subsequently, and relaunched after 15 seconds idle. Reviewers: csilvers, vrana, btrahan Reviewed By: csilvers CC: aran Differential Revision: https://secure.phabricator.com/D2680
2012-06-26 20:00:26 +02:00
* Start the server. This method returns after the client limit or idle
* limit are exceeded. If neither limit is configured, this method does not
* exit.
*
Add various flags to the HgProxy daemons Summary: - Add flags to exit after an idle time or client count. - Add flags to control daemonization. - Add flags to control output. - Add flags to skip the "hello" frame of the protocol. - Make the client launch a server if one does not exist. The one-time overhead to launch a server and run a command through it looks to be ~130% of the overhead to run the command directly with "hg", so even if we never run a second command we're not paying too much. The incremental overhead to run subsequent command appears to be less than 3% of the overhead to run the command directly with "hg" (and maybe less than 1%, I'm not sure how long the computation part of a command like 'hg log' "actually" takes). The overhead to launch a PHP client, connect to an existing server, run a command, and then print it and exit is roughly 50% of the overhead to run the command directly with "hg". So theoretically a user can achieve an amortized 2x performance increase for all 'hg' commands by aliasing 'hg' to the PHP client in their shell. Test Plan: - Ran servers with idle and client count limits, let them idle and/or hit their connection limits, saw them exit. - Ran foreground and background servers. - Ran a daemon server with redirected stdout/stderr. Verified logs appeared. - Ran with --quiet. - Ran clients and servers with and without --skip-hello, things work if they agree and break if they disagree. The throughput gain on this is fairly small (maybe 5%?) but it seems simple enough to keep for the moment. - Ran serverless clients and verified that servers launched the first time, were available subsequently, and relaunched after 15 seconds idle. Reviewers: csilvers, vrana, btrahan Reviewed By: csilvers CC: aran Differential Revision: https://secure.phabricator.com/D2680
2012-06-26 20:00:26 +02:00
* @return null
*
* @task server
*/
public function start() {
// Create the unix domain socket in the working copy to listen for clients.
$socket = $this->startWorkingCopySocket();
$this->socket = $socket;
Add various flags to the HgProxy daemons Summary: - Add flags to exit after an idle time or client count. - Add flags to control daemonization. - Add flags to control output. - Add flags to skip the "hello" frame of the protocol. - Make the client launch a server if one does not exist. The one-time overhead to launch a server and run a command through it looks to be ~130% of the overhead to run the command directly with "hg", so even if we never run a second command we're not paying too much. The incremental overhead to run subsequent command appears to be less than 3% of the overhead to run the command directly with "hg" (and maybe less than 1%, I'm not sure how long the computation part of a command like 'hg log' "actually" takes). The overhead to launch a PHP client, connect to an existing server, run a command, and then print it and exit is roughly 50% of the overhead to run the command directly with "hg". So theoretically a user can achieve an amortized 2x performance increase for all 'hg' commands by aliasing 'hg' to the PHP client in their shell. Test Plan: - Ran servers with idle and client count limits, let them idle and/or hit their connection limits, saw them exit. - Ran foreground and background servers. - Ran a daemon server with redirected stdout/stderr. Verified logs appeared. - Ran with --quiet. - Ran clients and servers with and without --skip-hello, things work if they agree and break if they disagree. The throughput gain on this is fairly small (maybe 5%?) but it seems simple enough to keep for the moment. - Ran serverless clients and verified that servers launched the first time, were available subsequently, and relaunched after 15 seconds idle. Reviewers: csilvers, vrana, btrahan Reviewed By: csilvers CC: aran Differential Revision: https://secure.phabricator.com/D2680
2012-06-26 20:00:26 +02:00
if (!$this->doNotDaemonize) {
$this->daemonize();
}
// Start the Mercurial process which we'll forward client requests to.
$hg = $this->startMercurialProcess();
$clients = array();
$this->log(null, 'Listening');
Add various flags to the HgProxy daemons Summary: - Add flags to exit after an idle time or client count. - Add flags to control daemonization. - Add flags to control output. - Add flags to skip the "hello" frame of the protocol. - Make the client launch a server if one does not exist. The one-time overhead to launch a server and run a command through it looks to be ~130% of the overhead to run the command directly with "hg", so even if we never run a second command we're not paying too much. The incremental overhead to run subsequent command appears to be less than 3% of the overhead to run the command directly with "hg" (and maybe less than 1%, I'm not sure how long the computation part of a command like 'hg log' "actually" takes). The overhead to launch a PHP client, connect to an existing server, run a command, and then print it and exit is roughly 50% of the overhead to run the command directly with "hg". So theoretically a user can achieve an amortized 2x performance increase for all 'hg' commands by aliasing 'hg' to the PHP client in their shell. Test Plan: - Ran servers with idle and client count limits, let them idle and/or hit their connection limits, saw them exit. - Ran foreground and background servers. - Ran a daemon server with redirected stdout/stderr. Verified logs appeared. - Ran with --quiet. - Ran clients and servers with and without --skip-hello, things work if they agree and break if they disagree. The throughput gain on this is fairly small (maybe 5%?) but it seems simple enough to keep for the moment. - Ran serverless clients and verified that servers launched the first time, were available subsequently, and relaunched after 15 seconds idle. Reviewers: csilvers, vrana, btrahan Reviewed By: csilvers CC: aran Differential Revision: https://secure.phabricator.com/D2680
2012-06-26 20:00:26 +02:00
$this->idleSince = time();
while (true) {
// Wait for activity on any active clients, the Mercurial process, or
// the listening socket where new clients connect.
PhutilChannel::waitForAny(
array_merge($clients, array($hg)),
array(
Add various flags to the HgProxy daemons Summary: - Add flags to exit after an idle time or client count. - Add flags to control daemonization. - Add flags to control output. - Add flags to skip the "hello" frame of the protocol. - Make the client launch a server if one does not exist. The one-time overhead to launch a server and run a command through it looks to be ~130% of the overhead to run the command directly with "hg", so even if we never run a second command we're not paying too much. The incremental overhead to run subsequent command appears to be less than 3% of the overhead to run the command directly with "hg" (and maybe less than 1%, I'm not sure how long the computation part of a command like 'hg log' "actually" takes). The overhead to launch a PHP client, connect to an existing server, run a command, and then print it and exit is roughly 50% of the overhead to run the command directly with "hg". So theoretically a user can achieve an amortized 2x performance increase for all 'hg' commands by aliasing 'hg' to the PHP client in their shell. Test Plan: - Ran servers with idle and client count limits, let them idle and/or hit their connection limits, saw them exit. - Ran foreground and background servers. - Ran a daemon server with redirected stdout/stderr. Verified logs appeared. - Ran with --quiet. - Ran clients and servers with and without --skip-hello, things work if they agree and break if they disagree. The throughput gain on this is fairly small (maybe 5%?) but it seems simple enough to keep for the moment. - Ran serverless clients and verified that servers launched the first time, were available subsequently, and relaunched after 15 seconds idle. Reviewers: csilvers, vrana, btrahan Reviewed By: csilvers CC: aran Differential Revision: https://secure.phabricator.com/D2680
2012-06-26 20:00:26 +02:00
'read' => $socket ? array($socket) : array(),
'except' => $socket ? array($socket) : array(),
));
if (!$hg->update()) {
throw new Exception('Server exited unexpectedly!');
}
// Accept any new clients.
Add various flags to the HgProxy daemons Summary: - Add flags to exit after an idle time or client count. - Add flags to control daemonization. - Add flags to control output. - Add flags to skip the "hello" frame of the protocol. - Make the client launch a server if one does not exist. The one-time overhead to launch a server and run a command through it looks to be ~130% of the overhead to run the command directly with "hg", so even if we never run a second command we're not paying too much. The incremental overhead to run subsequent command appears to be less than 3% of the overhead to run the command directly with "hg" (and maybe less than 1%, I'm not sure how long the computation part of a command like 'hg log' "actually" takes). The overhead to launch a PHP client, connect to an existing server, run a command, and then print it and exit is roughly 50% of the overhead to run the command directly with "hg". So theoretically a user can achieve an amortized 2x performance increase for all 'hg' commands by aliasing 'hg' to the PHP client in their shell. Test Plan: - Ran servers with idle and client count limits, let them idle and/or hit their connection limits, saw them exit. - Ran foreground and background servers. - Ran a daemon server with redirected stdout/stderr. Verified logs appeared. - Ran with --quiet. - Ran clients and servers with and without --skip-hello, things work if they agree and break if they disagree. The throughput gain on this is fairly small (maybe 5%?) but it seems simple enough to keep for the moment. - Ran serverless clients and verified that servers launched the first time, were available subsequently, and relaunched after 15 seconds idle. Reviewers: csilvers, vrana, btrahan Reviewed By: csilvers CC: aran Differential Revision: https://secure.phabricator.com/D2680
2012-06-26 20:00:26 +02:00
while ($socket && ($client = $this->acceptNewClient($socket))) {
$clients[] = $client;
$key = last_key($clients);
$client->setName($key);
$this->log($client, 'Connected');
Add various flags to the HgProxy daemons Summary: - Add flags to exit after an idle time or client count. - Add flags to control daemonization. - Add flags to control output. - Add flags to skip the "hello" frame of the protocol. - Make the client launch a server if one does not exist. The one-time overhead to launch a server and run a command through it looks to be ~130% of the overhead to run the command directly with "hg", so even if we never run a second command we're not paying too much. The incremental overhead to run subsequent command appears to be less than 3% of the overhead to run the command directly with "hg" (and maybe less than 1%, I'm not sure how long the computation part of a command like 'hg log' "actually" takes). The overhead to launch a PHP client, connect to an existing server, run a command, and then print it and exit is roughly 50% of the overhead to run the command directly with "hg". So theoretically a user can achieve an amortized 2x performance increase for all 'hg' commands by aliasing 'hg' to the PHP client in their shell. Test Plan: - Ran servers with idle and client count limits, let them idle and/or hit their connection limits, saw them exit. - Ran foreground and background servers. - Ran a daemon server with redirected stdout/stderr. Verified logs appeared. - Ran with --quiet. - Ran clients and servers with and without --skip-hello, things work if they agree and break if they disagree. The throughput gain on this is fairly small (maybe 5%?) but it seems simple enough to keep for the moment. - Ran serverless clients and verified that servers launched the first time, were available subsequently, and relaunched after 15 seconds idle. Reviewers: csilvers, vrana, btrahan Reviewed By: csilvers CC: aran Differential Revision: https://secure.phabricator.com/D2680
2012-06-26 20:00:26 +02:00
$this->idleSince = time();
// Check if we've hit the client limit. If there's a configured
// client limit and we've hit it, stop accepting new connections
// and close the socket.
$this->lifetimeClientCount++;
if ($this->clientLimit) {
if ($this->lifetimeClientCount >= $this->clientLimit) {
$this->closeSocket();
$socket = null;
}
}
}
// Update all the active clients.
foreach ($clients as $key => $client) {
Add various flags to the HgProxy daemons Summary: - Add flags to exit after an idle time or client count. - Add flags to control daemonization. - Add flags to control output. - Add flags to skip the "hello" frame of the protocol. - Make the client launch a server if one does not exist. The one-time overhead to launch a server and run a command through it looks to be ~130% of the overhead to run the command directly with "hg", so even if we never run a second command we're not paying too much. The incremental overhead to run subsequent command appears to be less than 3% of the overhead to run the command directly with "hg" (and maybe less than 1%, I'm not sure how long the computation part of a command like 'hg log' "actually" takes). The overhead to launch a PHP client, connect to an existing server, run a command, and then print it and exit is roughly 50% of the overhead to run the command directly with "hg". So theoretically a user can achieve an amortized 2x performance increase for all 'hg' commands by aliasing 'hg' to the PHP client in their shell. Test Plan: - Ran servers with idle and client count limits, let them idle and/or hit their connection limits, saw them exit. - Ran foreground and background servers. - Ran a daemon server with redirected stdout/stderr. Verified logs appeared. - Ran with --quiet. - Ran clients and servers with and without --skip-hello, things work if they agree and break if they disagree. The throughput gain on this is fairly small (maybe 5%?) but it seems simple enough to keep for the moment. - Ran serverless clients and verified that servers launched the first time, were available subsequently, and relaunched after 15 seconds idle. Reviewers: csilvers, vrana, btrahan Reviewed By: csilvers CC: aran Differential Revision: https://secure.phabricator.com/D2680
2012-06-26 20:00:26 +02:00
if ($this->updateClient($client, $hg)) {
// In this case, the client is still connected so just move on to
// the next one. Otherwise we continue below and handle the
// disconnect.
Add various flags to the HgProxy daemons Summary: - Add flags to exit after an idle time or client count. - Add flags to control daemonization. - Add flags to control output. - Add flags to skip the "hello" frame of the protocol. - Make the client launch a server if one does not exist. The one-time overhead to launch a server and run a command through it looks to be ~130% of the overhead to run the command directly with "hg", so even if we never run a second command we're not paying too much. The incremental overhead to run subsequent command appears to be less than 3% of the overhead to run the command directly with "hg" (and maybe less than 1%, I'm not sure how long the computation part of a command like 'hg log' "actually" takes). The overhead to launch a PHP client, connect to an existing server, run a command, and then print it and exit is roughly 50% of the overhead to run the command directly with "hg". So theoretically a user can achieve an amortized 2x performance increase for all 'hg' commands by aliasing 'hg' to the PHP client in their shell. Test Plan: - Ran servers with idle and client count limits, let them idle and/or hit their connection limits, saw them exit. - Ran foreground and background servers. - Ran a daemon server with redirected stdout/stderr. Verified logs appeared. - Ran with --quiet. - Ran clients and servers with and without --skip-hello, things work if they agree and break if they disagree. The throughput gain on this is fairly small (maybe 5%?) but it seems simple enough to keep for the moment. - Ran serverless clients and verified that servers launched the first time, were available subsequently, and relaunched after 15 seconds idle. Reviewers: csilvers, vrana, btrahan Reviewed By: csilvers CC: aran Differential Revision: https://secure.phabricator.com/D2680
2012-06-26 20:00:26 +02:00
continue;
}
$this->log($client, 'Disconnected');
unset($clients[$key]);
// If we have a client limit and we've served that many clients, exit.
if ($this->clientLimit) {
if ($this->lifetimeClientCount >= $this->clientLimit) {
if (!$clients) {
$this->log(null, 'Exiting (Client Limit)');
return;
}
}
}
}
// If we have an idle limit and haven't had any activity in at least
// that long, exit.
if ($this->idleLimit) {
$remaining = $this->idleLimit - (time() - $this->idleSince);
if ($remaining <= 0) {
$this->log(null, 'Exiting (Idle Limit)');
return;
}
if ($remaining <= 5) {
$this->log(null, 'Exiting in '.$remaining.' seconds');
}
}
}
}
/**
* Update one client, processing any commands it has sent us. We fully
* process all commands we've received here before returning to the main
* server loop.
*
* @param ArcanistHgClientChannel The client to update.
* @param ArcanistHgServerChannel The Mercurial server.
*
* @task server
*/
private function updateClient(
ArcanistHgClientChannel $client,
ArcanistHgServerChannel $hg) {
if (!$client->update()) {
// Client has disconnected, don't bother proceeding.
return false;
}
// Read a command from the client if one is available. Note that we stop
// updating other clients or accepting new connections while processing a
// command, since there isn't much we can do with them until the server
// finishes executing this command.
$message = $client->read();
if (!$message) {
return true;
}
$this->log($client, '$ '.$message[0].' '.$message[1]);
$t_start = microtime(true);
// Forward the command to the server.
$hg->write($message);
while (true) {
PhutilChannel::waitForAny(array($client, $hg));
if (!$client->update() || !$hg->update()) {
// If either the client or server has exited, bail.
return false;
}
$response = $hg->read();
if (!$response) {
continue;
}
// Forward the response back to the client.
$client->write($response);
// If the response was on the 'r'esult channel, it indicates the end
// of the command output. We can process the next command (if any
// remain) or go back to accepting new connections and servicing
// other clients.
if ($response[0] == 'r') {
// Update the client immediately to try to get the bytes on the wire
// as quickly as possible. This gives us slightly more throughput.
$client->update();
break;
}
}
// Log the elapsed time.
$t_end = microtime(true);
$t = 1000000 * ($t_end - $t_start);
$this->log($client, '< '.number_format($t, 0).'us');
Add various flags to the HgProxy daemons Summary: - Add flags to exit after an idle time or client count. - Add flags to control daemonization. - Add flags to control output. - Add flags to skip the "hello" frame of the protocol. - Make the client launch a server if one does not exist. The one-time overhead to launch a server and run a command through it looks to be ~130% of the overhead to run the command directly with "hg", so even if we never run a second command we're not paying too much. The incremental overhead to run subsequent command appears to be less than 3% of the overhead to run the command directly with "hg" (and maybe less than 1%, I'm not sure how long the computation part of a command like 'hg log' "actually" takes). The overhead to launch a PHP client, connect to an existing server, run a command, and then print it and exit is roughly 50% of the overhead to run the command directly with "hg". So theoretically a user can achieve an amortized 2x performance increase for all 'hg' commands by aliasing 'hg' to the PHP client in their shell. Test Plan: - Ran servers with idle and client count limits, let them idle and/or hit their connection limits, saw them exit. - Ran foreground and background servers. - Ran a daemon server with redirected stdout/stderr. Verified logs appeared. - Ran with --quiet. - Ran clients and servers with and without --skip-hello, things work if they agree and break if they disagree. The throughput gain on this is fairly small (maybe 5%?) but it seems simple enough to keep for the moment. - Ran serverless clients and verified that servers launched the first time, were available subsequently, and relaunched after 15 seconds idle. Reviewers: csilvers, vrana, btrahan Reviewed By: csilvers CC: aran Differential Revision: https://secure.phabricator.com/D2680
2012-06-26 20:00:26 +02:00
$this->idleSince = time();
return true;
}
/* -( Managing Clients )--------------------------------------------------- */
/**
* @task client
*/
public static function getPathToSocket($working_copy) {
return $working_copy.'/.hg/hgdaemon-socket';
}
/**
* @task client
*/
private function startWorkingCopySocket() {
$errno = null;
$errstr = null;
$socket_path = self::getPathToSocket($this->workingCopy);
$socket_uri = 'unix://'.$socket_path;
$socket = @stream_socket_server($socket_uri, $errno, $errstr);
if ($errno || !$socket) {
Filesystem::remove($socket_path);
$socket = @stream_socket_server($socket_uri, $errno, $errstr);
}
if ($errno || !$socket) {
throw new Exception(
"Unable to start socket! Error #{$errno}: {$errstr}");
}
$ok = stream_set_blocking($socket, 0);
if ($ok === false) {
throw new Exception('Unable to set socket nonblocking!');
}
return $socket;
}
/**
* @task client
*/
private function acceptNewClient($socket) {
// NOTE: stream_socket_accept() always blocks, even when the socket has
// been set nonblocking.
$new_client = @stream_socket_accept($socket, $timeout = 0);
if (!$new_client) {
Add various flags to the HgProxy daemons Summary: - Add flags to exit after an idle time or client count. - Add flags to control daemonization. - Add flags to control output. - Add flags to skip the "hello" frame of the protocol. - Make the client launch a server if one does not exist. The one-time overhead to launch a server and run a command through it looks to be ~130% of the overhead to run the command directly with "hg", so even if we never run a second command we're not paying too much. The incremental overhead to run subsequent command appears to be less than 3% of the overhead to run the command directly with "hg" (and maybe less than 1%, I'm not sure how long the computation part of a command like 'hg log' "actually" takes). The overhead to launch a PHP client, connect to an existing server, run a command, and then print it and exit is roughly 50% of the overhead to run the command directly with "hg". So theoretically a user can achieve an amortized 2x performance increase for all 'hg' commands by aliasing 'hg' to the PHP client in their shell. Test Plan: - Ran servers with idle and client count limits, let them idle and/or hit their connection limits, saw them exit. - Ran foreground and background servers. - Ran a daemon server with redirected stdout/stderr. Verified logs appeared. - Ran with --quiet. - Ran clients and servers with and without --skip-hello, things work if they agree and break if they disagree. The throughput gain on this is fairly small (maybe 5%?) but it seems simple enough to keep for the moment. - Ran serverless clients and verified that servers launched the first time, were available subsequently, and relaunched after 15 seconds idle. Reviewers: csilvers, vrana, btrahan Reviewed By: csilvers CC: aran Differential Revision: https://secure.phabricator.com/D2680
2012-06-26 20:00:26 +02:00
return null;
}
$channel = new PhutilSocketChannel($new_client);
$client = new ArcanistHgClientChannel($channel);
Add various flags to the HgProxy daemons Summary: - Add flags to exit after an idle time or client count. - Add flags to control daemonization. - Add flags to control output. - Add flags to skip the "hello" frame of the protocol. - Make the client launch a server if one does not exist. The one-time overhead to launch a server and run a command through it looks to be ~130% of the overhead to run the command directly with "hg", so even if we never run a second command we're not paying too much. The incremental overhead to run subsequent command appears to be less than 3% of the overhead to run the command directly with "hg" (and maybe less than 1%, I'm not sure how long the computation part of a command like 'hg log' "actually" takes). The overhead to launch a PHP client, connect to an existing server, run a command, and then print it and exit is roughly 50% of the overhead to run the command directly with "hg". So theoretically a user can achieve an amortized 2x performance increase for all 'hg' commands by aliasing 'hg' to the PHP client in their shell. Test Plan: - Ran servers with idle and client count limits, let them idle and/or hit their connection limits, saw them exit. - Ran foreground and background servers. - Ran a daemon server with redirected stdout/stderr. Verified logs appeared. - Ran with --quiet. - Ran clients and servers with and without --skip-hello, things work if they agree and break if they disagree. The throughput gain on this is fairly small (maybe 5%?) but it seems simple enough to keep for the moment. - Ran serverless clients and verified that servers launched the first time, were available subsequently, and relaunched after 15 seconds idle. Reviewers: csilvers, vrana, btrahan Reviewed By: csilvers CC: aran Differential Revision: https://secure.phabricator.com/D2680
2012-06-26 20:00:26 +02:00
if (!$this->skipHello) {
$client->write($this->hello);
}
return $client;
}
/* -( Managing Mercurial )------------------------------------------------- */
/**
* Starts a Mercurial process which can actually handle requests.
*
* @return ArcanistHgServerChannel Channel to the Mercurial server.
* @task hg
*/
private function startMercurialProcess() {
// NOTE: "cmdserver.log=-" makes Mercurial use the 'd'ebug channel for
// log messages.
$future = new ExecFuture(
'HGPLAIN=1 hg --config cmdserver.log=- serve --cmdserver pipe');
$future->setCWD($this->workingCopy);
$channel = new PhutilExecChannel($future);
$hg = new ArcanistHgServerChannel($channel);
// The server sends a "hello" message with capability and encoding
// information. Save it and forward it to clients when they connect.
$this->hello = $hg->waitForMessage();
return $hg;
}
/* -( Internals )---------------------------------------------------------- */
/**
* Close and remove the unix domain socket in the working copy.
*
* @task internal
*/
public function __destruct() {
Add various flags to the HgProxy daemons Summary: - Add flags to exit after an idle time or client count. - Add flags to control daemonization. - Add flags to control output. - Add flags to skip the "hello" frame of the protocol. - Make the client launch a server if one does not exist. The one-time overhead to launch a server and run a command through it looks to be ~130% of the overhead to run the command directly with "hg", so even if we never run a second command we're not paying too much. The incremental overhead to run subsequent command appears to be less than 3% of the overhead to run the command directly with "hg" (and maybe less than 1%, I'm not sure how long the computation part of a command like 'hg log' "actually" takes). The overhead to launch a PHP client, connect to an existing server, run a command, and then print it and exit is roughly 50% of the overhead to run the command directly with "hg". So theoretically a user can achieve an amortized 2x performance increase for all 'hg' commands by aliasing 'hg' to the PHP client in their shell. Test Plan: - Ran servers with idle and client count limits, let them idle and/or hit their connection limits, saw them exit. - Ran foreground and background servers. - Ran a daemon server with redirected stdout/stderr. Verified logs appeared. - Ran with --quiet. - Ran clients and servers with and without --skip-hello, things work if they agree and break if they disagree. The throughput gain on this is fairly small (maybe 5%?) but it seems simple enough to keep for the moment. - Ran serverless clients and verified that servers launched the first time, were available subsequently, and relaunched after 15 seconds idle. Reviewers: csilvers, vrana, btrahan Reviewed By: csilvers CC: aran Differential Revision: https://secure.phabricator.com/D2680
2012-06-26 20:00:26 +02:00
$this->closeSocket();
}
private function closeSocket() {
if ($this->socket) {
@stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
@fclose($this->socket);
Filesystem::remove(self::getPathToSocket($this->workingCopy));
$this->socket = null;
}
}
private function log($client, $message) {
Add various flags to the HgProxy daemons Summary: - Add flags to exit after an idle time or client count. - Add flags to control daemonization. - Add flags to control output. - Add flags to skip the "hello" frame of the protocol. - Make the client launch a server if one does not exist. The one-time overhead to launch a server and run a command through it looks to be ~130% of the overhead to run the command directly with "hg", so even if we never run a second command we're not paying too much. The incremental overhead to run subsequent command appears to be less than 3% of the overhead to run the command directly with "hg" (and maybe less than 1%, I'm not sure how long the computation part of a command like 'hg log' "actually" takes). The overhead to launch a PHP client, connect to an existing server, run a command, and then print it and exit is roughly 50% of the overhead to run the command directly with "hg". So theoretically a user can achieve an amortized 2x performance increase for all 'hg' commands by aliasing 'hg' to the PHP client in their shell. Test Plan: - Ran servers with idle and client count limits, let them idle and/or hit their connection limits, saw them exit. - Ran foreground and background servers. - Ran a daemon server with redirected stdout/stderr. Verified logs appeared. - Ran with --quiet. - Ran clients and servers with and without --skip-hello, things work if they agree and break if they disagree. The throughput gain on this is fairly small (maybe 5%?) but it seems simple enough to keep for the moment. - Ran serverless clients and verified that servers launched the first time, were available subsequently, and relaunched after 15 seconds idle. Reviewers: csilvers, vrana, btrahan Reviewed By: csilvers CC: aran Differential Revision: https://secure.phabricator.com/D2680
2012-06-26 20:00:26 +02:00
if ($this->quiet) {
return;
}
if ($client) {
$message = '[Client '.$client->getName().'] '.$message;
} else {
$message = '[Server] '.$message;
}
Add various flags to the HgProxy daemons Summary: - Add flags to exit after an idle time or client count. - Add flags to control daemonization. - Add flags to control output. - Add flags to skip the "hello" frame of the protocol. - Make the client launch a server if one does not exist. The one-time overhead to launch a server and run a command through it looks to be ~130% of the overhead to run the command directly with "hg", so even if we never run a second command we're not paying too much. The incremental overhead to run subsequent command appears to be less than 3% of the overhead to run the command directly with "hg" (and maybe less than 1%, I'm not sure how long the computation part of a command like 'hg log' "actually" takes). The overhead to launch a PHP client, connect to an existing server, run a command, and then print it and exit is roughly 50% of the overhead to run the command directly with "hg". So theoretically a user can achieve an amortized 2x performance increase for all 'hg' commands by aliasing 'hg' to the PHP client in their shell. Test Plan: - Ran servers with idle and client count limits, let them idle and/or hit their connection limits, saw them exit. - Ran foreground and background servers. - Ran a daemon server with redirected stdout/stderr. Verified logs appeared. - Ran with --quiet. - Ran clients and servers with and without --skip-hello, things work if they agree and break if they disagree. The throughput gain on this is fairly small (maybe 5%?) but it seems simple enough to keep for the moment. - Ran serverless clients and verified that servers launched the first time, were available subsequently, and relaunched after 15 seconds idle. Reviewers: csilvers, vrana, btrahan Reviewed By: csilvers CC: aran Differential Revision: https://secure.phabricator.com/D2680
2012-06-26 20:00:26 +02:00
echo $message."\n";
}
Add various flags to the HgProxy daemons Summary: - Add flags to exit after an idle time or client count. - Add flags to control daemonization. - Add flags to control output. - Add flags to skip the "hello" frame of the protocol. - Make the client launch a server if one does not exist. The one-time overhead to launch a server and run a command through it looks to be ~130% of the overhead to run the command directly with "hg", so even if we never run a second command we're not paying too much. The incremental overhead to run subsequent command appears to be less than 3% of the overhead to run the command directly with "hg" (and maybe less than 1%, I'm not sure how long the computation part of a command like 'hg log' "actually" takes). The overhead to launch a PHP client, connect to an existing server, run a command, and then print it and exit is roughly 50% of the overhead to run the command directly with "hg". So theoretically a user can achieve an amortized 2x performance increase for all 'hg' commands by aliasing 'hg' to the PHP client in their shell. Test Plan: - Ran servers with idle and client count limits, let them idle and/or hit their connection limits, saw them exit. - Ran foreground and background servers. - Ran a daemon server with redirected stdout/stderr. Verified logs appeared. - Ran with --quiet. - Ran clients and servers with and without --skip-hello, things work if they agree and break if they disagree. The throughput gain on this is fairly small (maybe 5%?) but it seems simple enough to keep for the moment. - Ran serverless clients and verified that servers launched the first time, were available subsequently, and relaunched after 15 seconds idle. Reviewers: csilvers, vrana, btrahan Reviewed By: csilvers CC: aran Differential Revision: https://secure.phabricator.com/D2680
2012-06-26 20:00:26 +02:00
private function daemonize() {
// Keep stdout if it's been redirected somewhere, otherwise shut it down.
$keep_stdout = false;
$keep_stderr = false;
if (function_exists('posix_isatty')) {
if (!posix_isatty(STDOUT)) {
$keep_stdout = true;
}
if (!posix_isatty(STDERR)) {
$keep_stderr = true;
}
}
$pid = pcntl_fork();
if ($pid === -1) {
throw new Exception('Unable to fork!');
Add various flags to the HgProxy daemons Summary: - Add flags to exit after an idle time or client count. - Add flags to control daemonization. - Add flags to control output. - Add flags to skip the "hello" frame of the protocol. - Make the client launch a server if one does not exist. The one-time overhead to launch a server and run a command through it looks to be ~130% of the overhead to run the command directly with "hg", so even if we never run a second command we're not paying too much. The incremental overhead to run subsequent command appears to be less than 3% of the overhead to run the command directly with "hg" (and maybe less than 1%, I'm not sure how long the computation part of a command like 'hg log' "actually" takes). The overhead to launch a PHP client, connect to an existing server, run a command, and then print it and exit is roughly 50% of the overhead to run the command directly with "hg". So theoretically a user can achieve an amortized 2x performance increase for all 'hg' commands by aliasing 'hg' to the PHP client in their shell. Test Plan: - Ran servers with idle and client count limits, let them idle and/or hit their connection limits, saw them exit. - Ran foreground and background servers. - Ran a daemon server with redirected stdout/stderr. Verified logs appeared. - Ran with --quiet. - Ran clients and servers with and without --skip-hello, things work if they agree and break if they disagree. The throughput gain on this is fairly small (maybe 5%?) but it seems simple enough to keep for the moment. - Ran serverless clients and verified that servers launched the first time, were available subsequently, and relaunched after 15 seconds idle. Reviewers: csilvers, vrana, btrahan Reviewed By: csilvers CC: aran Differential Revision: https://secure.phabricator.com/D2680
2012-06-26 20:00:26 +02:00
} else if ($pid) {
// We're the parent; exit. First, drop our reference to the socket so
// our __destruct() doesn't tear it down; the child will tear it down
// later.
$this->socket = null;
exit(0);
}
// We're the child; continue.
fclose(STDIN);
if (!$keep_stdout) {
fclose(STDOUT);
$this->quiet = true;
}
if (!$keep_stderr) {
fclose(STDERR);
}
}
}