feat(scheduler): random run-window scheduling and deduped manual triggers

Support a random daily run window (random_window_start/end) where the
next auto-run time is derived from a stable per-job hash, avoiding
thundering-herd starts while keeping one run per day. Honors the last
auto-run on initial scheduling so restarts don't double-fire.

Manual triggers can opt into dedupe (dedupe_manual_triggers): an
advisory-locked path collapses concurrent requests onto an existing
pending/running job instead of queueing duplicates. Scheduler error
messages are localized to Chinese.

Register the media_supply_cache_sync job (dry-run aware) and add the
ops migration that seeds it.
This commit is contained in:
2026-06-02 14:50:25 +08:00
parent ba2f117265
commit 842782b3dd
7 changed files with 343 additions and 8 deletions
@@ -0,0 +1,8 @@
DELETE FROM ops.scheduler_job_triggers
WHERE job_key = 'media_supply_cache_sync';
DELETE FROM ops.scheduler_job_runs
WHERE job_key = 'media_supply_cache_sync';
DELETE FROM ops.scheduler_jobs
WHERE job_key = 'media_supply_cache_sync';
@@ -0,0 +1,27 @@
INSERT INTO ops.scheduler_jobs
(job_key, display_name, category, description, enabled, schedule_type, interval_seconds, timezone, timeout_seconds, batch_size, config)
VALUES
(
'media_supply_cache_sync',
'媒体资源缓存同步',
'media_supply',
'每日低频同步权威媒体资源缓存,并在成本价上涨超过手动售价时清理手动改价。',
true,
'interval',
86400,
'Asia/Shanghai',
3600,
1,
'{"model_id":1,"random_window_start":"01:00","random_window_end":"04:00","dedupe_manual_triggers":true}'::jsonb
)
ON CONFLICT (job_key) DO UPDATE
SET display_name = EXCLUDED.display_name,
category = EXCLUDED.category,
description = EXCLUDED.description,
schedule_type = EXCLUDED.schedule_type,
interval_seconds = EXCLUDED.interval_seconds,
timezone = EXCLUDED.timezone,
timeout_seconds = EXCLUDED.timeout_seconds,
batch_size = EXCLUDED.batch_size,
config = ops.scheduler_jobs.config || EXCLUDED.config,
updated_at = NOW();