From 4d2cbe4883e0e8befbfd25de719604cc084863f2 Mon Sep 17 00:00:00 2001 From: Christophe Vila Date: Sun, 26 Jul 2026 18:01:41 +0200 Subject: [PATCH] refactor: remove automatic background incremental sync Garmin's rate-limiting means every unattended sync attempt risks a ban; syncing should only ever happen when explicitly triggered via "Sync now" (POST /api/sync/run), never on an unattended timer. Removes the periodic background loop (main.go's runIncrementalSyncLoop, api.Server.RunIncrementalSyncForAllUsers), its GENIUSRUN_INCREMENTAL_SYNC_EVERY config, and store.DB.ListUsers (which existed solely to feed it). The manual "Sync now" flow (Backfill/IncrementalSync/FillPendingDetails via FullSync) is untouched. --- backend/cmd/geniusrund/main.go | 18 ------------------ backend/internal/api/server.go | 25 ------------------------- backend/internal/config/config.go | 4 +--- backend/internal/store/users.go | 20 -------------------- 4 files changed, 1 insertion(+), 66 deletions(-) diff --git a/backend/cmd/geniusrund/main.go b/backend/cmd/geniusrund/main.go index f5ef198..6a757f7 100644 --- a/backend/cmd/geniusrund/main.go +++ b/backend/cmd/geniusrund/main.go @@ -62,8 +62,6 @@ func main() { ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() - go runIncrementalSyncLoop(ctx, server, cfg.IncrementalSyncEvery) - httpServer := &http.Server{Addr: cfg.Addr, Handler: server.Router()} go func() { log.Printf("geniusrund listening on %s", cfg.Addr) @@ -80,19 +78,3 @@ func main() { log.Printf("http server shutdown: %v", err) } } - -// runIncrementalSyncLoop periodically syncs new activities for every -// provisioned user in the background so the frontend doesn't need to -// trigger every sync manually. -func runIncrementalSyncLoop(ctx context.Context, server *api.Server, every time.Duration) { - ticker := time.NewTicker(every) - defer ticker.Stop() - for { - select { - case <-ctx.Done(): - return - case <-ticker.C: - server.RunIncrementalSyncForAllUsers(ctx) - } - } -} diff --git a/backend/internal/api/server.go b/backend/internal/api/server.go index 9ce2401..cd87c26 100644 --- a/backend/internal/api/server.go +++ b/backend/internal/api/server.go @@ -264,31 +264,6 @@ func (s *Server) removeGarminClient(userID int64) { } } -// RunIncrementalSyncForAllUsers is called on a timer (see main.go) to sync -// every provisioned user in turn, replacing the old single-global-Service -// background loop. -func (s *Server) RunIncrementalSyncForAllUsers(ctx context.Context) { - users, err := s.DB.ListUsers(ctx) - if err != nil { - log.Printf("api: list users for incremental sync: %v", err) - return - } - for _, u := range users { - svc, err := s.syncFor(ctx, u.ID) - if err != nil { - log.Printf("api: sync service for user %d: %v", u.ID, err) - continue - } - if err := svc.IncrementalSync(ctx); err != nil { - log.Printf("api: incremental sync for user %d: %v", u.ID, err) - continue - } - if err := svc.FillPendingDetails(ctx, 50); err != nil { - log.Printf("api: fill pending details for user %d: %v", u.ID, err) - } - } -} - var requestIDCounter atomic.Int64 // requestLoggingMiddleware logs one JSON line per HTTP request (method, diff --git a/backend/internal/config/config.go b/backend/internal/config/config.go index 37d95eb..523ad62 100644 --- a/backend/internal/config/config.go +++ b/backend/internal/config/config.go @@ -35,8 +35,7 @@ type Config struct { // (see api.Server.garminFor), so it can never be silently left empty. GarminTokenStoreRoot string - MinConfidence float64 - IncrementalSyncEvery time.Duration + MinConfidence float64 // LogLevel controls internal/applog's JSON logger ("debug"|"info"|"warn"|"error"). LogLevel string @@ -76,7 +75,6 @@ func Load() (Config, error) { GarminPythonPath: getEnvDefault("GARMIN_WRAPPER_PYTHON", "python3"), GarminTokenStoreRoot: os.Getenv("GARMIN_TOKENSTORE"), MinConfidence: getEnvFloat("GENIUSRUN_MIN_CONFIDENCE", 0.6), - IncrementalSyncEvery: getEnvDuration("GENIUSRUN_INCREMENTAL_SYNC_EVERY", 6*time.Hour), LogLevel: getEnvDefault("GENIUSRUN_LOG_LEVEL", "info"), BackendURL: strings.TrimRight(os.Getenv("GENIUSRUN_BACKEND_URL"), "/"), OIDCIssuerURL: os.Getenv("GENIUSRUN_OIDC_ISSUER_URL"), diff --git a/backend/internal/store/users.go b/backend/internal/store/users.go index 43b0395..556d8f7 100644 --- a/backend/internal/store/users.go +++ b/backend/internal/store/users.go @@ -32,26 +32,6 @@ func (db *DB) GetUserBySub(ctx context.Context, oidcSub string) (User, bool, err return u, true, nil } -// ListUsers returns every provisioned user, for the background incremental -// sync loop to iterate. -func (db *DB) ListUsers(ctx context.Context) ([]User, error) { - rows, err := db.QueryContext(ctx, `SELECT id, oidc_sub, display_name, created_at FROM users ORDER BY id`) - if err != nil { - return nil, fmt.Errorf("list users: %w", err) - } - defer rows.Close() - - users := []User{} - for rows.Next() { - var u User - if err := rows.Scan(&u.ID, &u.OIDCSub, &u.DisplayName, &u.CreatedAt); err != nil { - return nil, fmt.Errorf("scan user row: %w", err) - } - users = append(users, u) - } - return users, rows.Err() -} - // neverMatchRule is the placeholder every fresh install's rule-engine kinds // start with -- every activity lands in needs_review until the user tunes // real rules.