Skip to content

CPalius CMF 2.2.19

Released: 2026-09-25 · Channel: stable · Critical: no

A busy AACP page could log Update hook could not be inspected. {exception: MaxQueriesExceededException} — non-fatal, caught and logged, but worth fixing at the root rather than leaving it to log noise.

What was actually happening

UpdateHookLedger and ReleaseChecker each read exactly one row of cp_settings — their own internal bookkeeping key — once per request, already correctly memoized. Individually, neither was doing anything wrong. But each ran its own separate SELECT against cp_settings, on top of whatever SettingsRegistry itself already reads for the page. On a page busy enough to touch several of these (a dashboard checking for pending updates, the latest release, and this ledger, alongside the registered settings the page renders), the table's distinct-query count could pass the ten-per-request guard (Law 6.1) that exists specifically to catch real N+1 loops. It isn't one here — just several well-behaved readers landing on the same table — but the guard can't tell the difference, and whichever read happened to be last is the one that gets rejected.

What's fixed

SettingsRegistry gained getRaw(): a definition-free accessor into the same batch-loaded, cache.app-cached values map its get() already serves everything else from — for internal keys that were never meant to be operator-editable settings, so they don't belong in the settings UI just to get a shared cache. UpdateHookLedger and ReleaseChecker now read through it instead of running a query of their own, folding both into a read that was already happening. Both invalidate that cache after writing, so a hook that just ran or a release that was just refreshed shows up immediately rather than waiting out the cache's TTL.

Upgrade notes

No schema changes. No manual steps.

Last updated: 13.10.2026