Skip to content

Commit 8ee7e20

Browse files
committed
Optimize memcached
1 parent 86cce31 commit 8ee7e20

File tree

2 files changed

+75
-43
lines changed

2 files changed

+75
-43
lines changed

src/Dashboards/Memcached/PHPMem.php

Lines changed: 74 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
use function explode;
1212
use function is_numeric;
1313
use function preg_match;
14+
use function preg_match_all;
1415
use function preg_replace;
16+
use function rtrim;
1517
use function str_contains;
1618
use function str_ends_with;
1719
use function str_starts_with;
@@ -27,6 +29,20 @@ class PHPMem {
2729

2830
private ?string $server_version = null;
2931

32+
private const NO_END_COMMANDS = [
33+
'me' => true, 'incr' => true, 'decr' => true, 'version' => true,
34+
'ms' => true, 'md' => true, 'ma' => true, 'cache_memlimit' => true,
35+
'mn' => true, 'quit' => true, 'mg' => true,
36+
];
37+
38+
private const END_MARKERS = [
39+
"ERROR\r\n", "CLIENT_ERROR\r\n", "SERVER_ERROR\r\n", "STORED\r\n",
40+
"NOT_STORED\r\n", "EXISTS\r\n", "NOT_FOUND\r\n", "TOUCHED\r\n",
41+
"DELETED\r\n", "OK\r\n", "END\r\n", "BUSY\r\n", "BADCLASS\r\n",
42+
"NOSPARE\r\n", "NOTFULL\r\n", "UNSAFE\r\n", "SAME\r\n", "RESET\r\n",
43+
"EN\r\n",
44+
];
45+
3046
/**
3147
* @param array<string, int|string> $server
3248
*/
@@ -177,27 +193,35 @@ public function getKeys(): array {
177193
return $lines;
178194
}
179195

180-
$slabs = $this->runCommand('stats items');
181-
$lines = explode("\n", $slabs);
182-
$slab_ids = [];
196+
$slabs_raw = $this->runCommand('stats items');
197+
$keys = [];
198+
$seen_slabs = [];
199+
$seen_keys = [];
183200

184-
foreach ($lines as $line) {
185-
if (preg_match('/STAT items:(\d+):/', $line, $matches)) {
186-
$slab_ids[] = $matches[1];
187-
}
188-
}
201+
foreach (explode("\n", $slabs_raw) as $line) {
202+
if (preg_match('/STAT items:(\d+):/', $line, $m)) {
203+
$slab_id = $m[1];
189204

190-
$keys = [];
205+
if (isset($seen_slabs[$slab_id])) {
206+
continue;
207+
}
208+
209+
$seen_slabs[$slab_id] = true;
191210

192-
foreach (array_unique($slab_ids) as $slab_id) {
193-
$dump = $this->runCommand('stats cachedump '.$slab_id.' 0');
194-
$dump_lines = explode("\n", $dump);
211+
$dump = $this->runCommand('stats cachedump '.$slab_id.' 100000');
195212

196-
foreach ($dump_lines as $line) {
197-
if (preg_match('/ITEM (\S+) \[(\d+) b; (\d+) s]/', $line, $matches)) {
198-
$exp = (int) $matches[3] === 0 ? -1 : (int) $matches[3];
199-
// Intentionally formatted as lru_crawler output
200-
$keys[] = 'key='.$matches[1].' exp='.$exp.' la=0 cas=0 fetch=no cls=1 size='.$matches[2];
213+
if (preg_match_all('/ITEM (\S+) \[(\d+) b; (\d+) s]/', $dump, $matches, PREG_SET_ORDER)) {
214+
foreach ($matches as $item) {
215+
$key_name = $item[1];
216+
217+
if (isset($seen_keys[$key_name])) {
218+
continue;
219+
}
220+
$seen_keys[$key_name] = true;
221+
222+
$exp = ((int) $item[3] === 0) ? -1 : (int) $item[3];
223+
$keys[] = 'key='.$key_name.' exp='.$exp.' la=0 cas=0 fetch=no cls=1 size='.$item[2];
224+
}
201225
}
202226
}
203227
}
@@ -212,7 +236,8 @@ public function getKeys(): array {
212236
*/
213237
public function parseLine(string $line): array {
214238
$data = [];
215-
if (preg_match_all('/(\w+)=(\S+)/', $line, $matches, PREG_SET_ORDER)) {
239+
240+
if (preg_match_all('/(key|exp|la|size)=(\S+)/', $line, $matches, PREG_SET_ORDER)) {
216241
foreach ($matches as [, $key, $val]) {
217242
switch ($key) {
218243
case 'key':
@@ -405,8 +430,27 @@ private function streamConnection(string $command, string $command_name): string
405430

406431
$buffer = '';
407432
$start = microtime(true);
433+
$select_timeout_usec = 100_000; // 100ms
408434

409435
while (microtime(true) - $start < 5) {
436+
$read = [$this->stream];
437+
$write = null;
438+
$except = null;
439+
440+
$num = @stream_select($read, $write, $except, 0, $select_timeout_usec);
441+
442+
if ($num === false) {
443+
usleep(1000);
444+
continue;
445+
}
446+
447+
if ($num === 0) {
448+
if ($this->checkCommandEnd($buffer)) {
449+
break;
450+
}
451+
continue;
452+
}
453+
410454
$chunk = fread($this->stream, 65536);
411455

412456
if ($chunk === false) {
@@ -423,10 +467,7 @@ private function streamConnection(string $command, string $command_name): string
423467
$buffer .= $chunk;
424468

425469
// Commands without a specific end string.
426-
if ($command_name === 'incr' || $command_name === 'decr' || $command_name === 'version' ||
427-
$command_name === 'me' || $command_name === 'mg' || $command_name === 'ms' ||
428-
$command_name === 'md' || $command_name === 'ma' || $command_name === 'mn' ||
429-
$command_name === 'cache_memlimit' || $command_name === 'quit') {
470+
if (isset(self::NO_END_COMMANDS[$command_name])) {
430471
break;
431472
}
432473

@@ -440,25 +481,16 @@ private function streamConnection(string $command, string $command_name): string
440481
}
441482

442483
private function checkCommandEnd(string $buffer): bool {
443-
return
444-
str_ends_with($buffer, "ERROR\r\n") ||
445-
str_ends_with($buffer, "CLIENT_ERROR\r\n") ||
446-
str_ends_with($buffer, "SERVER_ERROR\r\n") ||
447-
str_ends_with($buffer, "STORED\r\n") ||
448-
str_ends_with($buffer, "NOT_STORED\r\n") ||
449-
str_ends_with($buffer, "EXISTS\r\n") ||
450-
str_ends_with($buffer, "NOT_FOUND\r\n") ||
451-
str_ends_with($buffer, "TOUCHED\r\n") ||
452-
str_ends_with($buffer, "DELETED\r\n") ||
453-
str_ends_with($buffer, "OK\r\n") ||
454-
str_ends_with($buffer, "END\r\n") ||
455-
str_ends_with($buffer, "BUSY\r\n") ||
456-
str_ends_with($buffer, "BADCLASS\r\n") ||
457-
str_ends_with($buffer, "NOSPARE\r\n") ||
458-
str_ends_with($buffer, "NOTFULL\r\n") ||
459-
str_ends_with($buffer, "UNSAFE\r\n") ||
460-
str_ends_with($buffer, "SAME\r\n") ||
461-
str_ends_with($buffer, "RESET\r\n") ||
462-
str_ends_with($buffer, "EN\r\n");
484+
if ($buffer === '') {
485+
return false;
486+
}
487+
488+
foreach (self::END_MARKERS as $marker) {
489+
if (str_ends_with($buffer, $marker)) {
490+
return true;
491+
}
492+
}
493+
494+
return false;
463495
}
464496
}

tests/Dashboards/MemcachedTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ public static function commandDataProvider(): Iterator {
137137
yield 'test set incr' => ['6', 'incr pu-test-rc-int 5'];
138138
yield 'test set decr' => ['3', 'decr pu-test-rc-int 3'];
139139
yield 'test ms' => ['HD', 'ms pu-test-rc-ms 1\r\n4'];
140-
yield 'test mg' => ['VA 1', 'mg pu-test-rc-ms v'];
140+
yield 'test mg' => ['VA 1\r\n4', 'mg pu-test-rc-ms v'];
141141
yield 'test ma' => ['HD', 'ma pu-test-rc-ms'];
142142
yield 'test md' => ['HD', 'md pu-test-rc-ms'];
143143
yield 'test cache_memlimit' => ['OK', 'cache_memlimit 100'];

0 commit comments

Comments
 (0)