From 00ab097760b88cfbb34b26e949c98261d5625475 Mon Sep 17 00:00:00 2001 From: Mauro Cali Date: Wed, 23 Sep 2026 13:05:21 +0200 Subject: [PATCH] fix(database): gate the table open cache rule on uptime like its siblings Table_open_cache derives its value from counters that reset with the server (misses / (hits + misses)), but it was missing from COUNTER_SENSITIVE_RULE_IDS while every rule of the same shape -- Thread_cache_hit_rate, Key_buffer_hit_rate, Innodb_buffer_pool_hit_rate -- is listed there. The opens performed while the caches are cold dominate the ratio for hours after a restart, so a correctly sized cache is reported as too small until enough hits accumulate to dilute them. Observed on a 221-table instance with table_open_cache = 4000 and zero overflows: the miss counter stopped growing at 704 while the rule kept failing for hours purely because the cumulative ratio had not yet fallen below the 1 % threshold. Assisted-by: ClaudeCode:claude-opus-5 Signed-off-by: Mauro Cali --- lib/Database/Advisor.php | 1 + tests/lib/Database/AdvisorTest.php | 31 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/lib/Database/Advisor.php b/lib/Database/Advisor.php index db14d205..f905a9d5 100644 --- a/lib/Database/Advisor.php +++ b/lib/Database/Advisor.php @@ -56,6 +56,7 @@ class Advisor { 'Query_cache_efficiency', 'Key_buffer_hit_rate', 'Table_locks_waited', + 'Table_open_cache', 'Binlog_cache_disk_use', 'Slow_query_rate', 'Sort_rows', diff --git a/tests/lib/Database/AdvisorTest.php b/tests/lib/Database/AdvisorTest.php index 397aaca6..300e3a89 100644 --- a/tests/lib/Database/AdvisorTest.php +++ b/tests/lib/Database/AdvisorTest.php @@ -91,6 +91,37 @@ public function testReplicaLagFailsOnStoppedReplica(): void { $this->assertSame(RuleResult::STATUS_FAIL, $result->status); } + /** + * Table_open_cache derives from counters that reset with the server, so + * the cold-start opens dominate the ratio for hours after a restart. It + * belongs behind the same uptime gate as the other hit-rate rules. + */ + public function testTableOpenCacheSkippedOnFreshlyRestartedServer(): void { + $status = [ + 'Uptime' => 3600, + 'Table_open_cache_hits' => 3411, + 'Table_open_cache_misses' => 87, + ]; + $derived = ['Uptime_hours' => 1.0, 'Uptime_days' => 1.0 / 24.0]; + + $snapshot = new Snapshot( + Snapshot::FLAVOUR_MARIADB, + '11.8.8-MariaDB', + $status, + ['table_open_cache' => 4000], + $derived + ['value' => 0.0], + ); + + foreach ($this->advisor->run(new Mysql(), $snapshot) as $result) { + if ($result->rule->id === 'Table_open_cache') { + $this->assertSame(RuleResult::STATUS_SKIPPED, $result->status); + return; + } + } + + $this->fail("Rule 'Table_open_cache' was not evaluated"); + } + /** * A healthy replica must not be flagged. */