package sync import ( "context" "log/slog" "time" "gorm.io/gorm" ) // Worker performs background synchronization at regular intervals type Worker struct { service *Service db *gorm.DB interval time.Duration logger *slog.Logger stopCh chan struct{} } // NewWorker creates a new background sync worker func NewWorker(service *Service, db *gorm.DB, interval time.Duration) *Worker { return &Worker{ service: service, db: db, interval: interval, logger: slog.Default(), stopCh: make(chan struct{}), } } // isOnline checks if the database connection is available func (w *Worker) isOnline() bool { sqlDB, err := w.db.DB() if err != nil { return false } return sqlDB.Ping() == nil } // Start begins the background sync loop in a goroutine func (w *Worker) Start(ctx context.Context) { w.logger.Info("starting background sync worker", "interval", w.interval) ticker := time.NewTicker(w.interval) defer ticker.Stop() // Run once immediately w.runSync() for { select { case <-ctx.Done(): w.logger.Info("background sync worker stopped by context") return case <-w.stopCh: w.logger.Info("background sync worker stopped") return case <-ticker.C: w.runSync() } } } // Stop gracefully stops the worker func (w *Worker) Stop() { w.logger.Info("stopping background sync worker") close(w.stopCh) } // runSync performs a single sync iteration func (w *Worker) runSync() { // Check if online if !w.isOnline() { w.logger.Debug("offline, skipping background sync") return } w.logger.Debug("running background sync") // Push pending changes first pushed, err := w.service.PushPendingChanges() if err != nil { w.logger.Warn("failed to push pending changes", "error", err) } else if pushed > 0 { w.logger.Info("pushed pending changes", "count", pushed) } // Then check for new pricelists err = w.service.SyncPricelistsIfNeeded() if err != nil { w.logger.Warn("failed to sync pricelists", "error", err) } w.logger.Debug("background sync completed") }