// SPDX-License-Identifier: AGPL-3.0-or-later package main import ( "context" "log" "time" "comail/internal/adminclient" "comail/internal/featureflags" ) // flagPullTimeout bounds a single overrides fetch. const flagPullTimeout = 10 * time.Second // flagPullInterval is how often cmd/web pulls live overrides from the relay. // The relay owns the overrides file; web can't read it (separate user + // state dir), so it pulls the snapshot over the tailnet admin channel and // applies it to its own OpenFeature provider. ~30s latency on a live flip // reaching web's /pricing gate + account card is acceptable. const flagPullInterval = 30 * time.Second // runOverridesPuller pulls the relay's live-override snapshot once // immediately, then every flagPullInterval until ctx is done, applying each // snapshot to web's provider. Best-effort: a failed pull is logged and the // last good snapshot is kept (a transient relay blip never blanks web's // overrides), self-healing on the next tick. func runOverridesPuller(ctx context.Context, client *adminclient.Client) { pullOverridesOnce(ctx, client) ticker := time.NewTicker(flagPullInterval) defer ticker.Stop() for { select { case <-ctx.Done(): return case <-ticker.C: pullOverridesOnce(ctx, client) } } } // pullOverridesOnce fetches the relay snapshot and applies it. Any failure // is logged and swallowed; the provider keeps its current snapshot. func pullOverridesOnce(parent context.Context, client *adminclient.Client) { ctx, cancel := context.WithTimeout(parent, flagPullTimeout) defer cancel() ov, err := client.FetchOverrides(ctx) if err != nil { log.Printf("web.flags.overrides_pull_failed: %v", err) return } featureflags.ApplyOverrides(ov) }