diff --git a/cmd/msgvault/cmd/sync.go b/cmd/msgvault/cmd/sync.go index cc65cfde..e420811c 100644 --- a/cmd/msgvault/cmd/sync.go +++ b/cmd/msgvault/cmd/sync.go @@ -174,6 +174,23 @@ Examples: } } + // Rebuild analytics cache if requested. + var cacheErr error + if syncRebuildCache { + analyticsDir := cfg.AnalyticsDir() + fullRebuild := false + if staleness := cacheNeedsBuild(dbPath, analyticsDir); staleness.FullRebuild { + fullRebuild = true + } + result, err := buildCache(dbPath, analyticsDir, fullRebuild) + if err != nil { + cacheErr = err + fmt.Printf("\nCache rebuild failed: %v\n", err) + } else if !result.Skipped { + logger.Info("cache build completed", "exported", result.ExportedCount) + } + } + if len(syncErrors) > 0 { fmt.Println() fmt.Println("Errors:") @@ -183,6 +200,9 @@ Examples: return fmt.Errorf("%d account(s) failed to sync: %s", len(syncErrors), strings.Join(syncErrors, "; ")) } + if cacheErr != nil { + return fmt.Errorf("cache rebuild failed: %w", cacheErr) + } return nil }, @@ -264,6 +284,9 @@ func runIncrementalSync(ctx context.Context, s *store.Store, getOAuthMgr func(st return nil } +var syncRebuildCache bool + func init() { + syncIncrementalCmd.Flags().BoolVar(&syncRebuildCache, "rebuild-cache", false, "Rebuild analytics cache after sync") rootCmd.AddCommand(syncIncrementalCmd) } diff --git a/cmd/msgvault/cmd/syncfull.go b/cmd/msgvault/cmd/syncfull.go index 15847e62..95038946 100644 --- a/cmd/msgvault/cmd/syncfull.go +++ b/cmd/msgvault/cmd/syncfull.go @@ -166,6 +166,23 @@ Examples: } } + // Rebuild analytics cache if requested. + var cacheErr error + if syncFullRebuildCache { + analyticsDir := cfg.AnalyticsDir() + fullRebuild := false + if staleness := cacheNeedsBuild(dbPath, analyticsDir); staleness.FullRebuild { + fullRebuild = true + } + result, err := buildCache(dbPath, analyticsDir, fullRebuild) + if err != nil { + cacheErr = err + fmt.Printf("\nCache rebuild failed: %v\n", err) + } else if !result.Skipped { + logger.Info("cache build completed", "exported", result.ExportedCount) + } + } + if len(syncErrors) > 0 { fmt.Println() fmt.Println("Errors:") @@ -175,6 +192,9 @@ Examples: return fmt.Errorf("%d account(s) failed to sync: %s", len(syncErrors), strings.Join(syncErrors, "; ")) } + if cacheErr != nil { + return fmt.Errorf("cache rebuild failed: %w", cacheErr) + } return nil }, @@ -422,11 +442,14 @@ func formatDuration(d time.Duration) string { return fmt.Sprintf("%ds", s) } +var syncFullRebuildCache bool + func init() { syncFullCmd.Flags().StringVar(&syncQuery, "query", "", "Gmail search query") syncFullCmd.Flags().BoolVar(&syncNoResume, "noresume", false, "Force fresh sync (don't resume)") syncFullCmd.Flags().StringVar(&syncBefore, "before", "", "Only messages before this date (YYYY-MM-DD)") syncFullCmd.Flags().StringVar(&syncAfter, "after", "", "Only messages after this date (YYYY-MM-DD)") syncFullCmd.Flags().IntVar(&syncLimit, "limit", 0, "Limit number of messages (for testing)") + syncFullCmd.Flags().BoolVar(&syncFullRebuildCache, "rebuild-cache", false, "Rebuild analytics cache after sync") rootCmd.AddCommand(syncFullCmd) } diff --git a/internal/sync/incremental.go b/internal/sync/incremental.go index 2517f2d9..d85577bf 100644 --- a/internal/sync/incremental.go +++ b/internal/sync/incremental.go @@ -159,6 +159,7 @@ func (s *Syncer) Incremental(ctx context.Context, source *store.Source) (summary } else { for i, raw := range rawMessages { if raw == nil { + s.logger.Warn("failed to fetch message (nil response)", "id", newMsgIDs[i]) checkpoint.ErrorsCount++ continue } diff --git a/internal/sync/sync.go b/internal/sync/sync.go index 15cf307a..d46f7c51 100644 --- a/internal/sync/sync.go +++ b/internal/sync/sync.go @@ -191,6 +191,7 @@ func (s *Syncer) processBatch(ctx context.Context, sourceID int64, listResp *gma for i, raw := range rawMessages { if raw == nil { + s.logger.Warn("failed to fetch message (nil response)", "id", newIDs[i]) checkpoint.ErrorsCount++ continue }