runServiceLoop is the main service loop. stopCh signals shutdown (nil = run forever on Unix)
(stopCh <-chan struct{})
| 74 | |
| 75 | // runServiceLoop is the main service loop. stopCh signals shutdown (nil = run forever on Unix) |
| 76 | func runServiceLoop(stopCh <-chan struct{}) error { |
| 77 | // When running as Windows service, allow a brief delay for system initialization |
| 78 | // (network, filesystem) to be ready after SCM starts the process. This addresses |
| 79 | // first-start issues where the report task would not run. |
| 80 | if runtime.GOOS == "windows" && isWindowsService() { |
| 81 | logger.Info("Windows service detected, waiting briefly for system initialization...") |
| 82 | time.Sleep(5 * time.Second) |
| 83 | } |
| 84 | |
| 85 | // Load credentials with retry on Windows service (first start may race with installer) |
| 86 | var loadErr error |
| 87 | for attempt := 0; attempt < 3; attempt++ { |
| 88 | loadErr = cfgManager.LoadCredentials() |
| 89 | if loadErr == nil { |
| 90 | break |
| 91 | } |
| 92 | if runtime.GOOS == "windows" && isWindowsService() && attempt < 2 { |
| 93 | logger.WithError(loadErr).Warn("Failed to load credentials, retrying in 2s...") |
| 94 | time.Sleep(2 * time.Second) |
| 95 | } else { |
| 96 | return loadErr |
| 97 | } |
| 98 | } |
| 99 | if loadErr != nil { |
| 100 | return loadErr |
| 101 | } |
| 102 | |
| 103 | httpClient := client.New(cfgManager, logger) |
| 104 | ctx := context.Background() |
| 105 | |
| 106 | // Get api_id for offset calculation |
| 107 | apiID := cfgManager.GetCredentials().APIID |
| 108 | |
| 109 | // Load interval from config.yml (with default fallback) |
| 110 | intervalMinutes := cfgManager.GetConfig().UpdateInterval |
| 111 | if intervalMinutes <= 0 { |
| 112 | // Default to 60 if not set or invalid |
| 113 | intervalMinutes = 60 |
| 114 | logger.WithField("interval", intervalMinutes).Info("Using default interval (not set in config)") |
| 115 | } else { |
| 116 | logger.WithField("interval", intervalMinutes).Info("Loaded interval from config.yml") |
| 117 | } |
| 118 | |
| 119 | // Fetch interval from server and update config if different |
| 120 | if resp, err := httpClient.GetUpdateInterval(ctx); err == nil && resp.UpdateInterval > 0 { |
| 121 | if resp.UpdateInterval != intervalMinutes { |
| 122 | logger.WithFields(logutil.SanitizeMap(map[string]interface{}{ |
| 123 | "config_interval": intervalMinutes, |
| 124 | "server_interval": resp.UpdateInterval, |
| 125 | })).Info("Server interval differs from config, updating config.yml") |
| 126 | |
| 127 | if err := cfgManager.SetUpdateInterval(resp.UpdateInterval); err != nil { |
| 128 | logger.WithError(err).Warn("Failed to save interval to config.yml") |
| 129 | } else { |
| 130 | intervalMinutes = resp.UpdateInterval |
| 131 | logger.WithField("interval", intervalMinutes).Info("Updated interval in config.yml") |
| 132 | } |
| 133 | } |
no test coverage detected