Skip redundant scheduled automations on restart if last run is still recent
This commit is contained in:
parent
7411bd1eab
commit
729f42f1a4
1 changed files with 36 additions and 4 deletions
|
|
@ -167,7 +167,7 @@ class AutomationEngine:
|
||||||
# --- System Automations ---
|
# --- System Automations ---
|
||||||
|
|
||||||
def ensure_system_automations(self):
|
def ensure_system_automations(self):
|
||||||
"""Create system automations if they don't exist, and reset next_run to initial delays on every startup."""
|
"""Create system automations if they don't exist, and schedule next_run respecting last_run."""
|
||||||
for spec in SYSTEM_AUTOMATIONS:
|
for spec in SYSTEM_AUTOMATIONS:
|
||||||
existing = self.db.get_system_automation_by_action(spec['action_type'])
|
existing = self.db.get_system_automation_by_action(spec['action_type'])
|
||||||
if not existing:
|
if not existing:
|
||||||
|
|
@ -185,11 +185,43 @@ class AutomationEngine:
|
||||||
existing = self.db.get_system_automation_by_action(spec['action_type'])
|
existing = self.db.get_system_automation_by_action(spec['action_type'])
|
||||||
|
|
||||||
if existing:
|
if existing:
|
||||||
# Only reset next_run for timer-based triggers that have an initial delay
|
|
||||||
if spec.get('initial_delay') is not None:
|
if spec.get('initial_delay') is not None:
|
||||||
nr = _utc_after(spec['initial_delay'])
|
# Compute full interval from trigger config
|
||||||
|
full_interval = self._calc_delay_seconds(spec['trigger_config'])
|
||||||
|
initial_delay = spec['initial_delay']
|
||||||
|
|
||||||
|
# Only respect last_run for longer intervals (>= 1 hour).
|
||||||
|
# Short-interval automations (wishlist 30min, clean downloads 5min) are cheap
|
||||||
|
# and users may expect them to run shortly after startup.
|
||||||
|
min_interval_for_skip = 3600 # 1 hour
|
||||||
|
|
||||||
|
# Check if last_run exists and is recent enough to skip the startup scan
|
||||||
|
last_run_str = existing.get('last_run')
|
||||||
|
last_error = existing.get('last_error')
|
||||||
|
if full_interval >= min_interval_for_skip and last_run_str and not last_error:
|
||||||
|
try:
|
||||||
|
last_run = datetime.strptime(last_run_str, '%Y-%m-%d %H:%M:%S').replace(tzinfo=timezone.utc)
|
||||||
|
elapsed = (_utcnow() - last_run).total_seconds()
|
||||||
|
|
||||||
|
if elapsed >= 0 and elapsed < full_interval:
|
||||||
|
# Last run was recent and successful — schedule for when the interval naturally expires
|
||||||
|
remaining = full_interval - elapsed
|
||||||
|
nr = _utc_after(remaining)
|
||||||
|
self.db.update_automation(existing['id'], next_run=nr)
|
||||||
|
logger.info(f"System automation '{spec['name']}' last ran {elapsed/3600:.1f}h ago (ok), next run in {remaining/3600:.1f}h")
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
# Overdue or clock skew — run after initial delay
|
||||||
|
logger.info(f"System automation '{spec['name']}' last ran {elapsed/3600:.1f}h ago (overdue), running in {initial_delay}s")
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
pass # Malformed timestamp — fall through to initial delay
|
||||||
|
elif last_run_str and last_error:
|
||||||
|
logger.info(f"System automation '{spec['name']}' last run had error, retrying in {initial_delay}s")
|
||||||
|
|
||||||
|
# No last_run or overdue — use initial delay
|
||||||
|
nr = _utc_after(initial_delay)
|
||||||
self.db.update_automation(existing['id'], next_run=nr)
|
self.db.update_automation(existing['id'], next_run=nr)
|
||||||
logger.info(f"System automation '{spec['name']}' next_run reset to {spec['initial_delay']}s from now")
|
logger.info(f"System automation '{spec['name']}' next_run set to {initial_delay}s from now")
|
||||||
else:
|
else:
|
||||||
logger.info(f"System automation '{spec['name']}' ready (event-based)")
|
logger.info(f"System automation '{spec['name']}' ready (event-based)")
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue