summaryrefslogtreecommitdiff
path: root/internal/controlplane/manager.go
diff options
context:
space:
mode:
authorChia <Chia@93.nz>2026-08-05 22:01:29 +1200
committerChia <Chia@93.nz>2026-08-05 22:07:50 +1200
commiteadb2ffe85c43cf6fc741c9823cd28eedb4a844c (patch)
tree1aba2536d57360da403aa35c9ced58b615c7064e /internal/controlplane/manager.go
parentcd0dd91ab93653631904f2ea0e574ccde6d60339 (diff)
feat: harden prepaid billing and commercial operations
Diffstat (limited to 'internal/controlplane/manager.go')
-rw-r--r--internal/controlplane/manager.go44
1 files changed, 34 insertions, 10 deletions
diff --git a/internal/controlplane/manager.go b/internal/controlplane/manager.go
index b6be748..212963b 100644
--- a/internal/controlplane/manager.go
+++ b/internal/controlplane/manager.go
@@ -28,16 +28,19 @@ type policyReplacer interface {
}
type Manager struct {
- store managerStore
- catalog *catalog.Catalog
- authenticator *auth.StaticAuthenticator
- logger *slog.Logger
- pollInterval time.Duration
- generation atomic.Int64
- redisConnected atomic.Bool
- reloadMu sync.Mutex
- broadcasts chan ChangeEvent
- policyTarget policyReplacer
+ store managerStore
+ catalog *catalog.Catalog
+ authenticator *auth.StaticAuthenticator
+ logger *slog.Logger
+ pollInterval time.Duration
+ generation atomic.Int64
+ redisConnected atomic.Bool
+ loaded atomic.Bool
+ lastReloadUnix atomic.Int64
+ lastHealthyUnix atomic.Int64
+ reloadMu sync.Mutex
+ broadcasts chan ChangeEvent
+ policyTarget policyReplacer
}
func NewManager(store managerStore, modelCatalog *catalog.Catalog, authenticator *auth.StaticAuthenticator, logger *slog.Logger, pollInterval time.Duration, policyTargets ...policyReplacer) *Manager {
@@ -67,10 +70,30 @@ func (m *Manager) Reload(ctx context.Context) (int64, error) {
m.policyTarget.ReplacePolicies(snapshot.Limits)
}
m.generation.Store(snapshot.Generation)
+ m.loaded.Store(true)
+ m.lastReloadUnix.Store(time.Now().UTC().Unix())
+ m.lastHealthyUnix.Store(time.Now().UTC().Unix())
m.logger.Info("control_plane_reloaded", "generation", snapshot.Generation, "models", len(snapshot.Models), "api_keys", len(snapshot.APIKeys))
return snapshot.Generation, nil
}
+func (m *Manager) Loaded() bool { return m.loaded.Load() }
+func (m *Manager) LastReloadAt() time.Time {
+ value := m.lastReloadUnix.Load()
+ if value == 0 {
+ return time.Time{}
+ }
+ return time.Unix(value, 0).UTC()
+}
+
+func (m *Manager) LastHealthyAt() time.Time {
+ value := m.lastHealthyUnix.Load()
+ if value == 0 {
+ return time.Time{}
+ }
+ return time.Unix(value, 0).UTC()
+}
+
func (m *Manager) AfterMutation(ctx context.Context, generation int64, resource, id string) error {
loadedGeneration, err := m.Reload(ctx)
if err != nil {
@@ -135,6 +158,7 @@ func (m *Manager) runPolling(ctx context.Context) {
m.logger.Warn("control_plane_generation_check_failed", "error", err)
continue
}
+ m.lastHealthyUnix.Store(time.Now().UTC().Unix())
if generation > m.generation.Load() {
if _, err := m.Reload(ctx); err != nil {
m.logger.Error("control_plane_reload_failed", "source", "postgres", "error", err)