# CPalius CMF 2.2.19

**Released:** 2026-09-25 · **Channel:** stable · **Critical:** no

Turkish: [2.2.19-tr.md](2.2.19-tr.md)

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.
