Просмотр исходного кода

chore(sqlite): allow periodic database maintenance to be disabled (#10441)

This change allows the periodic database maintenance to be disabled, while providing a way to programmatically start maintenance at a convenient moment.

Signed-off-by: Tommy van der Vorst <[email protected]>
Tommy van der Vorst 6 дней назад
Родитель
Сommit
5bf27a432c
4 измененных файлов с 40 добавлено и 9 удалено
  1. 1 1
      cmd/syncthing/main.go
  2. 10 1
      internal/db/interface.go
  3. 21 6
      internal/db/sqlite/db_service.go
  4. 8 1
      lib/syncthing/syncthing.go

+ 1 - 1
cmd/syncthing/main.go

@@ -155,7 +155,7 @@ type serveCmd struct {
 	AllowNewerConfig          bool          `help:"Allow loading newer than current config version" env:"STALLOWNEWERCONFIG"`
 	Audit                     bool          `help:"Write events to audit file" env:"STAUDIT"`
 	AuditFile                 string        `name:"auditfile" help:"Specify audit file (use \"-\" for stdout, \"--\" for stderr)" placeholder:"PATH" env:"STAUDITFILE"`
-	DBMaintenanceInterval     time.Duration `help:"Database maintenance interval" default:"8h" env:"STDBMAINTENANCEINTERVAL"`
+	DBMaintenanceInterval     time.Duration `help:"Database maintenance interval; set to zero to disable periodic maintenance" default:"8h" env:"STDBMAINTENANCEINTERVAL"`
 	DBDeleteRetentionInterval time.Duration `help:"Database deleted item retention interval" default:"10920h" env:"STDBDELETERETENTIONINTERVAL"`
 	GUIAddress                string        `name:"gui-address" help:"Override GUI address (e.g. \"http://192.0.2.42:8443\")" placeholder:"URL" env:"STGUIADDRESS"`
 	GUIAPIKey                 string        `name:"gui-apikey" help:"Override GUI API key" placeholder:"API-KEY" env:"STGUIAPIKEY"`

+ 10 - 1
internal/db/interface.go

@@ -15,8 +15,17 @@ import (
 	"github.com/thejerf/suture/v4"
 )
 
+type DBService interface {
+	suture.Service
+
+	// Starts maintenance asynchronously, if not already running
+	StartMaintenance()
+}
+
 type DB interface {
-	Service(maintenanceInterval time.Duration) suture.Service
+	// Create a service that performs database maintenance periodically (no
+	// more often than the requested interval)
+	Service(maintenanceInterval time.Duration) DBService
 
 	// Basics
 	Update(folder string, device protocol.DeviceID, fs []protocol.FileInfo) error

+ 21 - 6
internal/db/sqlite/db_service.go

@@ -19,7 +19,6 @@ import (
 	"github.com/syncthing/syncthing/internal/db"
 	"github.com/syncthing/syncthing/internal/slogutil"
 	"github.com/syncthing/syncthing/lib/protocol"
-	"github.com/thejerf/suture/v4"
 )
 
 const (
@@ -32,7 +31,7 @@ const (
 	gcMaxRuntime = 5 * time.Minute // max time to spend on gc, per table, per run
 )
 
-func (s *DB) Service(maintenanceInterval time.Duration) suture.Service {
+func (s *DB) Service(maintenanceInterval time.Duration) db.DBService {
 	return newService(s, maintenanceInterval)
 }
 
@@ -40,6 +39,7 @@ type Service struct {
 	sdb                 *DB
 	maintenanceInterval time.Duration
 	internalMeta        *db.Typed
+	start               chan struct{}
 }
 
 func (s *Service) String() string {
@@ -51,12 +51,19 @@ func newService(sdb *DB, maintenanceInterval time.Duration) *Service {
 		sdb:                 sdb,
 		maintenanceInterval: maintenanceInterval,
 		internalMeta:        db.NewTyped(sdb, internalMetaPrefix),
+		start:               make(chan struct{}),
+	}
+}
+
+func (s *Service) StartMaintenance() {
+	select {
+	case s.start <- struct{}{}:
+	default:
 	}
 }
 
 func (s *Service) Serve(ctx context.Context) error {
 	// Run periodic maintenance
-
 	// Figure out when we last ran maintenance and schedule accordingly. If
 	// it was never, do it now.
 	lastMaint, _, _ := s.internalMeta.Time(lastMaintKey)
@@ -66,21 +73,29 @@ func (s *Service) Serve(ctx context.Context) error {
 		wait = time.Minute
 	}
 	slog.DebugContext(ctx, "Next periodic run due", "after", wait)
-
 	timer := time.NewTimer(wait)
+
+	if s.maintenanceInterval == 0 {
+		timer.Stop()
+	}
+
 	for {
 		select {
 		case <-ctx.Done():
 			return ctx.Err()
 		case <-timer.C:
+		case <-s.start:
 		}
 
 		if err := s.periodic(ctx); err != nil {
 			return wrap(err)
 		}
 
-		timer.Reset(s.maintenanceInterval)
-		slog.DebugContext(ctx, "Next periodic run due", "after", s.maintenanceInterval)
+		if s.maintenanceInterval != 0 {
+			timer.Reset(s.maintenanceInterval)
+			slog.DebugContext(ctx, "Next periodic run due", "after", s.maintenanceInterval)
+		}
+
 		_ = s.internalMeta.PutTime(lastMaintKey, time.Now())
 	}
 }

+ 8 - 1
lib/syncthing/syncthing.go

@@ -72,6 +72,7 @@ type App struct {
 	stopOnce          sync.Once
 	mainServiceCancel context.CancelFunc
 	stopped           chan struct{}
+	dbService         db.DBService
 
 	// Access to internals for direct users of this package. Note that the interface in Internals is unstable!
 	Internals *Internals
@@ -114,10 +115,16 @@ func (a *App) Start() error {
 	return nil
 }
 
+// StartMaintenance asynchronously triggers database maintenance to start.
+func (a *App) StartMaintenance() {
+	a.dbService.StartMaintenance()
+}
+
 func (a *App) startup() error {
 	a.mainService.Add(ur.NewFailureHandler(a.cfg, a.evLogger))
 
-	a.mainService.Add(a.sdb.Service(a.opts.DBMaintenanceInterval))
+	a.dbService = a.sdb.Service(a.opts.DBMaintenanceInterval)
+	a.mainService.Add(a.dbService)
 
 	if a.opts.AuditWriter != nil {
 		a.mainService.Add(newAuditService(a.opts.AuditWriter, a.evLogger))