From 3703355b56b5f4706395661f14f8be10a8d15c7e Mon Sep 17 00:00:00 2001 From: Luan Vu Date: Fri, 10 Apr 2026 07:33:37 +0700 Subject: [PATCH 1/3] fix(sqlite): reset stale 'running' cron jobs to 'interrupted' on startup (#797) PG version already resets jobs stuck in 'running' state on startup (from a crash mid-execution), but SQLite was missing this step. Desktop app crashes would leave cron jobs permanently stuck in 'running' status, never executing again. Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com> --- internal/store/sqlitestore/cron_scheduler.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/internal/store/sqlitestore/cron_scheduler.go b/internal/store/sqlitestore/cron_scheduler.go index 49539d03..3f56e16b 100644 --- a/internal/store/sqlitestore/cron_scheduler.go +++ b/internal/store/sqlitestore/cron_scheduler.go @@ -79,6 +79,15 @@ func (s *SQLiteCronStore) InvalidateCache() { // the original schedule anchor is not persisted. After the first execution cycle, // anchor-based scheduling in executeOneJob preserves spacing going forward. func (s *SQLiteCronStore) recomputeStaleJobs() { + // Reset stale 'running' status — jobs that were mid-execution when the app + // crashed will never self-recover, so mark them as interrupted on startup. + if res, err := s.db.ExecContext(s.baseCtx, + `UPDATE cron_jobs SET last_status = 'interrupted' WHERE last_status = 'running'`); err != nil { + slog.Warn("cron: failed to reset stale running jobs on startup", "error", err) + } else if n, _ := res.RowsAffected(); n > 0 { + slog.Info("cron: reset stale running jobs to interrupted", "count", n) + } + now := time.Now() rows, err := s.db.QueryContext(s.baseCtx, `SELECT id, schedule_kind, cron_expression, run_at, timezone, interval_ms From bff89b0b3cb7493eafe68dc1600209a722070bed Mon Sep 17 00:00:00 2001 From: viettranx Date: Fri, 10 Apr 2026 07:34:03 +0700 Subject: [PATCH 2/3] fix(sqlite): add missing doc comment for stale running job reset --- internal/store/sqlitestore/cron_scheduler.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/store/sqlitestore/cron_scheduler.go b/internal/store/sqlitestore/cron_scheduler.go index 3f56e16b..3b92d78d 100644 --- a/internal/store/sqlitestore/cron_scheduler.go +++ b/internal/store/sqlitestore/cron_scheduler.go @@ -70,6 +70,7 @@ func (s *SQLiteCronStore) InvalidateCache() { } // recomputeStaleJobs fixes enabled jobs on startup: +// - Resets any jobs stuck in 'running' state from a previous crash. // - Recomputes next_run_at for jobs where it is NULL (crashed mid-execution). // - Advances past-due jobs (next_run_at < now) to their next future run time, // preventing a flood of all missed jobs firing simultaneously after downtime. From f64ac39dd30fa76f48bd66f9b80301f5907a62ed Mon Sep 17 00:00:00 2001 From: viettranx Date: Fri, 10 Apr 2026 07:34:46 +0700 Subject: [PATCH 3/3] chore: fix trailing slash in plans gitignore pattern --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 142b218d..62bbd755 100644 --- a/.gitignore +++ b/.gitignore @@ -55,7 +55,7 @@ deploy-*.sh docs/technical-diaries docs/zuey -plans +plans/ release-manifest.json k8s-*/*