diff --git a/base/MemcachedDaemon.php b/base/MemcachedDaemon.php new file mode 100644 index 0000000..c9d67f3 --- /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 897a54e..057002d 100644 --- a/base/PerfOptions.php +++ b/base/PerfOptions.php @@ -37,6 +37,7 @@ final class PerfOptions { public string $siege; public string $nginx; + public string $memcached; public ?string $dbUsername; public ?string $dbPassword; @@ -86,6 +87,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. @@ -96,6 +99,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 @@ -122,6 +126,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 {}; @@ -139,6 +145,7 @@ public function __construct(Vector $argv) { 'no-fpm', 'siege:', 'nginx:', + 'memcached:', 'wait-at-end', 'wait-after-warmup', 'no-proxygen', @@ -175,6 +182,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:', @@ -191,6 +199,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); @@ -237,6 +248,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 = ""; @@ -275,6 +289,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) { @@ -317,6 +332,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); @@ -343,7 +359,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 151d9c5..9d62c13 100644 --- a/base/PerfRunner.php +++ b/base/PerfRunner.php @@ -78,6 +78,14 @@ private static function RunWithOptionsAndEngine( Process::sleepSeconds($options->delayNginxStartup); invariant($nginx->isRunning(), 'Failed to start nginx'); + if ($options->useMemcached && $target->supportsMemcached()) { + $memcached = new MemcachedDaemon($options, $target); + self::PrintProgress('Starting Memcached ('. + $memcached->getNumThreads().' threads)'); + 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 cfbaba9..bb1e3a8 100644 --- a/base/PerfTarget.php +++ b/base/PerfTarget.php @@ -110,4 +110,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 a471b27..bcfc5a1 100644 --- a/base/SystemChecks.php +++ b/base/SystemChecks.php @@ -11,7 +11,7 @@ class SystemChecks { public static function CheckAll(PerfOptions $options): void { self::CheckNotRoot(); - self::CheckPortAvailability(); + self::CheckPortAvailability($options); self::CheckCPUFreq(); self::CheckTCPTimeWaitReuse(); self::CheckForAuditd($options); @@ -79,13 +79,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 a395bd3..0d3b0e2 100644 --- a/targets/mediawiki/LocalSettings.php +++ b/targets/mediawiki/LocalSettings.php @@ -161,6 +161,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 b93519b..39c5e47 100644 --- a/targets/mediawiki/MediaWikiTarget.php +++ b/targets/mediawiki/MediaWikiTarget.php @@ -18,6 +18,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) { @@ -40,16 +47,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>> @@ -66,4 +80,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 @@ +