[rest] Do not report a missing store strategy when another config stores the items - #5778
[rest] Do not report a missing store strategy when another config stores the items#5778ML19821 wants to merge 4 commits into
Conversation
…res the items
The persistence health check reported PERSISTENCE_NO_STORE_STRATEGY for every
configuration entry whose only strategy is restoreOnStartup, judging each entry
in isolation. Persistence application is additive - PersistenceManagerImpl
streams over all matching configurations - so the documented split
configuration is correct and was still reported as a problem:
items: ["*"], strategies: ["everyChange"]
items: ["gRestoreOnStartup*"], strategies: ["restoreOnStartup"]
Those items are stored, by the first entry. The additive rule is documented
behaviour, so the check contradicted the semantics of the format it checks.
The problem is now only reported when no other entry of the same service both
carries a store strategy and provably covers the items. Coverage has to be
provable from the configuration alone, because suppressing a warning that
should fire loses data silently: an entry with any exclude selector never
counts as covering, and anything short of "selects everything" or a
structurally equal selector keeps warning. Selectors are compared structurally
since the PersistenceConfig implementations do not override equals().
Cron strategies count as store strategies, matching schedulePersistJobs();
restoreOnStartup and forecast do not.
Adds the first tests for httpGetPersistenceHealth, including the cases that
must keep warning: no coverage at all, a covering entry that carries an
exclude, coverage only by a forecast entry, and a store entry in a different
service.
Signed-off-by: Martin Littkovsky <2018turtle@proton.me>
AI-assisted-by: Claude Code
|
Two practical additions, in case they help judging the change. How the configuration that triggered this looks once worked around. For anyone finding this issue with a red health page, the warning can be avoided today by giving every entry its own store strategy instead of relying on the additive rule: I have been running exactly this since yesterday and can confirm it behaves: same restore scope, no item left unstored, and a reboot restored all 61 group members (verified item by item, including ones carrying a hand-set setting with no live source, which are the only ones where a broken restore would be silent). It is arguably the tidier configuration, so it is worth asking whether the check is nudging users somewhere good. I still think the report is wrong as it stands, for two reasons: it makes a factual claim — these items have no store strategy — that is false for the additive form, and the additive form is what the documentation describes. A nudge would be worded differently from a problem. A documentation gap this exposed, which I am happy to fix separately. Getting from "store everything, restore only a group" to a correct configuration is currently a two-step trap. A short example for that case would probably save others the round trip. I would rather wait for the outcome here before writing it, since the shape worth recommending depends on whether this fix lands — no point documenting a workaround for something about to be fixed. Happy to open a docs PR either way if you think it is worth having; there is also a duplicated word in that same paragraph ("all the strategies strategies listed") that I would fix in passing. (Edited: the first version of this comment lost three inline code spans to a shell quoting mistake on my side.) |
wborn
left a comment
There was a problem hiding this comment.
AI review performed before manual maintainer review.
The change correctly addresses the false positive caused by additive persistence configurations, and the added health-check coverage is useful.
There are two correctness issues in the new coverage logic that should be addressed before merging:
hasStoreStrategy()currently treats any strategy other thanrestoreOnStartupandforecastas storing data. Generic named strategies can exist without actually being executed byPersistenceManagerImpl, so such a strategy could incorrectly suppress the warning.- Coverage can itself be additive across multiple store configurations. The current implementation requires a single other configuration to cover all selectors, so a restore configuration such as
ItemA, ItemBis still reported whenItemAandItemBare provably stored by two separate entries.
There is also a minor maintainability concern with isStructurallyEqual() treating every otherwise unknown PersistenceConfig subtype as structurally equal.
The CI checks are green.
…ditive coverage Three corrections to the coverage logic: * hasStoreStrategy() treated everything that is neither restoreOnStartup nor forecast as storing. A file-based configuration may name arbitrary strategies, which PersistenceModelManager turns into plain PersistenceStrategy instances that PersistenceManagerImpl never acts on - it stores for everyUpdate, everyChange and cron strategies only. Such a strategy could therefore silence a warning that should fire, which is exactly the direction this check must not fail in. The storing strategies are now named explicitly. * Coverage is additive as well. A restore entry selecting ItemA and ItemB is provably covered when one entry stores ItemA and another stores ItemB, but the previous implementation required a single entry to cover both. Each selector is now looked up across all eligible store entries. * isStructurallyEqual() no longer treats unknown PersistenceConfig subtypes as equal just because their classes match: a future subtype with distinguishing state would silently widen coverage. PersistenceAllConfig, the one type where the class alone decides, is handled before that point. An entry that selects nothing positively (only excludes) is no longer treated as vacuously covered either. Three tests added: an arbitrary named strategy does not suppress the report, coverage split across two store entries does suppress it, and partial coverage of a two-item entry still reports. Signed-off-by: Martin Littkovsky <2018turtle@proton.me> AI-assisted-by: Claude Code
wborn
left a comment
There was a problem hiding this comment.
AI found no further issues in the current changes. The previous review findings have been addressed.
The current CI workflow for this revision still needs to run before merge. A human maintainer should still review the PR before merge.
As a minor housekeeping item, the PR description should be updated to reflect the revised store-strategy detection and the additional tests added in d8f431a.
AI found no further blocking issues in the current changes. A human maintainer review is still needed before merge.
…tion alone Two corrections to isStoredByAnotherConfig(), both closing gaps where the coverage proof was weaker than the configuration warrants: * A store entry carrying filters no longer counts as coverage. At runtime, PersistenceManagerImpl.storeItem() drops every write for which a filter returns false, and a filter such as PersistenceEqualsFilter can veto every single write (e.g. when the item never takes one of the configured values). Whether it does cannot be decided from the configuration, so a filtered entry cannot prove that a restore-only entry ever gets data to restore. Trade-off: restore-only entries whose items are stored only by an entry with a harmless filter are warned about again. That is the status quo before this PR, not a regression - the warning errs on the side of firing, which is the direction this check must fail in. * The all-items shortcut no longer precedes the empty-selectors guard. An entry that selects nothing positively (only excludes) covers no items and must be reported, but the early return for a '*' store entry came first and suppressed the warning for such entries whenever an all-items store entry existed - contradicting the intent of the previous commit. The guard now runs before the shortcut. Two tests added, one per correction: coverage only by a filtered entry is still reported, and an exclude-only restore entry is still reported despite an all-items store entry. Both fail against the previous commit. Signed-off-by: Martin Littkovsky <2018turtle@proton.me> AI-assisted-by: Claude Code
|
While re-reviewing the coverage logic against Filtered entries counted as coverage. The store-entry candidates were filtered on strategy and exclude-freedom, but not on Order of the all-items shortcut and the exclude-only guard. The early return for a One test per fix, and both fail against the previous head (d8f431a) without the fix, so they actually detect the two behaviours rather than merely passing alongside them. Full module test run is green (48 tests, 26 in |
That should be covered at the source, when parsing the DSL. I had created a PR #5775 for that. |
|
Did you look into adding extra public methods to PersistenceManager? There are already private methods to get all items for a strategy and to check if a configuration applies to an item. That may enable to do a real and precise check, at the cost of iterating all items with |
I am not convinced this should be covered in the checks. After all, if an item on has a everyChange strategy and never changes, it may not end in persistence either. And nothing to restore may be the right answer then. |
|
Thanks — both points taken. You are right that the check is and stays a heuristic on intent: an On your suggestion: exposing the manager's own applies-to logic would make the coverage computation precise, at the cost of iterating the items. Following that thought one step further: for Happy to implement whichever direction you prefer — the manager-based precise coverage, the empirical stored-value check, or keeping the current conservative config-only shape — in this PR or as a follow-up. |
I understand that. Still, filters serve a different purpose then defining which item will be persisted. Its purpose is to filter what changes for an item state gets persisted. I don’t think we should overload the rules for the check as it will make it harder to understand why a warning is there in the first place.
My fear would be this is taken is too far from the potential configuration issue. It gets harder to find the consequence. After all, something might be wrong with the database itself as well. I think we should remain focussed on the item and strategy configuration side. Someone that manually persists (or even puts some values directly in the DB) would understand the warning may not apply to him. |
…n review Filters describe which state changes get persisted, not whether an item is covered - overloading the coverage rules with them makes the warning harder to understand. The check stays focused on items and strategies; the guard-order fix from the previous commit is unaffected. AI-assisted-by: Claude Code Signed-off-by: Martin Littkovsky <2018turtle@proton.me>
|
Done — the filter clause and its test are dropped ( And thanks for the |
Description
Fixes #5777.
httpGetPersistenceHealth()reportsPERSISTENCE_SERVICE_ITEMS_NO_STORE_STRATEGYfor every configuration entry whose only strategy isrestoreOnStartup, judging each entry in isolation. Persistence application is additive —PersistenceManagerImpl.storeItem()streams over all matching configurations — so the split configuration below is correct and still permanently red:The additive rule is documented behaviour, not an implementation detail:
configuration/persistence.mdsays "The entries are additive. This means if one Item appears in more than one<itemlist>… all the strategies … listed on all those lines apply to that Item." The check therefore contradicts the documented semantics of the format it checks, and states something factually wrong about the user's configuration.The problem is now reported only when no other entry of the same service both carries a store strategy and provably covers the same items.
Conservative on purpose. Suppressing a warning that ought to fire means silently losing data, which is far worse than one warning too many, so coverage has to be provable from the configuration alone:
everyUpdate,everyChangeand cron strategies count, because those are the onesPersistenceManagerImplactually acts on (schedulePersistJobs()turns cron strategies into ordinary persist jobs). A file-based configuration may name an arbitrary strategy, whichPersistenceModelManagerturns into a plainPersistenceStrategyinstance that nothing ever executes — treating "neither restore nor forecast" as storing would let such a strategy silence a warning that should fire.ItemA, ItemBis therefore covered by two separate entries that storeItemAandItemBrespectively — but it keeps warning if only one of the two is covered.appliesToItem()drops the whole entry for the excluded items, so nothing about it is provable. A*entry with!Itemor!Group*is not evidence of coverage.equals(), because thePersistenceConfigimplementations do not override it. Comparison is by concrete type plus identifying field, and any implementation this method does not know is treated as not equal — calling two unknown selectors equal would silence a warning.PersistenceAllConfig, the one type where the class alone is decisive, is handled by the caller before that point.Testing
PersistenceResourceTesthad no coverage ofhttpGetPersistenceHealthat all; these are the first tests for it. Ten cases, deliberately weighted towards the ones that must keep warning:*+ everyChange, restore-only group)*store entry carrying an exclude selectorforecasteveryUpdate)Bundle build green, static analysis clean.
The cases that require the new behaviour (standard split, cron coverage, exact-selector coverage, split coverage) were verified to fail against the unmodified code — the ones asserting that the warning is still raised pass under both versions by construction, which is the point of including them.
Verified on a live openHAB 5.2.1 installation as well: the reported items carried several thousand datapoints per 48 hours in InfluxDB while the health page insisted they had no store strategy.
Note for completeness: a configuration can be rewritten so that every entry carries its own store strategy (
["*", "!gRestoreOnStartup*"]plus["gRestoreOnStartup*"]with both store and restore strategies), and that is arguably tidier. It does not make the report correct, though — the documented additive form stays valid, and users following the documentation should not be told their configuration is broken.Signed-off-by (in commit): Martin Littkovsky 2018turtle@proton.me