feat(config): job intervals live in sysconfig, every setting says when it applies, the unread SponsorBlock default goes
An interval override was a row of its own table beside the one store every other admin knob uses; it is now the `job_interval_<job>` sysconfig key (migration 0110 copies them over and drops the table). The Scheduler page stays its one editor: the Configuration page and the setup wizard neither list nor write those keys, since a write there would skip the reschedule. Each ConfigSpec now states `applies` (required, no default). Every key is `live`, held by a scan that finds no read at import time; a `restart` key is refused until the page can say so. A second scan requires a reader for every key on the Configuration page — `sponsorblock_default` had none since it was added, so it leaves the registry, the translations and the store.
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
"""Migration 0110's data step, DB-lane: an admin's interval override moves from `scheduler_settings`
|
||||
into sysconfig and back, a row for a job that no longer exists is not carried over, and the unread
|
||||
SponsorBlock default leaves the store.
|
||||
|
||||
The schema half is checked by test_migrations.py; a value lost on the way is invisible there.
|
||||
"""
|
||||
|
||||
from alembic import command
|
||||
from alembic.config import Config
|
||||
from sqlalchemy import text
|
||||
|
||||
from app import scheduler
|
||||
from app.db import engine
|
||||
|
||||
|
||||
def _config(conn) -> dict[str, str]:
|
||||
return dict(conn.execute(text("SELECT key, value FROM system_config")).tuples().all())
|
||||
|
||||
|
||||
def test_an_interval_override_moves_into_sysconfig_and_back(db):
|
||||
cfg = Config("alembic.ini")
|
||||
command.downgrade(cfg, "0109_job_runs")
|
||||
try:
|
||||
with engine.begin() as conn:
|
||||
conn.execute(
|
||||
text(
|
||||
"INSERT INTO scheduler_settings (job_id, interval_minutes)"
|
||||
" VALUES ('rss_poll', 25), ('log_gc', 90), ('retired_job', 5)"
|
||||
)
|
||||
)
|
||||
conn.execute(
|
||||
text(
|
||||
"INSERT INTO system_config (key, value, encrypted) VALUES"
|
||||
" ('sponsorblock_default', 'true', false), ('quota_daily_budget', '5000', false)"
|
||||
)
|
||||
)
|
||||
finally:
|
||||
command.upgrade(cfg, "head")
|
||||
|
||||
with engine.connect() as conn:
|
||||
assert _config(conn) == {
|
||||
"job_interval_rss_poll": "25",
|
||||
"job_interval_log_gc": "90",
|
||||
"quota_daily_budget": "5000",
|
||||
}
|
||||
assert conn.execute(text("SELECT to_regclass('scheduler_settings')")).scalar() is None
|
||||
assert scheduler.load_intervals(db)["rss_poll"] == 25
|
||||
|
||||
command.downgrade(cfg, "0109_job_runs")
|
||||
try:
|
||||
with engine.connect() as conn:
|
||||
back = conn.execute(text("SELECT job_id, interval_minutes FROM scheduler_settings"))
|
||||
assert dict(back.tuples().all()) == {"rss_poll": 25, "log_gc": 90}
|
||||
assert _config(conn) == {"quota_daily_budget": "5000"}
|
||||
finally:
|
||||
command.upgrade(cfg, "head")
|
||||
Reference in New Issue
Block a user