mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-15 04:18:03 +00:00
Fix 6 issues surfaced by adversarial agent team (round 35)
Concurrency/lifecycle (P1, highest-value): - singleton_resources.go: RegisterBackgroundTask now REPLACES a stopped task instead of reusing it. BackgroundTask.Start is sync.Once-guarded, so a task stopped on instance close and re-registered (Traefik close+recreate for the same provider) was permanently dead: IsTaskRunning returned false (started && !stopped) so callers retried a start that was a no-op. Metadata refresh, token cleanup, and health checks now resume across a recreate cycle. - error_recovery.go: the shared graceful-degradation health task was bound to the FIRST instance's performHealthChecks (later instances' checks never ran) and any instance's Close stopped it for all. Added a global live-instance registry; the shared task runs every instance's checks and is stopped only when the last instance closes (mirrors the singleton-token-cleanup gate). Storage/cache: - hybrid.go recordL2Error: Store(now) then immediately compared time.Since(t)<1s against its own write => ALWAYS fired, so a single transient L2 error flipped the whole cache into fallback (full L2 bypass -> upstream stampede). Now enters fallback only on a PRIOR error within the window (true "multiple errors"). Updated two tests that had encoded the old single-error-fallback bug. - redis_pool.go: ConnectionPool.Put checked closed but sent on p.connections without holding p.mu; Close closed it under p.mu. Window => 'send on closed channel' panic at shutdown. Put now holds p.mu (the lock only Close used). HTTP/transport: - jwk.go fetchJWKS: added an explicit >1 MiB guard (was silent partial read, could yield a truncated keyset and spurious missing-kid). - http_client_pool.go configKey: now includes every transport-behavior field it actually consumes (compression, HTTP2, keep-alives, timeouts, buffer sizes, dial) so behaviorally different configs no longer share one transport built with the first config's settings. Each fix has a regression test. Verified: full source-order suite green, golangci-lint 0 issues, 3x full -race -count=1 -shuffle=on ./... all exit 0.
This commit is contained in:
+14
-7
@@ -123,16 +123,23 @@ func (rm *ResourceManager) RegisterBackgroundTask(name string, interval time.Dur
|
||||
rm.tasksMu.Lock()
|
||||
defer rm.tasksMu.Unlock()
|
||||
|
||||
// Check if task already exists
|
||||
if _, exists := rm.tasks[name]; exists {
|
||||
if rm.logger != nil {
|
||||
rm.logger.Debugf("Background task %s already registered", name)
|
||||
// If a task with this name already exists and is still running, keep it
|
||||
// (idempotent re-registration from another middleware instance).
|
||||
if existing, exists := rm.tasks[name]; exists {
|
||||
if atomic.LoadInt32(&existing.stopped) == 0 {
|
||||
if rm.logger != nil {
|
||||
rm.logger.Debugf("Background task %s already registered", name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Return existing task without error for idempotency
|
||||
return nil
|
||||
// The existing task was stopped. Reusing the same BackgroundTask
|
||||
// object would leave it permanently dead: BackgroundTask.Start is
|
||||
// guarded by sync.Once, so once stopped it can never restart, yet
|
||||
// IsTaskRunning returns false and callers will call Start again.
|
||||
// Replace it with a fresh task so re-registration after a
|
||||
// close+recreate cycle (same provider URL) actually resumes it.
|
||||
}
|
||||
|
||||
// Create new task with WaitGroup for proper cleanup
|
||||
task := NewBackgroundTask(name, interval, taskFunc, rm.logger, &rm.wg)
|
||||
rm.tasks[name] = task
|
||||
|
||||
|
||||
Reference in New Issue
Block a user