Fix cache refresh timer with a wrapper service#22
Merged
Conversation
PR #20 generated psi-{provider}-setup.timer pointing directly at the existing setup unit, but that never fired more than once. The setup service uses Type=oneshot + RemainAfterExit=yes so ActiveEnterTimestamp is set once and never updates, and OnUnitActiveSec on the timer is anchored to that frozen timestamp. Once the first fire happens, 'next fire' = ActiveEnterTimestamp + interval — already in the past, so systemd sets next_elapse to infinity and the timer never re-arms. Worse, even if it did fire again, systemctl start on a oneshot that is currently active (exited) is a no-op, so the cache would not update. Fix: generate a tiny wrapper psi-{provider}-refresh.service (plain oneshot, no RemainAfterExit) that does: ExecStart=/usr/bin/systemctl restart psi-{provider}-setup.service Point the timer at the wrapper. The wrapper's ActiveEnterTimestamp moves forward every run, OnUnitActiveSec re-arms correctly, and systemctl restart on the setup unit does re-run its ExecStart and repopulate the cache. Verified end-to-end on a test host with a 2 minute interval: the second and third scheduled runs both rewrote cache.enc and the timer showed NEXT/LEFT for the next cycle each time.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
psi-{provider}-refresh.servicewrapper whose only job isExecStart=/usr/bin/systemctl restart psi-{provider}-setup.service.psi-{provider}-refresh.timerat the wrapper instead of at the setup unit directly.psi-{provider}-setup.timer).Why
PR #20 generated
psi-{provider}-setup.timerpointing at the existingpsi-{provider}-setup.service. That never worked for the typical case. The setup unit usesType=oneshotwithRemainAfterExit=yesso other services can depend on "setup has successfully run" without re-triggering it on every workload restart. The side effect:ActiveEnterTimestampis set once on the first run and never updates. The timer'sOnUnitActiveSec=1hanchors against that frozen timestamp, so the "next fire" is alwaysfirst_run + 1h— usually already in the past — and systemd setsNextElapseUSecMonotonic=infinityand gives up.Even when
Persistent=truecaught up the first overdue run,systemctl starton a oneshot that is currently inactive (exited)state is a no-op, so the cache was not rewritten.Observed on the test host:
Fix
Generate two units per refreshable provider instead of one:
psi-{provider}-refresh.service— a plain oneshot, noRemainAfterExit:psi-{provider}-refresh.timer— points at the wrapper:The wrapper's
ActiveEnterTimestampmoves forward on every run (because it is notRemainAfterExit=yes) soOnUnitActiveSecre-arms correctly. Andsystemctl restarton aRemainAfterExit=yesoneshot DOES re-run itsExecStart— unlikesystemctl startwhich is a no-op for an already-active unit.Verified on test host
With
OnBootSec=2m/OnUnitActiveSec=2mfor fast iteration:The second and third scheduled runs both rewrote
cache.encand the timer re-armed correctly each time.What changes
psi/unitgen.pygenerate_provider_refresh_service(provider)generate_provider_setup_timer→ renamed togenerate_provider_refresh_timer, points at the wrapperpsi/installer.py_write_refresh_timersnow writes both the wrapper service AND the timerpsi-{provider}-setup.timertopsi-{provider}-refresh.timerTests
tests/test_unitgen.py:TestProviderRefreshService(3 tests —Type=oneshot, noRemainAfterExit, correctExecStartandAfter=) andTestProviderRefreshTimer(5 tests, including a regression test that asserts the timer does NOT target the setup unit directly)tests/test_installer.py: updatedTestWriteRefreshTimersto check both units are written, the wrapper has the rightExecStart, and the timer points at the wrapperDocs
docs/secret-cache.mdrotation section documents both units and the reasoning for the wrapper patternREADME.mdFCOS generator list mentions both unitsCLAUDE.mdCLI reference note updatedTest plan
uv run ruff check psi/ tests/— cleanuv run ruff format --check psi/ tests/— cleanuv run ty check— cleanuv run pytest -q— 312 passed (3 new wrapper tests + renamed existing tests)1hcadence