CFE-4681: timer_policy support for classes: promises#6167
Conversation
|
Thank you for submitting a PR! Maybe @craigcomstock can review this? |
1ff08f3 to
41cb477
Compare
|
@cf-bottom jenkins please |
|
Sure, I triggered a build: Jenkins: https://ci.cfengine.com/job/pr-pipeline/13940/ Packages: http://buildcache.cfengine.com/packages/testing-pr/jenkins-pr-pipeline-13940/ |
vpodzime
left a comment
There was a problem hiding this comment.
So the idea here is that the class promise would be in a file that's not loaded at all? How else would one make sure the promise is not evaluated? Classes are part of pre-eval, right? I think we should have a test case for this as well.
|
Thanks for taking a look at my 🤖 ✨ Feel free to be harsh.
So, the thing is that currently/prior to this PR there are two ways to define a persistent class.
Currently there is no way to extend the time remaining that the persistent class should be defined. It The classes promise itself won't even trigger if the promiser is a defined class (optimization for not doing work you don't need to do), so you have to wait for the class to go beyond the timer and then be undefined, so it may then not be defined for some time during next policy evaluation (e.g. maybe it was guarded behind a separate class that was the result of a promise so that it wasnt resolved during pre-eval) until the classes promise is encountered and evaluated.
Classes bodies already have the ability to either extend the timer or not. Extending the timer helps to avoid that hole where a class might not be defined for a period of time until the promise re-defines it.
Yes, but only classes in common bundles are part of pre-eval: https://docs.cfengine.com/docs/lts/reference/language-concepts/policy-evaluation/#agent-pre-evaluation-step The following steps are executed per-bundle for each file parsed, in this order: if it’s a common bundle, evaluate vars promises |
41cb477 to
4c500a8
Compare
|
On testing that the promise isn't evaluated: added You're right that classes are pre-eval, but only for common bundles; the skip itself happens in I also tightened the reset test so it actually catches a broken existing-record lookup (confirmed: it fails without the |
|
@cf-bottom jenkins please |
|
Alright, I triggered a build: Jenkins: https://ci.cfengine.com/job/pr-pipeline/13942/ Packages: http://buildcache.cfengine.com/packages/testing-pr/jenkins-pr-pipeline-13942/ |
vpodzime
left a comment
There was a problem hiding this comment.
Nice! Looks good to me except for the first commit message which is too long and confusing (at least to me) -- I'd not refer to code that was never executed due to a bug in an if condition as dead code. It can easily just say that it fixes how the key is looked up which actually enables the behavior relying on its existence in the DB and the related value.
…ntSave ValueSizeDB() was called with strlen(key), but WriteDB(), ReadDB() and DeleteDB() store and look up keys with strlen(key) + 1 (including the terminating NUL), and LMDB matches keys by exact byte length. The lookup therefore never found the stored record and always reported size 0. Passing strlen(key) + 1 makes the lookup match the key as written. This lets EvalContextHeapPersistentSave() find an existing record and act on it: preserve an already-set, unexpired timer (CONTEXT_STATE_POLICY_PRESERVE) and log "Resetting" rather than "Creating" on a RESET save. Ticket: CFE-4681 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4c500a8 to
e5ec752
Compare
|
@vpodzime Good call — reworded the first commit. Dropped the "dead code" framing and shortened it: the subject now points at the actual bug (the key length used in the lookup), and the body explains it in terms of the behavior the fix enables — finding the existing record so the preserve/reset logic can act on it — rather than describing a dead branch. Force-pushed (diff unchanged). |
|
@cf-bottom jenkins please |
|
Alright, I triggered a build: Jenkins: https://ci.cfengine.com/job/pr-pipeline/13943/ Packages: http://buildcache.cfengine.com/packages/testing-pr/jenkins-pr-pipeline-13943/ |
vpodzime
left a comment
There was a problem hiding this comment.
Still too wordy to my taste, but much better.
|
@olehermanse can I get a 🍏 ? |
olehermanse
left a comment
There was a problem hiding this comment.
I think we should error if someone tries to use timer_policy without persistence. AFAICT you have not added any error for this(?)
Add a timer_policy attribute to classes: promises, controlling whether a
persistent class timer resets on re-evaluation ("reset") or preserves the
original expiry ("absolute"). Defaults to "absolute" for backward
compatibility; planned to change to "reset" in 3.28.0 to match classes
bodies, keeping "absolute" on the 3.24 and 3.27 LTS backports.
timer_policy without persistence is now an error, since it only governs
the persistence timer.
Ticket: CFE-4681
Changelog: Title
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
e5ec752 to
53afadc
Compare
|
Thanks @olehermanse — both addressed in 53afadc:
|
|
@cf-bottom jenkins, please |
|
Sure, I triggered a build: Jenkins: https://ci.cfengine.com/job/pr-pipeline/13947/ Packages: http://buildcache.cfengine.com/packages/testing-pr/jenkins-pr-pipeline-13947/ |
Summary
timer_policyattribute toclasses:promise type, allowing users to control whether a persistent class timer resets on re-evaluation (reset) or preserves the original expiry (absolute)timer_policyforclasses:promises isabsolute(PRESERVE) for backward compatibility — historically these promises skip re-evaluation when the class is already defined, so the timer was never resetExpandDeRefPromiseskip whentimer_policy => "reset"is explicitly set withpersistence > 0, allowing the promise to reachVerifyClassPromiseso the DB timer can be updatedVerifyClassPromisefor the case whereEvalClassExpressionreturns false (class already in context from persistent DB) buttimer_policyisresetValueSizeDBkey length to include null terminator (strlen+1), matchingReadDB/WriteDBconventionsTest plan
timer_policy => "absolute"creates class with "policy preserve", second run skips the promisetimer_policy => "reset"creates class with "policy reset", second run resets the timerpersistent_timer_policy.cf: verifies absolute/preserve log outputpersistent_timer_policy_reset.cf: verifies timer reset across two agent runsTicket: CFE-4681
🤖 Generated with Claude Code