From 5cf9168dc2b6a2483f2d47b9afd689adf1b6b98f Mon Sep 17 00:00:00 2001 From: Tommy van der Vorst Date: Wed, 11 Feb 2026 10:28:11 +0100 Subject: [PATCH] chore(db): add ability to wait for programmatically started database maintenance, query last maintenance time (#10565) Also adds a method to query the last database maintenance time. Signed-off-by: Tommy van der Vorst Co-authored-by: Jakob Borg --- internal/db/interface.go | 7 ++++++- internal/db/sqlite/db_service.go | 25 +++++++++++++++++++------ lib/syncthing/syncthing.go | 10 ++++++++-- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/internal/db/interface.go b/internal/db/interface.go index 447f64cba..32c79cc49 100644 --- a/internal/db/interface.go +++ b/internal/db/interface.go @@ -19,7 +19,12 @@ type DBService interface { suture.Service // Starts maintenance asynchronously, if not already running - StartMaintenance() + // Returns a channel that will receive the result when the requested maintenance finishes + StartMaintenance() <-chan error + + // Returns the last time database maintenance completed + // This will return time zero when database maintenance has never completed successfully. + LastMaintenanceTime() time.Time } type DB interface { diff --git a/internal/db/sqlite/db_service.go b/internal/db/sqlite/db_service.go index 448b084cd..4dc817771 100644 --- a/internal/db/sqlite/db_service.go +++ b/internal/db/sqlite/db_service.go @@ -39,7 +39,7 @@ type Service struct { sdb *DB maintenanceInterval time.Duration internalMeta *db.Typed - start chan struct{} + start chan chan error } func (s *Service) String() string { @@ -51,15 +51,17 @@ func newService(sdb *DB, maintenanceInterval time.Duration) *Service { sdb: sdb, maintenanceInterval: maintenanceInterval, internalMeta: db.NewTyped(sdb, internalMetaPrefix), - start: make(chan struct{}), + start: make(chan chan error), } } -func (s *Service) StartMaintenance() { +func (s *Service) StartMaintenance() <-chan error { + finishChan := make(chan error, 1) select { - case s.start <- struct{}{}: + case s.start <- finishChan: default: } + return finishChan } func (s *Service) Serve(ctx context.Context) error { @@ -80,14 +82,20 @@ func (s *Service) Serve(ctx context.Context) error { } for { + var finishChan chan error select { case <-ctx.Done(): return ctx.Err() case <-timer.C: - case <-s.start: + case finishChan = <-s.start: } - if err := s.periodic(ctx); err != nil { + err := s.periodic(ctx) + if finishChan != nil { + finishChan <- err + } + + if err != nil { return wrap(err) } @@ -100,6 +108,11 @@ func (s *Service) Serve(ctx context.Context) error { } } +func (s *Service) LastMaintenanceTime() time.Time { + lastMaint, _, _ := s.internalMeta.Time(lastMaintKey) + return lastMaint +} + func (s *Service) periodic(ctx context.Context) error { t0 := time.Now() slog.DebugContext(ctx, "Periodic start") diff --git a/lib/syncthing/syncthing.go b/lib/syncthing/syncthing.go index 21b186c2f..1f3664285 100644 --- a/lib/syncthing/syncthing.go +++ b/lib/syncthing/syncthing.go @@ -116,8 +116,14 @@ func (a *App) Start() error { } // StartMaintenance asynchronously triggers database maintenance to start. -func (a *App) StartMaintenance() { - a.dbService.StartMaintenance() +func (a *App) StartMaintenance() <-chan error { + return a.dbService.StartMaintenance() +} + +// LastMaintenanceTime returns the last time database maintenance completed successfully +// This will return time zero when database maintenance has never completed successfully. +func (a *App) LastMaintenanceTime() time.Time { + return a.dbService.LastMaintenanceTime() } func (a *App) startup() error {