package securityevents import ( "context" "errors" "time" "github.com/easyai/easyai-ai-gateway/apps/api/internal/store" ) const ( identitySecretCleanupInterval = time.Minute identitySecretCleanupLease = 5 * time.Minute identitySecretCleanupBatch = 100 ) type IdentitySecretCleanupRepository interface { ClaimIdentitySecretCleanups(context.Context, int, time.Duration) ([]store.IdentitySecretCleanupClaim, error) CompleteIdentitySecretCleanup(context.Context, string, string) (bool, error) } // RunIdentitySecretCleanupWorker owns cleanup at the server lifecycle rather // than at any individual Active or Prepared identity Runtime. PostgreSQL claim // tokens make running one worker per server replica safe. func RunIdentitySecretCleanupWorker(ctx context.Context, repository IdentitySecretCleanupRepository, secrets SecretStore) { if repository == nil || secrets == nil { return } ticker := time.NewTicker(identitySecretCleanupInterval) defer ticker.Stop() for { cleanupClaimedIdentitySecrets(ctx, repository, secrets) select { case <-ctx.Done(): return case <-ticker.C: } } } func cleanupClaimedIdentitySecrets(ctx context.Context, repository IdentitySecretCleanupRepository, secrets SecretStore) { claims, err := repository.ClaimIdentitySecretCleanups(ctx, identitySecretCleanupBatch, identitySecretCleanupLease) if err != nil { return } for _, claim := range claims { if err := secrets.Delete(ctx, claim.Reference); err != nil && !errors.Is(err, ErrSecretNotFound) { continue } _, _ = repository.CompleteIdentitySecretCleanup(ctx, claim.Reference, claim.ClaimToken) } }