Create Engines\Innodb\BufferPool value object

Signed-off-by: Maurício Meneghini Fauth <mauricio@mfauth.net>
This commit is contained in:
Maurício Meneghini Fauth 2025-07-27 18:25:08 -03:00
parent 1ec41f0457
commit ae674da150
No known key found for this signature in database
GPG Key ID: 6A16FD38AFC89CC8
6 changed files with 217 additions and 100 deletions

View File

@ -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

View File

@ -4096,29 +4096,6 @@
<code><![CDATA[DatabaseInterface::getInstance()]]></code>
<code><![CDATA[DatabaseInterface::getInstance()]]></code>
</DeprecatedMethod>
<MixedArgument>
<code><![CDATA[$status['Innodb_buffer_pool_pages_data']]]></code>
<code><![CDATA[$status['Innodb_buffer_pool_pages_dirty']]]></code>
<code><![CDATA[$status['Innodb_buffer_pool_pages_flushed']]]></code>
<code><![CDATA[$status['Innodb_buffer_pool_pages_free']]]></code>
<code><![CDATA[$status['Innodb_buffer_pool_pages_latched']]]></code>
<code><![CDATA[$status['Innodb_buffer_pool_pages_misc']]]></code>
<code><![CDATA[$status['Innodb_buffer_pool_pages_total']]]></code>
<code><![CDATA[$status['Innodb_buffer_pool_pages_total'] * $status['Innodb_page_size']]]></code>
<code><![CDATA[$status['Innodb_buffer_pool_read_requests']]]></code>
<code><![CDATA[$status['Innodb_buffer_pool_reads']]]></code>
<code><![CDATA[$status['Innodb_buffer_pool_reads'] * 100
/ $status['Innodb_buffer_pool_read_requests']]]></code>
<code><![CDATA[$status['Innodb_buffer_pool_wait_free']]]></code>
<code><![CDATA[$status['Innodb_buffer_pool_wait_free'] * 100
/ $status['Innodb_buffer_pool_write_requests']]]></code>
<code><![CDATA[$status['Innodb_buffer_pool_write_requests']]]></code>
</MixedArgument>
<MixedOperand>
<code><![CDATA[$status['Innodb_buffer_pool_pages_total']]]></code>
<code><![CDATA[$status['Innodb_buffer_pool_reads']]]></code>
<code><![CDATA[$status['Innodb_buffer_pool_wait_free']]]></code>
</MixedOperand>
</file>
<file src="src/Engines/Pbxt.php">
<PossiblyUnusedMethod>

View File

@ -1,13 +1,11 @@
<?php
/**
* The InnoDB storage engine
*/
declare(strict_types=1);
namespace PhpMyAdmin\Engines;
use PhpMyAdmin\Dbal\DatabaseInterface;
use PhpMyAdmin\Engines\Innodb\BufferPool;
use PhpMyAdmin\StorageEngine;
use PhpMyAdmin\Util;
@ -110,18 +108,11 @@ class Innodb extends StorageEngine
*
* @return string html table with stats
*/
public function getPageBufferpool(): string
public function getPageBufferPool(): string
{
// The following query is only possible because we know
// that we are on MySQL 5 here (checked above)!
// side note: I love MySQL 5 for this. :-)
$sql = 'SHOW STATUS'
. ' WHERE Variable_name LIKE \'Innodb\\_buffer\\_pool\\_%\''
. ' OR Variable_name = \'Innodb_page_size\';';
$status = DatabaseInterface::getInstance()->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 = '<table class="table table-striped table-hover w-auto float-start caption-top">' . "\n"
. ' <caption>' . "\n"
@ -131,7 +122,7 @@ class Innodb extends StorageEngine
. ' <tr>' . "\n"
. ' <th colspan="2">' . "\n"
. ' ' . __('Total:') . ' '
. Util::formatNumber($status['Innodb_buffer_pool_pages_total'], 0)
. Util::formatNumber($status->pagesTotal, 0)
. '&nbsp;' . __('pages')
. ' / '
. implode('&nbsp;', $bytes) . "\n"
@ -142,40 +133,39 @@ class Innodb extends StorageEngine
. ' <tr>' . "\n"
. ' <th scope="row">' . __('Free pages') . '</th>' . "\n"
. ' <td class="font-monospace text-end">'
. Util::formatNumber($status['Innodb_buffer_pool_pages_free'], 0)
. Util::formatNumber($status->pagesFree, 0)
. '</td>' . "\n"
. ' </tr>' . "\n"
. ' <tr>' . "\n"
. ' <th scope="row">' . __('Dirty pages') . '</th>' . "\n"
. ' <td class="font-monospace text-end">'
. Util::formatNumber($status['Innodb_buffer_pool_pages_dirty'], 0)
. Util::formatNumber($status->pagesDirty, 0)
. '</td>' . "\n"
. ' </tr>' . "\n"
. ' <tr>' . "\n"
. ' <th scope="row">' . __('Pages containing data') . '</th>' . "\n"
. ' <td class="font-monospace text-end">'
. Util::formatNumber($status['Innodb_buffer_pool_pages_data'], 0)
. Util::formatNumber($status->pagesData, 0)
. '</td>' . "\n"
. ' </tr>' . "\n"
. ' <tr>' . "\n"
. ' <th scope="row">' . __('Pages to be flushed') . '</th>' . "\n"
. ' <td class="font-monospace text-end">'
. Util::formatNumber($status['Innodb_buffer_pool_pages_flushed'], 0)
. Util::formatNumber($status->pagesFlushed, 0)
. '</td>' . "\n"
. ' </tr>' . "\n"
. ' <tr>' . "\n"
. ' <th scope="row">' . __('Busy pages') . '</th>' . "\n"
. ' <td class="font-monospace text-end">'
. Util::formatNumber($status['Innodb_buffer_pool_pages_misc'], 0)
. Util::formatNumber($status->pagesMisc, 0)
. '</td>' . "\n"
. ' </tr>' . "\n";
// not present at least since MySQL 5.1.40
if (isset($status['Innodb_buffer_pool_pages_latched'])) {
if ($status->pagesLatched !== null) {
$output .= ' <tr>' . "\n"
. ' <th scope="row">' . __('Latched pages') . '</th>' . "\n"
. ' <td class="font-monospace text-end">'
. Util::formatNumber($status['Innodb_buffer_pool_pages_latched'], 0)
. Util::formatNumber($status->pagesLatched, 0)
. '</td>' . "\n"
. ' </tr>' . "\n";
}
@ -190,55 +180,41 @@ class Innodb extends StorageEngine
. ' <tr>' . "\n"
. ' <th scope="row">' . __('Read requests') . '</th>' . "\n"
. ' <td class="font-monospace text-end">'
. Util::formatNumber($status['Innodb_buffer_pool_read_requests'], 0)
. Util::formatNumber($status->readRequests, 0)
. '</td>' . "\n"
. ' </tr>' . "\n"
. ' <tr>' . "\n"
. ' <th scope="row">' . __('Write requests') . '</th>' . "\n"
. ' <td class="font-monospace text-end">'
. Util::formatNumber($status['Innodb_buffer_pool_write_requests'], 0)
. Util::formatNumber($status->writeRequests, 0)
. '</td>' . "\n"
. ' </tr>' . "\n"
. ' <tr>' . "\n"
. ' <th scope="row">' . __('Read misses') . '</th>' . "\n"
. ' <td class="font-monospace text-end">'
. Util::formatNumber($status['Innodb_buffer_pool_reads'], 0)
. Util::formatNumber($status->reads, 0)
. '</td>' . "\n"
. ' </tr>' . "\n"
. ' <tr>' . "\n"
. ' <th scope="row">' . __('Write waits') . '</th>' . "\n"
. ' <td class="font-monospace text-end">'
. Util::formatNumber($status['Innodb_buffer_pool_wait_free'], 0)
. Util::formatNumber($status->waitFree, 0)
. '</td>' . "\n"
. ' </tr>' . "\n"
. ' <tr>' . "\n"
. ' <th scope="row">' . __('Read misses in %') . '</th>' . "\n"
. ' <td class="font-monospace text-end">'
. ($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)) . ' %')
. '</td>' . "\n"
. ' </tr>' . "\n"
. ' <tr>' . "\n"
. ' <th scope="row">' . __('Write waits in %') . '</th>' . "\n"
. ' <td class="font-monospace text-end">'
. ($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)) . ' %')
. '</td>' . "\n"
. ' </tr>' . "\n"
. ' </tbody>' . "\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() : []);
}
}

View File

@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Engines\Innodb;
use function is_numeric;
/**
* @see https://dev.mysql.com/doc/refman/en/innodb-buffer-pool.html
* @see https://mariadb.com/docs/server/server-usage/storage-engines/innodb/innodb-buffer-pool
*/
final readonly class BufferPool
{
/**
* @param numeric-string $innodbPageSize Innodb_page_size
* @param numeric-string $pagesData Innodb_buffer_pool_pages_data
* @param numeric-string $pagesDirty Innodb_buffer_pool_pages_dirty
* @param numeric-string $pagesFlushed Innodb_buffer_pool_pages_flushed
* @param numeric-string $pagesFree Innodb_buffer_pool_pages_free
* @param numeric-string $pagesMisc Innodb_buffer_pool_pages_misc
* @param numeric-string $pagesTotal Innodb_buffer_pool_pages_total
* @param numeric-string $readRequests Innodb_buffer_pool_read_requests
* @param numeric-string $reads Innodb_buffer_pool_reads
* @param numeric-string $waitFree Innodb_buffer_pool_wait_free
* @param numeric-string $writeRequests Innodb_buffer_pool_write_requests
* @param numeric-string|null $pagesLatched Innodb_buffer_pool_pages_latched
*/
public function __construct(
public string $innodbPageSize,
public string $pagesData,
public string $pagesDirty,
public string $pagesFlushed,
public string $pagesFree,
public string $pagesMisc,
public string $pagesTotal,
public string $readRequests,
public string $reads,
public string $waitFree,
public string $writeRequests,
public string|null $pagesLatched,
) {
}
/** @param array<string|null> $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;
}
}

View File

@ -0,0 +1,116 @@
<?php
declare(strict_types=1);
namespace PhpMyAdmin\Tests\Engines\Innodb;
use PhpMyAdmin\Engines\Innodb\BufferPool;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
#[CoversClass(BufferPool::class)]
final class BufferPoolTest extends TestCase
{
/**
* @param array<string|null> $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<array-key, array{
* array<string|null>,
* 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];
}
}

View File

@ -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 = '<table class="table table-striped table-hover w-auto float-start caption-top">' . "\n" .