Fork from QuoteForge → PriceForge
Renamed module path git.mchus.pro/mchus/quoteforge → git.mchus.pro/mchus/priceforge, renamed package quoteforge → priceforge, moved binary from cmd/qfs to cmd/pfs.
This commit is contained in:
102
internal/services/sync/worker.go
Normal file
102
internal/services/sync/worker.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"git.mchus.pro/mchus/priceforge/internal/db"
|
||||
)
|
||||
|
||||
// Worker performs background synchronization at regular intervals
|
||||
type Worker struct {
|
||||
service *Service
|
||||
connMgr *db.ConnectionManager
|
||||
interval time.Duration
|
||||
logger *slog.Logger
|
||||
stopCh chan struct{}
|
||||
}
|
||||
|
||||
// NewWorker creates a new background sync worker
|
||||
func NewWorker(service *Service, connMgr *db.ConnectionManager, interval time.Duration) *Worker {
|
||||
return &Worker{
|
||||
service: service,
|
||||
connMgr: connMgr,
|
||||
interval: interval,
|
||||
logger: slog.Default(),
|
||||
stopCh: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// isOnline checks if the database connection is available
|
||||
func (w *Worker) isOnline() bool {
|
||||
return w.connMgr.IsOnline()
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
if readiness, err := w.service.EnsureReadinessForSync(); err != nil {
|
||||
w.logger.Warn("background sync: blocked by readiness guard",
|
||||
"error", err,
|
||||
"reason_code", readiness.ReasonCode,
|
||||
"reason_text", readiness.ReasonText,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Push pending changes first
|
||||
pushed, err := w.service.PushPendingChanges()
|
||||
if err != nil {
|
||||
w.logger.Warn("background sync: failed to push pending changes", "error", err)
|
||||
} else if pushed > 0 {
|
||||
w.logger.Info("background sync: pushed pending changes", "count", pushed)
|
||||
}
|
||||
|
||||
// Then check for new pricelists
|
||||
err = w.service.SyncPricelistsIfNeeded()
|
||||
if err != nil {
|
||||
w.logger.Warn("background sync: failed to sync pricelists", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Mark user's sync heartbeat (used for online/offline status in UI).
|
||||
w.service.RecordSyncHeartbeat()
|
||||
|
||||
w.logger.Info("background sync cycle completed")
|
||||
}
|
||||
Reference in New Issue
Block a user