From 1ec41f04573b8bd46c8f7d83fb3d3a84b1934c7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Meneghini=20Fauth?= Date: Sun, 27 Jul 2025 13:46:26 -0300 Subject: [PATCH 1/4] Refactor unit tests for Engines\Innodb::getPageBufferpool() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maurício Meneghini Fauth --- src/Engines/Innodb.php | 28 ++--- tests/unit/Engines/InnodbTest.php | 198 ++++++++++++++++++++---------- tests/unit/Stubs/DbiDummy.php | 21 ---- 3 files changed, 147 insertions(+), 100 deletions(-) diff --git a/src/Engines/Innodb.php b/src/Engines/Innodb.php index 7b1a538196..46675a633b 100644 --- a/src/Engines/Innodb.php +++ b/src/Engines/Innodb.php @@ -154,30 +154,30 @@ class Innodb extends StorageEngine . ' ' . "\n" . ' ' . __('Pages containing data') . '' . "\n" . ' ' - . Util::formatNumber($status['Innodb_buffer_pool_pages_data'], 0) . "\n" + . Util::formatNumber($status['Innodb_buffer_pool_pages_data'], 0) . '' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . __('Pages to be flushed') . '' . "\n" . ' ' - . Util::formatNumber($status['Innodb_buffer_pool_pages_flushed'], 0) . "\n" + . Util::formatNumber($status['Innodb_buffer_pool_pages_flushed'], 0) . '' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . __('Busy pages') . '' . "\n" . ' ' - . Util::formatNumber($status['Innodb_buffer_pool_pages_misc'], 0) . "\n" + . Util::formatNumber($status['Innodb_buffer_pool_pages_misc'], 0) . '' . "\n" - . ' '; + . ' ' . "\n"; // not present at least since MySQL 5.1.40 if (isset($status['Innodb_buffer_pool_pages_latched'])) { - $output .= ' ' - . ' ' . __('Latched pages') . '' + $output .= ' ' . "\n" + . ' ' . __('Latched pages') . '' . "\n" . ' ' . Util::formatNumber($status['Innodb_buffer_pool_pages_latched'], 0) - . '' - . ' '; + . '' . "\n" + . ' ' . "\n"; } $output .= ' ' . "\n" @@ -190,25 +190,25 @@ class Innodb extends StorageEngine . ' ' . "\n" . ' ' . __('Read requests') . '' . "\n" . ' ' - . Util::formatNumber($status['Innodb_buffer_pool_read_requests'], 0) . "\n" + . Util::formatNumber($status['Innodb_buffer_pool_read_requests'], 0) . '' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . __('Write requests') . '' . "\n" . ' ' - . Util::formatNumber($status['Innodb_buffer_pool_write_requests'], 0) . "\n" + . Util::formatNumber($status['Innodb_buffer_pool_write_requests'], 0) . '' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . __('Read misses') . '' . "\n" . ' ' - . Util::formatNumber($status['Innodb_buffer_pool_reads'], 0) . "\n" + . Util::formatNumber($status['Innodb_buffer_pool_reads'], 0) . '' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . __('Write waits') . '' . "\n" . ' ' - . Util::formatNumber($status['Innodb_buffer_pool_wait_free'], 0) . "\n" + . Util::formatNumber($status['Innodb_buffer_pool_wait_free'], 0) . '' . "\n" . ' ' . "\n" . ' ' . "\n" @@ -223,7 +223,7 @@ class Innodb extends StorageEngine 3, 2, ), - ) . ' %') . "\n" + ) . ' %') . '' . "\n" . ' ' . "\n" . ' ' . "\n" @@ -238,7 +238,7 @@ class Innodb extends StorageEngine 3, 2, ), - ) . ' %') . "\n" + ) . ' %') . '' . "\n" . ' ' . "\n" . ' ' . "\n" diff --git a/tests/unit/Engines/InnodbTest.php b/tests/unit/Engines/InnodbTest.php index a7a6b927a9..af82d0665b 100644 --- a/tests/unit/Engines/InnodbTest.php +++ b/tests/unit/Engines/InnodbTest.php @@ -8,6 +8,7 @@ use PhpMyAdmin\Dbal\DatabaseInterface; use PhpMyAdmin\Engines\Innodb; use PhpMyAdmin\Tests\AbstractTestCase; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; use function __; @@ -126,86 +127,153 @@ class InnodbTest extends AbstractTestCase } /** - * Test for getPageBufferpool + * @param list $variables + * @param list $usageTableRows + * @param list $activityTableRows */ - public function testGetPageBufferpool(): void - { - self::assertSame( - '' . "\n" . + #[DataProvider('pageBufferPoolProvider')] + public function testGetPageBufferPool( + array $variables, + string $totalPages, + string $totalBytes, + array $usageTableRows, + array $activityTableRows, + ): void { + $dbiDummy = $this->createDbiDummy(); + $dbiDummy->addResult( + 'SHOW STATUS WHERE Variable_name LIKE \'Innodb\\_buffer\\_pool\\_%\'' + . ' OR Variable_name = \'Innodb_page_size\';', + $variables, + ); + DatabaseInterface::$instance = $this->createDatabaseInterface($dbiDummy); + + $pageBufferPool = (new Innodb('innodb'))->getPageBufferpool(); + $dbiDummy->assertAllQueriesConsumed(); + + $expected = '
' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . + ' ' . "\n"; + + foreach ($usageTableRows as $tableRow) { + $expected .= ' ' . "\n" . + ' ' . "\n" . + ' ' . "\n" . + ' ' . "\n"; + } + + $expected .= ' ' . "\n" . '
' . "\n" . ' Buffer Pool Usage' . "\n" . '
' . "\n" . - ' Total: 4,096 pages / 65,536 KiB' . "\n" . + ' Total: ' . $totalPages . ' pages / ' . $totalBytes . "\n" . '
Free pages0
Dirty pages0
Pages containing data0' . "\n" . - '
Pages to be flushed0' . "\n" . - '
Busy pages0' . "\n" . - '
' . $tableRow[0] . '' . $tableRow[1] . '
' . "\n\n" . '' . "\n" . ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - '
' . "\n" . ' Buffer Pool Activity' . "\n" . '
Read requests64' . "\n" . - '
Write requests64' . "\n" . - '
Read misses32' . "\n" . - '
Write waits0' . "\n" . - '
Read misses in %50 %' . "\n" . - '
Write waits in %0 %' . "\n" . - '
' . "\n", - $this->object->getPageBufferpool(), - ); + ' ' . "\n"; + + foreach ($activityTableRows as $tableRow) { + $expected .= ' ' . "\n" . + ' ' . $tableRow[0] . '' . "\n" . + ' ' . $tableRow[1] . '' . "\n" . + ' ' . "\n"; + } + + $expected .= ' ' . "\n" . '' . "\n"; + + self::assertSame($expected, $pageBufferPool); + } + + /** + * @return iterable, + * string, + * string, + * list, + * list + * }> + */ + public static function pageBufferPoolProvider(): iterable + { + yield [ + [ + ['Innodb_buffer_pool_pages_data', '0'], + ['Innodb_buffer_pool_pages_dirty', '0'], + ['Innodb_buffer_pool_pages_flushed', '0'], + ['Innodb_buffer_pool_pages_free', '0'], + ['Innodb_buffer_pool_pages_misc', '0'], + ['Innodb_buffer_pool_pages_total', '4096'], + ['Innodb_buffer_pool_read_ahead_rnd', '0'], + ['Innodb_buffer_pool_read_ahead', '0'], + ['Innodb_buffer_pool_read_ahead_evicted', '0'], + ['Innodb_buffer_pool_read_requests', '64'], + ['Innodb_buffer_pool_reads', '32'], + ['Innodb_buffer_pool_wait_free', '0'], + ['Innodb_buffer_pool_write_requests', '64'], + ['Innodb_page_size', '16384'], + ], + '4,096', + '65,536 KiB', + [ + ['Free pages', '0'], + ['Dirty pages', '0'], + ['Pages containing data', '0'], + ['Pages to be flushed', '0'], + ['Busy pages', '0'], + ], + [ + ['Read requests', '64'], + ['Write requests', '64'], + ['Read misses', '32'], + ['Write waits', '0'], + ['Read misses in %', '50 %'], + ['Write waits in %', '0 %'], + ], + ]; + + yield [ + [ + ['Innodb_buffer_pool_pages_data', '0'], + ['Innodb_buffer_pool_pages_dirty', '0'], + ['Innodb_buffer_pool_pages_flushed', '0'], + ['Innodb_buffer_pool_pages_free', '0'], + ['Innodb_buffer_pool_pages_latched', '0'], + ['Innodb_buffer_pool_pages_misc', '0'], + ['Innodb_buffer_pool_pages_total', '4096'], + ['Innodb_buffer_pool_read_ahead_rnd', '0'], + ['Innodb_buffer_pool_read_ahead', '0'], + ['Innodb_buffer_pool_read_ahead_evicted', '0'], + ['Innodb_buffer_pool_read_requests', '0'], + ['Innodb_buffer_pool_reads', '32'], + ['Innodb_buffer_pool_wait_free', '0'], + ['Innodb_buffer_pool_write_requests', '0'], + ['Innodb_page_size', '16384'], + ], + '4,096', + '65,536 KiB', + [ + ['Free pages', '0'], + ['Dirty pages', '0'], + ['Pages containing data', '0'], + ['Pages to be flushed', '0'], + ['Busy pages', '0'], + ['Latched pages', '0'], + ], + [ + ['Read requests', '0'], + ['Write requests', '0'], + ['Read misses', '32'], + ['Write waits', '0'], + ['Read misses in %', '---'], + ['Write waits in %', '---'], + ], + ]; } /** diff --git a/tests/unit/Stubs/DbiDummy.php b/tests/unit/Stubs/DbiDummy.php index 15ff21fc0a..9bf85f8821 100644 --- a/tests/unit/Stubs/DbiDummy.php +++ b/tests/unit/Stubs/DbiDummy.php @@ -400,27 +400,6 @@ class DbiDummy implements DbiExtension ['Pbxt', 'NO', 'Pbxt storage engine'], ], ], - [ - 'query' => 'SHOW STATUS WHERE Variable_name' - . ' LIKE \'Innodb\\_buffer\\_pool\\_%\'' - . ' OR Variable_name = \'Innodb_page_size\';', - 'result' => [ - ['Innodb_buffer_pool_pages_data', 0], - ['Innodb_buffer_pool_pages_dirty', 0], - ['Innodb_buffer_pool_pages_flushed', 0], - ['Innodb_buffer_pool_pages_free', 0], - ['Innodb_buffer_pool_pages_misc', 0], - ['Innodb_buffer_pool_pages_total', 4096], - ['Innodb_buffer_pool_read_ahead_rnd', 0], - ['Innodb_buffer_pool_read_ahead', 0], - ['Innodb_buffer_pool_read_ahead_evicted', 0], - ['Innodb_buffer_pool_read_requests', 64], - ['Innodb_buffer_pool_reads', 32], - ['Innodb_buffer_pool_wait_free', 0], - ['Innodb_buffer_pool_write_requests', 64], - ['Innodb_page_size', 16384], - ], - ], ['query' => 'SHOW ENGINE INNODB STATUS;', 'result' => false], ['query' => 'SELECT @@innodb_version;', 'result' => [['1.1.8']]], ['query' => 'SELECT @@disabled_storage_engines', 'result' => [['']]], From ae674da150974999971ce23eb64a02af182eb27e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Meneghini=20Fauth?= Date: Sun, 27 Jul 2025 18:25:08 -0300 Subject: [PATCH 2/4] Create Engines\Innodb\BufferPool value object MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maurício Meneghini Fauth --- phpstan-baseline.neon | 30 ----- psalm-baseline.xml | 23 ---- src/Engines/Innodb.php | 74 +++++------- src/Engines/Innodb/BufferPool.php | 69 +++++++++++ tests/unit/Engines/Innodb/BufferPoolTest.php | 116 +++++++++++++++++++ tests/unit/Engines/InnodbTest.php | 5 +- 6 files changed, 217 insertions(+), 100 deletions(-) create mode 100644 src/Engines/Innodb/BufferPool.php create mode 100644 tests/unit/Engines/Innodb/BufferPoolTest.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 3519835579..a4a0c35547 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -6030,24 +6030,6 @@ parameters: count: 1 path: src/Encoding.php - - - message: '#^Binary operation "\*" between mixed and 100 results in an error\.$#' - identifier: binaryOp.invalid - count: 2 - path: src/Engines/Innodb.php - - - - message: '#^Binary operation "\*" between mixed and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 1 - path: src/Engines/Innodb.php - - - - message: '#^Binary operation "/" between \(float\|int\) and mixed results in an error\.$#' - identifier: binaryOp.invalid - count: 2 - path: src/Engines/Innodb.php - - message: ''' #^Call to deprecated method getInstance\(\) of class PhpMyAdmin\\Dbal\\DatabaseInterface\: @@ -6057,18 +6039,6 @@ parameters: count: 5 path: src/Engines/Innodb.php - - - message: '#^Loose comparison via "\=\=" is not allowed\.$#' - identifier: equal.notAllowed - count: 2 - path: src/Engines/Innodb.php - - - - message: '#^Parameter \#1 \$value of static method PhpMyAdmin\\Util\:\:formatNumber\(\) expects float\|int\|numeric\-string, mixed given\.$#' - identifier: argument.type - count: 11 - path: src/Engines/Innodb.php - - message: '#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\.$#' identifier: foreach.nonIterable diff --git a/psalm-baseline.xml b/psalm-baseline.xml index e7c301345b..ecd6ce9a2b 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -4096,29 +4096,6 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Engines/Innodb.php b/src/Engines/Innodb.php index 46675a633b..ac9f5e3cb3 100644 --- a/src/Engines/Innodb.php +++ b/src/Engines/Innodb.php @@ -1,13 +1,11 @@ fetchResult($sql, 0, 1); + $status = $this->getBufferPoolStatus(); - /** @var string[] $bytes */ - $bytes = Util::formatByteDown($status['Innodb_buffer_pool_pages_total'] * $status['Innodb_page_size']); + $bytes = Util::formatByteDown($status->pagesTotal * $status->innodbPageSize); $output = '' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n"; - // not present at least since MySQL 5.1.40 - if (isset($status['Innodb_buffer_pool_pages_latched'])) { + if ($status->pagesLatched !== null) { $output .= ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n"; } @@ -190,55 +180,41 @@ class Innodb extends StorageEngine . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" . ' ' . "\n" @@ -313,4 +289,14 @@ class Innodb extends StorageEngine return $dbi->fetchValue("SHOW GLOBAL VARIABLES LIKE 'innodb_file_per_table';", 1) === 'ON'; } + + private function getBufferPoolStatus(): BufferPool + { + $result = DatabaseInterface::getInstance()->tryQuery( + "SHOW STATUS WHERE Variable_name LIKE 'Innodb\\_buffer\\_pool\\_%' OR Variable_name = 'Innodb_page_size';", + cacheAffectedRows: false, + ); + + return BufferPool::fromResult($result !== false ? $result->fetchAllKeyPair() : []); + } } diff --git a/src/Engines/Innodb/BufferPool.php b/src/Engines/Innodb/BufferPool.php new file mode 100644 index 0000000000..2968f52629 --- /dev/null +++ b/src/Engines/Innodb/BufferPool.php @@ -0,0 +1,69 @@ + $result */ + public static function fromResult(array $result): self + { + return new self( + self::getNumeric($result['Innodb_page_size'] ?? null) ?? '0', + self::getNumeric($result['Innodb_buffer_pool_pages_data'] ?? null) ?? '0', + self::getNumeric($result['Innodb_buffer_pool_pages_dirty'] ?? null) ?? '0', + self::getNumeric($result['Innodb_buffer_pool_pages_flushed'] ?? null) ?? '0', + self::getNumeric($result['Innodb_buffer_pool_pages_free'] ?? null) ?? '0', + self::getNumeric($result['Innodb_buffer_pool_pages_misc'] ?? null) ?? '0', + self::getNumeric($result['Innodb_buffer_pool_pages_total'] ?? null) ?? '0', + self::getNumeric($result['Innodb_buffer_pool_read_requests'] ?? null) ?? '0', + self::getNumeric($result['Innodb_buffer_pool_reads'] ?? null) ?? '0', + self::getNumeric($result['Innodb_buffer_pool_wait_free'] ?? null) ?? '0', + self::getNumeric($result['Innodb_buffer_pool_write_requests'] ?? null) ?? '0', + self::getNumeric($result['Innodb_buffer_pool_pages_latched'] ?? null), + ); + } + + /** @return numeric-string|null */ + private static function getNumeric(string|null $value): string|null + { + return is_numeric($value) ? $value : null; + } +} diff --git a/tests/unit/Engines/Innodb/BufferPoolTest.php b/tests/unit/Engines/Innodb/BufferPoolTest.php new file mode 100644 index 0000000000..6a3f9388eb --- /dev/null +++ b/tests/unit/Engines/Innodb/BufferPoolTest.php @@ -0,0 +1,116 @@ + $result + * @param numeric-string $innodbPageSize + * @param numeric-string $pagesData + * @param numeric-string $pagesDirty + * @param numeric-string $pagesFlushed + * @param numeric-string $pagesFree + * @param numeric-string $pagesMisc + * @param numeric-string $pagesTotal + * @param numeric-string $readRequests + * @param numeric-string $reads + * @param numeric-string $waitFree + * @param numeric-string $writeRequests + * @param numeric-string|null $pagesLatched + */ + #[DataProvider('resultProvider')] + public function testCreateFromResult( + array $result, + string $innodbPageSize, + string $pagesData, + string $pagesDirty, + string $pagesFlushed, + string $pagesFree, + string $pagesMisc, + string $pagesTotal, + string $readRequests, + string $reads, + string $waitFree, + string $writeRequests, + string|null $pagesLatched, + ): void { + $bufferPool = BufferPool::fromResult($result); + self::assertSame($innodbPageSize, $bufferPool->innodbPageSize); + self::assertSame($pagesData, $bufferPool->pagesData); + self::assertSame($pagesDirty, $bufferPool->pagesDirty); + self::assertSame($pagesFlushed, $bufferPool->pagesFlushed); + self::assertSame($pagesFree, $bufferPool->pagesFree); + self::assertSame($pagesMisc, $bufferPool->pagesMisc); + self::assertSame($pagesTotal, $bufferPool->pagesTotal); + self::assertSame($readRequests, $bufferPool->readRequests); + self::assertSame($reads, $bufferPool->reads); + self::assertSame($waitFree, $bufferPool->waitFree); + self::assertSame($writeRequests, $bufferPool->writeRequests); + self::assertSame($pagesLatched, $bufferPool->pagesLatched); + } + + /** + * @return iterable, + * numeric-string, + * numeric-string, + * numeric-string, + * numeric-string, + * numeric-string, + * numeric-string, + * numeric-string, + * numeric-string, + * numeric-string, + * numeric-string, + * numeric-string, + * numeric-string|null + * }> + */ + public static function resultProvider(): iterable + { + yield [[], '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', null]; + + $result = [ + 'Innodb_page_size' => '1', + 'Innodb_buffer_pool_pages_data' => '2', + 'Innodb_buffer_pool_pages_dirty' => '3', + 'Innodb_buffer_pool_pages_flushed' => '4', + 'Innodb_buffer_pool_pages_free' => '5', + 'Innodb_buffer_pool_pages_misc' => '6', + 'Innodb_buffer_pool_pages_total' => '7', + 'Innodb_buffer_pool_read_requests' => '8', + 'Innodb_buffer_pool_reads' => '9', + 'Innodb_buffer_pool_wait_free' => '10', + 'Innodb_buffer_pool_write_requests' => '11', + 'Innodb_buffer_pool_pages_latched' => '12', + ]; + + yield [$result, '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12']; + + $result = [ + 'Innodb_page_size' => null, + 'Innodb_buffer_pool_pages_data' => null, + 'Innodb_buffer_pool_pages_dirty' => null, + 'Innodb_buffer_pool_pages_flushed' => null, + 'Innodb_buffer_pool_pages_free' => null, + 'Innodb_buffer_pool_pages_misc' => null, + 'Innodb_buffer_pool_pages_total' => null, + 'Innodb_buffer_pool_read_requests' => null, + 'Innodb_buffer_pool_reads' => null, + 'Innodb_buffer_pool_wait_free' => null, + 'Innodb_buffer_pool_write_requests' => null, + 'Innodb_buffer_pool_pages_latched' => null, + ]; + + yield [$result, '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', null]; + } +} diff --git a/tests/unit/Engines/InnodbTest.php b/tests/unit/Engines/InnodbTest.php index af82d0665b..888ae96ec5 100644 --- a/tests/unit/Engines/InnodbTest.php +++ b/tests/unit/Engines/InnodbTest.php @@ -141,13 +141,12 @@ class InnodbTest extends AbstractTestCase ): void { $dbiDummy = $this->createDbiDummy(); $dbiDummy->addResult( - 'SHOW STATUS WHERE Variable_name LIKE \'Innodb\\_buffer\\_pool\\_%\'' - . ' OR Variable_name = \'Innodb_page_size\';', + "SHOW STATUS WHERE Variable_name LIKE 'Innodb\\_buffer\\_pool\\_%' OR Variable_name = 'Innodb_page_size';", $variables, ); DatabaseInterface::$instance = $this->createDatabaseInterface($dbiDummy); - $pageBufferPool = (new Innodb('innodb'))->getPageBufferpool(); + $pageBufferPool = (new Innodb('innodb'))->getPageBufferPool(); $dbiDummy->assertAllQueriesConsumed(); $expected = '
' . "\n" @@ -131,7 +122,7 @@ class Innodb extends StorageEngine . '
' . "\n" . ' ' . __('Total:') . ' ' - . Util::formatNumber($status['Innodb_buffer_pool_pages_total'], 0) + . Util::formatNumber($status->pagesTotal, 0) . ' ' . __('pages') . ' / ' . implode(' ', $bytes) . "\n" @@ -142,40 +133,39 @@ class Innodb extends StorageEngine . '
' . __('Free pages') . '' - . Util::formatNumber($status['Innodb_buffer_pool_pages_free'], 0) + . Util::formatNumber($status->pagesFree, 0) . '
' . __('Dirty pages') . '' - . Util::formatNumber($status['Innodb_buffer_pool_pages_dirty'], 0) + . Util::formatNumber($status->pagesDirty, 0) . '
' . __('Pages containing data') . '' - . Util::formatNumber($status['Innodb_buffer_pool_pages_data'], 0) + . Util::formatNumber($status->pagesData, 0) . '
' . __('Pages to be flushed') . '' - . Util::formatNumber($status['Innodb_buffer_pool_pages_flushed'], 0) + . Util::formatNumber($status->pagesFlushed, 0) . '
' . __('Busy pages') . '' - . Util::formatNumber($status['Innodb_buffer_pool_pages_misc'], 0) + . Util::formatNumber($status->pagesMisc, 0) . '
' . __('Latched pages') . '' - . Util::formatNumber($status['Innodb_buffer_pool_pages_latched'], 0) + . Util::formatNumber($status->pagesLatched, 0) . '
' . __('Read requests') . '' - . Util::formatNumber($status['Innodb_buffer_pool_read_requests'], 0) + . Util::formatNumber($status->readRequests, 0) . '
' . __('Write requests') . '' - . Util::formatNumber($status['Innodb_buffer_pool_write_requests'], 0) + . Util::formatNumber($status->writeRequests, 0) . '
' . __('Read misses') . '' - . Util::formatNumber($status['Innodb_buffer_pool_reads'], 0) + . Util::formatNumber($status->reads, 0) . '
' . __('Write waits') . '' - . Util::formatNumber($status['Innodb_buffer_pool_wait_free'], 0) + . Util::formatNumber($status->waitFree, 0) . '
' . __('Read misses in %') . '' - . ($status['Innodb_buffer_pool_read_requests'] == 0 + . ((float) $status->readRequests === 0.0 ? '---' - : htmlspecialchars( - Util::formatNumber( - $status['Innodb_buffer_pool_reads'] * 100 - / $status['Innodb_buffer_pool_read_requests'], - 3, - 2, - ), - ) . ' %') + : htmlspecialchars(Util::formatNumber($status->reads * 100 / $status->readRequests, 3, 2)) . ' %') . '
' . __('Write waits in %') . '' - . ($status['Innodb_buffer_pool_write_requests'] == 0 + . ((float) $status->writeRequests === 0.0 ? '---' - : htmlspecialchars( - Util::formatNumber( - $status['Innodb_buffer_pool_wait_free'] * 100 - / $status['Innodb_buffer_pool_write_requests'], - 3, - 2, - ), - ) . ' %') + : htmlspecialchars(Util::formatNumber($status->waitFree * 100 / $status->writeRequests, 3, 2)) . ' %') . '
' . "\n" . From e0a54917258b99b784d242860135530f164742cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Meneghini=20Fauth?= Date: Sun, 27 Jul 2025 21:36:57 -0300 Subject: [PATCH 3/4] Extract HTML from Engine\Innodb::getPageBufferPool() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maurício Meneghini Fauth --- .../server/engines/_innodb_buffer_pool.twig | 76 ++++++++++++ src/Engines/Innodb.php | 117 +----------------- 2 files changed, 80 insertions(+), 113 deletions(-) create mode 100644 resources/templates/server/engines/_innodb_buffer_pool.twig diff --git a/resources/templates/server/engines/_innodb_buffer_pool.twig b/resources/templates/server/engines/_innodb_buffer_pool.twig new file mode 100644 index 0000000000..f03ebff21a --- /dev/null +++ b/resources/templates/server/engines/_innodb_buffer_pool.twig @@ -0,0 +1,76 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +{% if buffer_pool.pagesLatched != null %} + + + + +{% endif %} + +
+ {{ t('Buffer Pool Usage') }} +
+ {{ t('Total:') }} {{ format_number(buffer_pool.pagesTotal, 0) }} {{ t('pages') }} / {{ format_byte_down(buffer_pool.pagesTotal * buffer_pool.innodbPageSize)|join(' ')|raw }} +
{{ t('Free pages') }}{{ format_number(buffer_pool.pagesFree, 0) }}
{{ t('Dirty pages') }}{{ format_number(buffer_pool.pagesDirty, 0) }}
{{ t('Pages containing data') }}{{ format_number(buffer_pool.pagesData, 0) }}
{{ t('Pages to be flushed') }}{{ format_number(buffer_pool.pagesFlushed, 0) }}
{{ t('Busy pages') }}{{ format_number(buffer_pool.pagesMisc, 0) }}
{{ t('Latched pages') }}{{ format_number(buffer_pool.pagesLatched, 0) }}
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ {{ t('Buffer Pool Activity') }} +
{{ t('Read requests') }}{{ format_number(buffer_pool.readRequests, 0) }}
{{ t('Write requests') }}{{ format_number(buffer_pool.writeRequests, 0) }}
{{ t('Read misses') }}{{ format_number(buffer_pool.reads, 0) }}
{{ t('Write waits') }}{{ format_number(buffer_pool.waitFree, 0) }}
{{ t('Read misses in %') }} + {{- buffer_pool.readRequests != 0 ? format_number(buffer_pool.reads * 100 / buffer_pool.readRequests, 3, 2) ~ ' %' : '---' -}} +
{{ t('Write waits in %') }} + {{- buffer_pool.writeRequests != 0 ? format_number(buffer_pool.waitFree * 100 / buffer_pool.writeRequests, 3, 2) ~ ' %' : '---' -}} +
diff --git a/src/Engines/Innodb.php b/src/Engines/Innodb.php index ac9f5e3cb3..1767990d64 100644 --- a/src/Engines/Innodb.php +++ b/src/Engines/Innodb.php @@ -7,11 +7,10 @@ namespace PhpMyAdmin\Engines; use PhpMyAdmin\Dbal\DatabaseInterface; use PhpMyAdmin\Engines\Innodb\BufferPool; use PhpMyAdmin\StorageEngine; -use PhpMyAdmin\Util; +use PhpMyAdmin\Template; use function __; use function htmlspecialchars; -use function implode; /** * The InnoDB storage engine @@ -110,117 +109,9 @@ class Innodb extends StorageEngine */ public function getPageBufferPool(): string { - $status = $this->getBufferPoolStatus(); - - $bytes = Util::formatByteDown($status->pagesTotal * $status->innodbPageSize); - - $output = '' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n"; - - if ($status->pagesLatched !== null) { - $output .= ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n"; - } - - $output .= ' ' . "\n" - . '
' . "\n" - . ' ' . __('Buffer Pool Usage') . "\n" - . '
' . "\n" - . ' ' . __('Total:') . ' ' - . Util::formatNumber($status->pagesTotal, 0) - . ' ' . __('pages') - . ' / ' - . implode(' ', $bytes) . "\n" - . '
' . __('Free pages') . '' - . Util::formatNumber($status->pagesFree, 0) - . '
' . __('Dirty pages') . '' - . Util::formatNumber($status->pagesDirty, 0) - . '
' . __('Pages containing data') . '' - . Util::formatNumber($status->pagesData, 0) - . '
' . __('Pages to be flushed') . '' - . Util::formatNumber($status->pagesFlushed, 0) - . '
' . __('Busy pages') . '' - . Util::formatNumber($status->pagesMisc, 0) - . '
' . __('Latched pages') . '' - . Util::formatNumber($status->pagesLatched, 0) - . '
' . "\n\n" - . '' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . ' ' . "\n" - . '
' . "\n" - . ' ' . __('Buffer Pool Activity') . "\n" - . '
' . __('Read requests') . '' - . Util::formatNumber($status->readRequests, 0) - . '
' . __('Write requests') . '' - . Util::formatNumber($status->writeRequests, 0) - . '
' . __('Read misses') . '' - . Util::formatNumber($status->reads, 0) - . '
' . __('Write waits') . '' - . Util::formatNumber($status->waitFree, 0) - . '
' . __('Read misses in %') . '' - . ((float) $status->readRequests === 0.0 - ? '---' - : htmlspecialchars(Util::formatNumber($status->reads * 100 / $status->readRequests, 3, 2)) . ' %') - . '
' . __('Write waits in %') . '' - . ((float) $status->writeRequests === 0.0 - ? '---' - : htmlspecialchars(Util::formatNumber($status->waitFree * 100 / $status->writeRequests, 3, 2)) . ' %') - . '
' . "\n"; - - return $output; + return (new Template())->render('server/engines/_innodb_buffer_pool', [ + 'buffer_pool' => $this->getBufferPoolStatus(), + ]); } /** From 710ae6a455c68e9131a77873c482f11fd0680e83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maur=C3=ADcio=20Meneghini=20Fauth?= Date: Sun, 27 Jul 2025 22:02:54 -0300 Subject: [PATCH 4/4] Reformat server/engines/_innodb_buffer_pool.twig template MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maurício Meneghini Fauth --- .../server/engines/_innodb_buffer_pool.twig | 134 +++++++++--------- tests/unit/Engines/InnodbTest.php | 89 ++++++++---- 2 files changed, 125 insertions(+), 98 deletions(-) diff --git a/resources/templates/server/engines/_innodb_buffer_pool.twig b/resources/templates/server/engines/_innodb_buffer_pool.twig index f03ebff21a..bd903c2cfc 100644 --- a/resources/templates/server/engines/_innodb_buffer_pool.twig +++ b/resources/templates/server/engines/_innodb_buffer_pool.twig @@ -1,76 +1,72 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + {% if buffer_pool.pagesLatched != null %} - - - - + + + + {% endif %} - + + + + + +
- {{ t('Buffer Pool Usage') }} -
- {{ t('Total:') }} {{ format_number(buffer_pool.pagesTotal, 0) }} {{ t('pages') }} / {{ format_byte_down(buffer_pool.pagesTotal * buffer_pool.innodbPageSize)|join(' ')|raw }} -
{{ t('Free pages') }}{{ format_number(buffer_pool.pagesFree, 0) }}
{{ t('Dirty pages') }}{{ format_number(buffer_pool.pagesDirty, 0) }}
{{ t('Pages containing data') }}{{ format_number(buffer_pool.pagesData, 0) }}
{{ t('Pages to be flushed') }}{{ format_number(buffer_pool.pagesFlushed, 0) }}
{{ t('Busy pages') }}{{ format_number(buffer_pool.pagesMisc, 0) }}
{{ t('Buffer pool usage') }}
{{ t('Free pages') }}{{ format_number(buffer_pool.pagesFree, 0) }}
{{ t('Dirty pages') }}{{ format_number(buffer_pool.pagesDirty, 0) }}
{{ t('Pages containing data') }}{{ format_number(buffer_pool.pagesData, 0) }}
{{ t('Pages to be flushed') }}{{ format_number(buffer_pool.pagesFlushed, 0) }}
{{ t('Busy pages') }}{{ format_number(buffer_pool.pagesMisc, 0) }}
{{ t('Latched pages') }}{{ format_number(buffer_pool.pagesLatched, 0) }}
{{ t('Latched pages') }}{{ format_number(buffer_pool.pagesLatched, 0) }}
+ {{ t('Total:') }} {{ t('%s pages')|format(format_number(buffer_pool.pagesTotal, 0)) }} / {{ format_byte_down(buffer_pool.pagesTotal * buffer_pool.innodbPageSize)|join(' ') }} +
- - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + +
- {{ t('Buffer Pool Activity') }} -
{{ t('Read requests') }}{{ format_number(buffer_pool.readRequests, 0) }}
{{ t('Write requests') }}{{ format_number(buffer_pool.writeRequests, 0) }}
{{ t('Read misses') }}{{ format_number(buffer_pool.reads, 0) }}
{{ t('Write waits') }}{{ format_number(buffer_pool.waitFree, 0) }}
{{ t('Read misses in %') }} - {{- buffer_pool.readRequests != 0 ? format_number(buffer_pool.reads * 100 / buffer_pool.readRequests, 3, 2) ~ ' %' : '---' -}} -
{{ t('Write waits in %') }} - {{- buffer_pool.writeRequests != 0 ? format_number(buffer_pool.waitFree * 100 / buffer_pool.writeRequests, 3, 2) ~ ' %' : '---' -}} -
{{ t('Buffer pool activity') }}
{{ t('Read requests') }}{{ format_number(buffer_pool.readRequests, 0) }}
{{ t('Write requests') }}{{ format_number(buffer_pool.writeRequests, 0) }}
{{ t('Read misses') }}{{ format_number(buffer_pool.reads, 0) }}
{{ t('Write waits') }}{{ format_number(buffer_pool.waitFree, 0) }}
{{ t('Read misses in %') }} + {{- buffer_pool.readRequests != 0 ? format_number(buffer_pool.reads * 100 / buffer_pool.readRequests, 3, 2) ~ '%' : '---' -}} +
{{ t('Write waits in %') }} + {{- buffer_pool.writeRequests != 0 ? format_number(buffer_pool.waitFree * 100 / buffer_pool.writeRequests, 3, 2) ~ '%' : '---' -}} +
diff --git a/tests/unit/Engines/InnodbTest.php b/tests/unit/Engines/InnodbTest.php index 888ae96ec5..88a978f44a 100644 --- a/tests/unit/Engines/InnodbTest.php +++ b/tests/unit/Engines/InnodbTest.php @@ -150,41 +150,37 @@ class InnodbTest extends AbstractTestCase $dbiDummy->assertAllQueriesConsumed(); $expected = '' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n"; + ' ' . "\n" . + ' ' . "\n"; foreach ($usageTableRows as $tableRow) { - $expected .= ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n"; + $expected .= ' ' . "\n" . + ' ' . "\n" . + ' ' . "\n" . + ' ' . "\n"; } - $expected .= ' ' . "\n" . + $expected .= ' ' . "\n" . + ' ' . "\n" . + ' ' . "\n" . + ' ' . "\n" . + ' ' . "\n" . + ' ' . "\n" . '
' . "\n" . - ' Buffer Pool Usage' . "\n" . - '
' . "\n" . - ' Total: ' . $totalPages . ' pages / ' . $totalBytes . "\n" . - '
Buffer pool usage
' . $tableRow[0] . '' . $tableRow[1] . '
' . $tableRow[0] . '' . $tableRow[1] . '
' . "\n" . + ' Total: ' . $totalPages . ' pages / ' . $totalBytes . "\n" . + '
' . "\n\n" . '' . "\n" . - ' ' . "\n" . - ' ' . "\n"; + ' ' . "\n" . + ' ' . "\n"; foreach ($activityTableRows as $tableRow) { - $expected .= ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n" . - ' ' . "\n"; + $expected .= ' ' . "\n" . + ' ' . "\n" . + ' ' . "\n" . + ' ' . "\n"; } - $expected .= ' ' . "\n" . '
' . "\n" . - ' Buffer Pool Activity' . "\n" . - ' Buffer pool activity
' . $tableRow[0] . '' . $tableRow[1] . '
' . $tableRow[0] . '' . $tableRow[1] . '
' . "\n"; + $expected .= ' ' . "\n" . '' . "\n"; self::assertSame($expected, $pageBufferPool); } @@ -218,7 +214,7 @@ class InnodbTest extends AbstractTestCase ['Innodb_page_size', '16384'], ], '4,096', - '65,536 KiB', + '65,536 KiB', [ ['Free pages', '0'], ['Dirty pages', '0'], @@ -231,8 +227,8 @@ class InnodbTest extends AbstractTestCase ['Write requests', '64'], ['Read misses', '32'], ['Write waits', '0'], - ['Read misses in %', '50 %'], - ['Write waits in %', '0 %'], + ['Read misses in %', '50%'], + ['Write waits in %', '0%'], ], ]; @@ -255,7 +251,7 @@ class InnodbTest extends AbstractTestCase ['Innodb_page_size', '16384'], ], '4,096', - '65,536 KiB', + '65,536 KiB', [ ['Free pages', '0'], ['Dirty pages', '0'], @@ -273,6 +269,41 @@ class InnodbTest extends AbstractTestCase ['Write waits in %', '---'], ], ]; + + yield [ + [ + ['Innodb_buffer_pool_pages_data', '1000'], + ['Innodb_buffer_pool_pages_dirty', '2000'], + ['Innodb_buffer_pool_pages_flushed', '3000'], + ['Innodb_buffer_pool_pages_free', '4000'], + ['Innodb_buffer_pool_pages_misc', '5000'], + ['Innodb_buffer_pool_reads', '6000'], + ['Innodb_buffer_pool_read_requests', '7000'], + ['Innodb_buffer_pool_wait_free', '8000'], + ['Innodb_buffer_pool_write_requests', '9000'], + ['Innodb_buffer_pool_pages_latched', '10000'], + ['Innodb_page_size', '11000'], + ['Innodb_buffer_pool_pages_total', '12000'], + ], + '12,000', + '129 k KiB', + [ + ['Free pages', '4,000'], + ['Dirty pages', '2,000'], + ['Pages containing data', '1,000'], + ['Pages to be flushed', '3,000'], + ['Busy pages', '5,000'], + ['Latched pages', '10,000'], + ], + [ + ['Read requests', '7,000'], + ['Write requests', '9,000'], + ['Read misses', '6,000'], + ['Write waits', '8,000'], + ['Read misses in %', '85.71%'], + ['Write waits in %', '88.89%'], + ], + ]; } /**