PluginProbe
404 Solution / trunk
404 Solution vtrunk
4.3.5 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 4.2.0 4.1.19 4.1.18 4.1.17 4.1.16 4.1.15 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.1.7 4.1.6 4.1.5 4.1.4 4.1.3 trunk 2.30.0 All 109 releases
404-solution / includes / feedback / RollupFreshnessProbe.php

RollupFreshnessProbe.php in 404 Solution trunk, at includes/feedback/RollupFreshnessProbe.php

81 lines 4.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) {
4 exit;
5 }
6
7 /**
8 * Freshness signals for the redirects-hits rollup, for the feedback payload's
9 * `environment_extras.view_build_state` field.
10 *
11 * The rollup (ABJ_404_Solution_LogsHitsRollupService, reached through the
12 * `logs_repository` service) is the subsystem most implicated in "the admin
13 * redirects page never loads" reports: the redirects tab reads hit counts from
14 * the rollup table, and a rollup that does not exist, is mid-rebuild, or has
15 * fallen far behind logsv2 is the difference between a page that renders and
16 * one that times out. This probe answers, in one payload field, whether that
17 * has happened on the reporting site.
18 *
19 * WHY THIS READS A SERVICE AND NOT OPTIONS. This is the live successor to the
20 * staged view-build pipeline that commit 73a55a70 (2026-06-13) deleted. The
21 * previous implementation hand-assembled the same field from five
22 * `get_option('abj404_view_build_*')` literals; that commit removed every
23 * writer of those names without touching the reads, and because get_option()
24 * returns null for a missing key and is_scalar(null) is false, every key was
25 * silently dropped and this field shipped `[]` on every payload for seven
26 * weeks -- through four consecutive support reports from a user whose reported
27 * symptom was exactly the one this field exists to diagnose. Reading the
28 * rollup service's own public accessors instead means there is no storage-key
29 * literal here to be orphaned: the service owns its keys, so a rename moves
30 * the read with the write. tests/FeedbackProbeOrphanedOptionReadsTest.php
31 * fails the build if any feedback probe reintroduces that shape.
32 *
33 * Owned by ABJ_404_Solution_FeedbackEnvironmentExtras via composition.
34 */
35 class ABJ_404_Solution_RollupFreshnessProbe {
36
37 /**
38 * Live rollup state: does the rollup table exist, does it need a rebuild
39 * right now, when did it last actually refresh or get scheduled, and how
40 * far behind is its stored watermark from logsv2's true max id?
41 *
42 * `rollup_stored_max_log_id` vs `logsv2_max_log_id` is the staleness
43 * measure the field exists for: equal means current, a large gap means the
44 * rollup has fallen behind and the redirects tab is showing stale hit
45 * counts (or timing out trying not to).
46 *
47 * Throws when the rollup service is unavailable, so the caller's
48 * recordProbe() wrapper writes a `view_build_state_error` marker. An empty
49 * array must never again be this probe's way of saying "I could not look."
50 *
51 * COST. Every accessor here is a single-row read (information_schema
52 * lookups, an indexed MAX(id), a runtime flag); none of them starts a
53 * rebuild, which matters because a probe that provoked the stall it exists
54 * to diagnose would be worse than no probe. hitsTableNeedsRebuild()
55 * re-reads three of the values below internally, so a support send issues
56 * those reads twice. That is deliberate: re-deriving the staleness rule
57 * here from the raw ids would copy LogsHitsRollupService's policy into the
58 * payload builder, and a duplicated rule that drifts is a far worse trade
59 * than three extra single-row queries on a user-initiated send.
60 *
61 * @return array<string, int|bool>
62 */
63 public function collectRollupFreshness(): array {
64 $logsRepo = function_exists('abj_service_optional') ? abj_service_optional('logs_repository') : null;
65 if (!$logsRepo instanceof ABJ_404_Solution_LogHitsLifecycleInterface
66 || !$logsRepo instanceof ABJ_404_Solution_LogHitsRebuildInterface) {
67 throw new \RuntimeException('logs_repository service unavailable for view_build_state probe');
68 }
69 $lastUpdated = $logsRepo->getLogsHitsTableLastUpdated();
70 $lastScheduled = $logsRepo->getLogsHitsTableLastScheduledAt();
71 return array(
72 'rollup_table_exists' => (bool)$logsRepo->logsHitsTableExists(),
73 'rollup_needs_rebuild' => (bool)$logsRepo->hitsTableNeedsRebuild(),
74 'rollup_last_updated_at' => is_numeric($lastUpdated) ? (int)$lastUpdated : 0,
75 'rollup_last_scheduled_at' => is_numeric($lastScheduled) ? (int)$lastScheduled : 0,
76 'rollup_stored_max_log_id' => (int)$logsRepo->getStoredMaxLogId(),
77 'logsv2_max_log_id' => (int)$logsRepo->getMaxLogId(),
78 );
79 }
80 }
81