From 4e07eb91f93d5a6fcfbcba5d2278c185e2b0d03f Mon Sep 17 00:00:00 2001 From: Octavian Moraru Date: Mon, 18 Sep 2017 18:21:09 -0700 Subject: [PATCH] Add Memcached support for the MediaWiki target This diff adds support for Memcached. If a target supports Memcached, oss-performance will start it on 127.0.0.1:11888. The only target supporting Memcached right now is MediaWiki. For other targets to support Memcached, they must override PerfTarget::supportsMemcached(). This diff also sets $wgUseDatabaseMessages to false in the configuration file if Memcached is disabled, as recommended in the MediaWiki manual. For MediaWiki with Memcached enabled, observing a ~6% increase in RPS, a decrease in system idle time, as well as ~7 percentage points more cycles being spent in HHVM. Command line options added: --memcached: path to memcached binary --delay-memcached-startup: number of seconds to delay Memcached startup --memcached-port: TCP port to listen on (default is 11888) --no-memcached: don't use memcached (even if target supports it) --memcached-threads: number of memcached threads to spawn (default is 4 for machines with small number of cores, 32 otherwise) --- base/MemcachedDaemon.php | 75 +++++++++++++++++++++++++++ base/PerfOptions.php | 18 ++++++- base/PerfRunner.php | 7 +++ base/PerfTarget.php | 4 ++ base/SystemChecks.php | 9 +++- targets/mediawiki/LocalSettings.php | 3 ++ targets/mediawiki/MediaWikiTarget.php | 29 +++++++++-- targets/mediawiki/Memcached.php | 8 +++ 8 files changed, 145 insertions(+), 8 deletions(-) create mode 100644 base/MemcachedDaemon.php create mode 100644 targets/mediawiki/Memcached.php diff --git a/base/MemcachedDaemon.php b/base/MemcachedDaemon.php new file mode 100644 index 0000000..f7f5526 --- /dev/null +++ b/base/MemcachedDaemon.php @@ -0,0 +1,75 @@ +options->memcached); + } + + public function start(): void { + parent::startWorker( + $this->options->daemonOutputFileName('memcached'), + $this->options->delayProcessLaunch, + $this->options->traceSubProcess, + ); + } + + public function getNumThreads(): int { + $output = []; + $ret = -1; + if ($this->options->memcachedThreads != 0) { + return $this->options->memcachedThreads; + } + + exec('nproc', $output, $ret); + if ($ret != 0) { + invariant_violation('%s', 'Execution of nproc failed'); + exit(1); + } + $numProcs = (int)($output[0]); + + // for small number of cores, use the default, which is 4; + // otherwise, we probably need more + if ($numProcs <= 8) + return 4; + + return 32; + } + + <<__Override>> + protected function getPidFilePath(): string { + return $this->options->tempDir.'/memcached.pid'; + } + + <<__Override>> + protected function getArguments(): Vector { + if ($this->options->cpuBind) { + $this->cpuRange = $this->options->helperProcessors; + } + return Vector { + '-m', + (string) $this->maxMemory, + '-l', + '127.0.0.1', + '-t', + (string) $this->getNumThreads(), + '-p', + (string) $this->options->memcachedPort, + '-P', # pid file + $this->getPidFilePath() + }; + } +} diff --git a/base/PerfOptions.php b/base/PerfOptions.php index b8c2cb3..aff1c09 100644 --- a/base/PerfOptions.php +++ b/base/PerfOptions.php @@ -38,6 +38,7 @@ final class PerfOptions { public string $siege; public string $nginx; + public string $memcached; public ?string $dbUsername; public ?string $dbPassword; @@ -87,6 +88,8 @@ final class PerfOptions { public bool $applyPatches = false; + public bool $useMemcached = true; + // // All times are given in seconds, stored in a float. // For PHP code, the usleep timer is used, so fractional seconds work fine. @@ -97,6 +100,7 @@ final class PerfOptions { // public float $delayNginxStartup; public float $delayPhpStartup; + public float $delayMemcachedStartup; public float $delayProcessLaunch; // secs to wait after start process public float $delayCheckHealth; // secs to wait before hit /check-health @@ -123,6 +127,8 @@ final class PerfOptions { public bool $notBenchmarking = false; public string $dbHost = '127.0.0.1'; //The hostname/IP of server which hosts the database. + public int $memcachedPort; //The hostname/IP of server which hosts memcached. + public int $memcachedThreads; // Number of memcached threads private array $args; private Vector $notBenchmarkingArgs = Vector {}; @@ -140,6 +146,7 @@ public function __construct(Vector $argv) { 'no-fpm', 'siege:', 'nginx:', + 'memcached:', 'wait-at-end', 'wait-after-warmup', 'no-proxygen', @@ -176,6 +183,7 @@ public function __construct(Vector $argv) { 'trace', 'delay-nginx-startup:', 'delay-php-startup:', + 'delay-memcached-startup:', 'delay-process-launch:', 'delay-check-health:', 'max-delay-unfreeze:', @@ -192,6 +200,9 @@ public function __construct(Vector $argv) { 'server-threads:', 'client-threads:', 'remote-siege:', + 'memcached-port:', + 'memcached-threads:', + 'no-memcached', // do not use memcached (even if target supports it) }; $targets = $this->getTargetDefinitions()->keys(); $def->addAll($targets); @@ -238,6 +249,9 @@ public function __construct(Vector $argv) { $this->siege = hphp_array_idx($o, 'siege', 'siege'); $this->nginx = hphp_array_idx($o, 'nginx', 'nginx'); + $this->memcached = hphp_array_idx($o, 'memcached', 'memcached'); + $this->memcachedPort = (int) hphp_array_idx($o, 'memcached-port', 11888); + $this->memcachedThreads = (int) hphp_array_idx($o, 'memcached-threads', 0); $isFacebook = array_key_exists('fbcode', $o); $fbcode = ""; @@ -276,6 +290,7 @@ public function __construct(Vector $argv) { $this->statCache = $this->getBool('stat-cache'); $this->jit = !$this->getBool('no-jit'); $this->applyPatches = $this->getBool('apply-patches'); + $this->useMemcached = !$this->getBool('no-memcached'); $fraction = $this->getFloat('cpu-fraction', 1.0); if ($fraction !== 1.0) { @@ -318,6 +333,7 @@ public function __construct(Vector $argv) { $this->phpFCGIChildren = $this->getInt('php-fcgi-children', 100); $this->delayNginxStartup = $this->getFloat('delay-nginx-startup', 0.1); $this->delayPhpStartup = $this->getFloat('delay-php-startup', 1.0); + $this->delayMemcachedStartup = $this->getFloat('delay-memcached-startup', 1.0); $this->delayProcessLaunch = $this->getFloat('delay-process-launch', 0.0); $this->delayCheckHealth = $this->getFloat('delay-check-health', 1.0); $this->maxdelayUnfreeze = $this->getFloat('max-delay-unfreeze', 60.0); @@ -344,7 +360,7 @@ public function __construct(Vector $argv) { if (array_key_exists('client-threads', $o)) { $this->clientThreads = $this->args['client-threads']; } - + if ($argTempDir === null) { $this->tempDir = tempnam('/tmp', 'hhvm-nginx'); // Currently a file - change to a dir diff --git a/base/PerfRunner.php b/base/PerfRunner.php index c037212..e02b666 100644 --- a/base/PerfRunner.php +++ b/base/PerfRunner.php @@ -79,6 +79,13 @@ private static function RunWithOptionsAndEngine( Process::sleepSeconds($options->delayNginxStartup); invariant($nginx->isRunning(), 'Failed to start nginx'); + if ($options->useMemcached && $target->supportsMemcached()) { + self::PrintProgress('Starting Memcached'); + $memcached = new MemcachedDaemon($options, $target); + Process::sleepSeconds($options->delayMemcachedStartup); + $memcached->start(); + } + self::PrintProgress('Starting PHP Engine'); $php_engine->start(); Process::sleepSeconds($options->delayPhpStartup); diff --git a/base/PerfTarget.php b/base/PerfTarget.php index 982a080..9868f03 100644 --- a/base/PerfTarget.php +++ b/base/PerfTarget.php @@ -111,4 +111,8 @@ public function __toString(): string { public function getSiegeRCPath(): ?string { return null; } + + public function supportsMemcached(): bool { + return false; + } } diff --git a/base/SystemChecks.php b/base/SystemChecks.php index 1ae811f..f23d385 100644 --- a/base/SystemChecks.php +++ b/base/SystemChecks.php @@ -3,7 +3,7 @@ class SystemChecks { public static function CheckAll(PerfOptions $options): void { self::CheckNotRoot(); - self::CheckPortAvailability(); + self::CheckPortAvailability($options); self::CheckCPUFreq(); self::CheckTCPTimeWaitReuse(); self::CheckForAuditd($options); @@ -71,13 +71,18 @@ private static function CheckCPUFreq(): void { } } - private static function CheckPortAvailability(): void { + private static function CheckPortAvailability(PerfOptions $options): void { $ports = Vector { PerfSettings::HttpPort(), PerfSettings::HttpAdminPort(), PerfSettings::BackendPort(), PerfSettings::BackendAdminPort(), }; + if ($options->useMemcached && + $options->getTarget()->supportsMemcached()) { + $ports[] = $options->memcachedPort; + } + $busy_ports = Vector {}; foreach ($ports as $port) { $result = @fsockopen('localhost', $port); diff --git a/targets/mediawiki/LocalSettings.php b/targets/mediawiki/LocalSettings.php index f2a57ef..466de82 100644 --- a/targets/mediawiki/LocalSettings.php +++ b/targets/mediawiki/LocalSettings.php @@ -153,6 +153,9 @@ require_once "$IP/extensions/TitleBlacklist/TitleBlacklist.php"; require_once "$IP/extensions/WikiEditor/WikiEditor.php"; +# This option should be false if we are not using memcached, otherwise it will incur a significant performance penalty +# source: https://www.mediawiki.org/wiki/Manual:$wgUseDatabaseMessages +$wgUseDatabaseMessages = false; # End of automatically generated settings. # Add more configuration options below. diff --git a/targets/mediawiki/MediaWikiTarget.php b/targets/mediawiki/MediaWikiTarget.php index c870942..d082e65 100644 --- a/targets/mediawiki/MediaWikiTarget.php +++ b/targets/mediawiki/MediaWikiTarget.php @@ -19,6 +19,13 @@ protected function getSanityCheckString(): string { return 'Obama'; } + private function replaceInFile(string $fileName, string $search, string $replace) { + $file = $this->getSourceRoot().'/'.$fileName; + $file_contents = file_get_contents($file); + $file_contents = str_replace($search, $replace, $file_contents); + file_put_contents($file, $file_contents); + } + public function install(): void { $src_dir = $this->options->srcDir; if ($src_dir) { @@ -41,16 +48,23 @@ public function install(): void { mkdir($cache_dir); copy(__DIR__.'/LocalSettings.php', $this->getSourceRoot().'/LocalSettings.php'); - $file = $this->getSourceRoot().'/LocalSettings.php'; - $file_contents = file_get_contents($file); - $file_contents = str_replace('__DB_HOST__', $this->options->dbHost, $file_contents ); - file_put_contents($file, $file_contents); + $this->replaceInFile('LocalSettings.php', '__DB_HOST__', $this->options->dbHost); file_put_contents( $this->getSourceRoot().'/LocalSettings.php', - '$wgCacheDirectory="'.$cache_dir.'";', + '$wgCacheDirectory="'.$cache_dir.'";'."\n", FILE_APPEND, ); + if ($this->options->useMemcached) { + copy(__DIR__.'/Memcached.php', $this->getSourceRoot().'/Memcached.php'); + $this->replaceInFile('Memcached.php', '__MEMCACHED_HOST__', '127.0.0.1'); + $this->replaceInFile('Memcached.php', '__MEMCACHED_PORT__', (string) $this->options->memcachedPort); + file_put_contents( + $this->getSourceRoot().'/LocalSettings.php', + 'require_once "'.$this->getSourceRoot().'/Memcached.php";'."\n", + FILE_APPEND, + ); + } } <<__Override>> @@ -67,4 +81,9 @@ public function postInstall(): void { public function getSourceRoot(): string { return $this->options->tempDir.'/'.self::MEDIAWIKI_VERSION; } + + <<__Override>> + public function supportsMemcached(): bool { + return true; + } } diff --git a/targets/mediawiki/Memcached.php b/targets/mediawiki/Memcached.php new file mode 100644 index 0000000..ad4a56d --- /dev/null +++ b/targets/mediawiki/Memcached.php @@ -0,0 +1,8 @@ +