Files
wehub-resource-sync 75f3dd141c
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CI and Release / lint-frontend (push) Has been cancelled
CI and Release / dockerfile-scan (push) Has been cancelled
CI and Release / test-frontend (push) Has been cancelled
CI and Release / lint-verification-agent (push) Has been cancelled
CI and Release / test-verification-agent (push) Has been cancelled
CI and Release / e2e-verification-agent (push) Has been cancelled
CI and Release / test-backend (push) Has been cancelled
CI and Release / build-dev-image (push) Has been cancelled
CI and Release / push-dev-image (push) Has been cancelled
CI and Release / build-image (push) Has been cancelled
CI and Release / build-verification-image (push) Has been cancelled
CI and Release / determine-version (push) Has been cancelled
CI and Release / push-image (push) Has been cancelled
CI and Release / push-verification-image (12) (push) Has been cancelled
CI and Release / lint-backend (push) Has been cancelled
CI and Release / push-verification-image (17) (push) Has been cancelled
CI and Release / push-verification-image (18) (push) Has been cancelled
CI and Release / release (push) Has been cancelled
CI and Release / publish-helm-chart (push) Has been cancelled
CI and Release / push-verification-image (13) (push) Has been cancelled
CI and Release / push-verification-image (14) (push) Has been cancelled
CI and Release / push-verification-image (15) (push) Has been cancelled
CI and Release / push-verification-image (16) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:11:07 +08:00

110 lines
2.3 KiB
Go

package telemetry
import (
"context"
"fmt"
"log/slog"
"math/rand/v2"
"sync/atomic"
"time"
"databasus-backend/internal/config"
)
const (
defaultWarmupDuration = 1 * time.Minute
defaultSuccessInterval = 24 * time.Hour
defaultJitter = 10 * time.Minute
defaultInitialBackoff = 1 * time.Minute
defaultMaxBackoff = 24 * time.Hour
)
type TelemetryBackgroundService struct {
telemetryService *TelemetryService
logger *slog.Logger
WarmupDuration time.Duration
SuccessInterval time.Duration
Jitter time.Duration
InitialBackoff time.Duration
MaxBackoff time.Duration
hasRun atomic.Bool
}
func NewTelemetryBackgroundService(
telemetryService *TelemetryService,
logger *slog.Logger,
) *TelemetryBackgroundService {
return &TelemetryBackgroundService{
telemetryService: telemetryService,
logger: logger,
WarmupDuration: defaultWarmupDuration,
SuccessInterval: defaultSuccessInterval,
Jitter: defaultJitter,
InitialBackoff: defaultInitialBackoff,
MaxBackoff: defaultMaxBackoff,
}
}
func (s *TelemetryBackgroundService) Run(ctx context.Context) {
if s.hasRun.Swap(true) {
panic(fmt.Sprintf("%T.Run() called multiple times", s))
}
if config.GetEnv().IsDisableAnonymousTelemetry {
s.logger.Info("anonymous telemetry is disabled")
return
}
if !sleep(ctx, s.WarmupDuration) {
return
}
backoff := s.InitialBackoff
for {
err := s.telemetryService.BuildAndSend(ctx)
var nextDelay time.Duration
if err != nil {
s.logger.Warn("telemetry ping failed", "error", err, "next_retry", backoff)
nextDelay = backoff
backoff = min(backoff*2, s.MaxBackoff)
} else {
backoff = s.InitialBackoff
nextDelay = s.SuccessInterval + jitter(s.Jitter)
}
if !sleep(ctx, nextDelay) {
return
}
}
}
// sleep returns false when ctx is cancelled before d elapses.
func sleep(ctx context.Context, d time.Duration) bool {
if d <= 0 {
return ctx.Err() == nil
}
timer := time.NewTimer(d)
defer timer.Stop()
select {
case <-ctx.Done():
return false
case <-timer.C:
return true
}
}
// jitter returns a random offset in [-d, +d]. With d == 0 it returns 0.
func jitter(d time.Duration) time.Duration {
if d <= 0 {
return 0
}
return time.Duration(rand.Int64N(int64(2*d))) - d
}