mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-18 04:47:28 +00:00
Smarter approach to the cookies (#103)
* Smarter approach to the cookies - Single maxCookieSize = 1400 constant with clear documentation - Combined cookie storage for ~40-45% size reduction - Backward compatible migration from legacy cookies * Tuneup the code.
This commit is contained in:
Vendored
+14
-25
@@ -18,33 +18,22 @@ const (
|
||||
|
||||
// Config provides common configuration for cache backends
|
||||
type Config struct {
|
||||
// Type specifies the backend type
|
||||
Type BackendType
|
||||
|
||||
// Memory backend settings
|
||||
MaxSize int
|
||||
MaxMemoryBytes int64
|
||||
CleanupInterval time.Duration
|
||||
|
||||
// Redis backend settings
|
||||
RedisAddr string
|
||||
RedisPassword string
|
||||
RedisDB int
|
||||
RedisPrefix string
|
||||
PoolSize int
|
||||
|
||||
// Hybrid backend settings
|
||||
L1Config *Config // Memory cache (L1)
|
||||
L2Config *Config // Redis cache (L2)
|
||||
AsyncWrites bool // Write to L2 asynchronously
|
||||
|
||||
// Resilience settings
|
||||
L2Config *Config
|
||||
L1Config *Config
|
||||
RedisPrefix string
|
||||
Type BackendType
|
||||
RedisAddr string
|
||||
RedisPassword string
|
||||
PoolSize int
|
||||
RedisDB int
|
||||
CleanupInterval time.Duration
|
||||
MaxMemoryBytes int64
|
||||
MaxSize int
|
||||
HealthCheckInterval time.Duration
|
||||
AsyncWrites bool
|
||||
EnableCircuitBreaker bool
|
||||
EnableHealthCheck bool
|
||||
HealthCheckInterval time.Duration
|
||||
|
||||
// Metrics
|
||||
EnableMetrics bool
|
||||
EnableMetrics bool
|
||||
}
|
||||
|
||||
// DefaultConfig returns a default configuration for in-memory caching
|
||||
|
||||
Vendored
+18
-28
@@ -13,40 +13,30 @@ import (
|
||||
// HybridBackend implements a two-tier cache with L1 (memory) and L2 (Redis) backends
|
||||
// It provides automatic failover, async writes for non-critical data, and optimized read paths
|
||||
type HybridBackend struct {
|
||||
primary CacheBackend // L1: Memory cache for fast access
|
||||
secondary CacheBackend // L2: Redis cache for distributed access
|
||||
|
||||
// Configuration
|
||||
syncWriteCacheTypes map[string]bool // Which cache types require synchronous writes
|
||||
lastL2Error atomic.Value
|
||||
secondary CacheBackend
|
||||
primary CacheBackend
|
||||
logger Logger
|
||||
ctx context.Context
|
||||
syncWriteCacheTypes map[string]bool
|
||||
asyncWriteBuffer chan *asyncWriteItem
|
||||
|
||||
// Metrics
|
||||
l1Hits atomic.Int64
|
||||
l2Hits atomic.Int64
|
||||
misses atomic.Int64
|
||||
l1Writes atomic.Int64
|
||||
l2Writes atomic.Int64
|
||||
errors atomic.Int64
|
||||
|
||||
// Fallback tracking
|
||||
fallbackMode atomic.Bool // True when operating in degraded mode (L1 only)
|
||||
lastL2Error atomic.Value // Stores last L2 error timestamp
|
||||
|
||||
// Lifecycle
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
wg sync.WaitGroup
|
||||
|
||||
// Logging
|
||||
logger Logger
|
||||
cancel context.CancelFunc
|
||||
wg sync.WaitGroup
|
||||
l1Hits atomic.Int64
|
||||
errors atomic.Int64
|
||||
l2Writes atomic.Int64
|
||||
l1Writes atomic.Int64
|
||||
misses atomic.Int64
|
||||
l2Hits atomic.Int64
|
||||
fallbackMode atomic.Bool
|
||||
}
|
||||
|
||||
// asyncWriteItem represents an async write operation
|
||||
type asyncWriteItem struct {
|
||||
ctx context.Context
|
||||
key string
|
||||
value []byte
|
||||
ttl time.Duration
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// Logger interface for structured logging
|
||||
@@ -82,9 +72,9 @@ func (l *defaultLogger) Errorf(format string, args ...interface{}) {
|
||||
type HybridConfig struct {
|
||||
Primary CacheBackend
|
||||
Secondary CacheBackend
|
||||
SyncWriteCacheTypes map[string]bool // Cache types requiring synchronous L2 writes
|
||||
AsyncBufferSize int
|
||||
Logger Logger
|
||||
SyncWriteCacheTypes map[string]bool
|
||||
AsyncBufferSize int
|
||||
}
|
||||
|
||||
// NewHybridBackend creates a new hybrid cache backend with L1 (memory) and L2 (Redis) tiers
|
||||
|
||||
+6
-6
@@ -17,23 +17,23 @@ import (
|
||||
|
||||
// mockBackend is a simple mock implementation of CacheBackend for testing
|
||||
type mockBackend struct {
|
||||
pingError error
|
||||
data map[string]mockEntry
|
||||
stats map[string]interface{}
|
||||
mu sync.RWMutex
|
||||
getCalls atomic.Int32
|
||||
setCalls atomic.Int32
|
||||
deleteCalls atomic.Int32
|
||||
failSet bool
|
||||
failGet bool
|
||||
failDelete bool
|
||||
failClear bool
|
||||
failPing bool
|
||||
pingError error
|
||||
stats map[string]interface{}
|
||||
getCalls atomic.Int32
|
||||
setCalls atomic.Int32
|
||||
deleteCalls atomic.Int32
|
||||
}
|
||||
|
||||
type mockEntry struct {
|
||||
value []byte
|
||||
expiresAt time.Time
|
||||
value []byte
|
||||
}
|
||||
|
||||
// mockBatchBackend extends mockBackend with batch operations
|
||||
|
||||
Vendored
+14
-45
@@ -41,53 +41,22 @@ type CacheBackend interface {
|
||||
|
||||
// BackendStats represents statistics for a cache backend
|
||||
type BackendStats struct {
|
||||
// Type is the backend type
|
||||
Type BackendType
|
||||
|
||||
// Hits is the number of cache hits
|
||||
Hits int64
|
||||
|
||||
// Misses is the number of cache misses
|
||||
Misses int64
|
||||
|
||||
// Sets is the number of set operations
|
||||
Sets int64
|
||||
|
||||
// Deletes is the number of delete operations
|
||||
Deletes int64
|
||||
|
||||
// Errors is the number of errors
|
||||
Errors int64
|
||||
|
||||
// Evictions is the number of evicted items
|
||||
Evictions int64
|
||||
|
||||
// CurrentSize is the current number of items in cache
|
||||
CurrentSize int64
|
||||
|
||||
// MaxSize is the maximum number of items (0 means unlimited)
|
||||
MaxSize int64
|
||||
|
||||
// MemoryUsage is the approximate memory usage in bytes
|
||||
MemoryUsage int64
|
||||
|
||||
// AverageGetLatency is the average latency for get operations
|
||||
StartTime time.Time
|
||||
LastErrorTime time.Time
|
||||
Type BackendType
|
||||
LastError string
|
||||
Deletes int64
|
||||
Errors int64
|
||||
Evictions int64
|
||||
CurrentSize int64
|
||||
MaxSize int64
|
||||
MemoryUsage int64
|
||||
AverageGetLatency time.Duration
|
||||
|
||||
// AverageSetLatency is the average latency for set operations
|
||||
AverageSetLatency time.Duration
|
||||
|
||||
// LastError is the last error encountered
|
||||
LastError string
|
||||
|
||||
// LastErrorTime is when the last error occurred
|
||||
LastErrorTime time.Time
|
||||
|
||||
// Uptime is how long the backend has been running
|
||||
Uptime time.Duration
|
||||
|
||||
// StartTime is when the backend was started
|
||||
StartTime time.Time
|
||||
Sets int64
|
||||
Misses int64
|
||||
Uptime time.Duration
|
||||
Hits int64
|
||||
}
|
||||
|
||||
// BackendCapabilities describes the capabilities of a cache backend
|
||||
|
||||
Vendored
+27
-35
@@ -11,14 +11,14 @@ import (
|
||||
|
||||
// memoryCacheItem represents an item in the memory cache
|
||||
type memoryCacheItem struct {
|
||||
key string
|
||||
value interface{}
|
||||
expiresAt time.Time
|
||||
createdAt time.Time
|
||||
accessedAt time.Time
|
||||
value interface{}
|
||||
element *list.Element
|
||||
key string
|
||||
accessCount int64
|
||||
size int64
|
||||
element *list.Element // for LRU tracking
|
||||
}
|
||||
|
||||
// isExpired checks if the item is expired
|
||||
@@ -31,39 +31,31 @@ func (item *memoryCacheItem) isExpired() bool {
|
||||
|
||||
// MemoryCacheBackend implements the CacheBackend interface using in-memory storage
|
||||
type MemoryCacheBackend struct {
|
||||
mu sync.RWMutex
|
||||
items map[string]*memoryCacheItem
|
||||
lruList *list.List
|
||||
maxSize int64
|
||||
maxMemory int64
|
||||
currentSize int64
|
||||
currentMemory int64
|
||||
|
||||
// Statistics
|
||||
hits atomic.Int64
|
||||
misses atomic.Int64
|
||||
sets atomic.Int64
|
||||
deletes atomic.Int64
|
||||
evictions atomic.Int64
|
||||
errors atomic.Int64
|
||||
|
||||
// Latency tracking
|
||||
totalGetTime atomic.Int64
|
||||
totalSetTime atomic.Int64
|
||||
getCount atomic.Int64
|
||||
setCount atomic.Int64
|
||||
|
||||
// Status
|
||||
startTime time.Time
|
||||
lastError string
|
||||
lastErrorTime time.Time
|
||||
cleanupTicker *time.Ticker
|
||||
cleanupDone chan bool
|
||||
closed atomic.Bool
|
||||
|
||||
// Configuration
|
||||
startTime time.Time
|
||||
lastErrorTime time.Time
|
||||
items map[string]*memoryCacheItem
|
||||
lruList *list.List
|
||||
cleanupDone chan bool
|
||||
cleanupTicker *time.Ticker
|
||||
evictionPolicy string
|
||||
lastError string
|
||||
currentMemory int64
|
||||
misses atomic.Int64
|
||||
deletes atomic.Int64
|
||||
evictions atomic.Int64
|
||||
errors atomic.Int64
|
||||
totalGetTime atomic.Int64
|
||||
totalSetTime atomic.Int64
|
||||
getCount atomic.Int64
|
||||
setCount atomic.Int64
|
||||
sets atomic.Int64
|
||||
hits atomic.Int64
|
||||
maxSize int64
|
||||
currentSize int64
|
||||
maxMemory int64
|
||||
cleanupInterval time.Duration
|
||||
evictionPolicy string // "lru", "lfu", "fifo"
|
||||
mu sync.RWMutex
|
||||
closed atomic.Bool
|
||||
}
|
||||
|
||||
// NewMemoryCacheBackend creates a new memory cache backend
|
||||
|
||||
+10
-16
@@ -9,30 +9,24 @@ import (
|
||||
|
||||
// HealthMonitor continuously monitors Redis connection health and triggers reconnections
|
||||
type HealthMonitor struct {
|
||||
pool *ConnectionPool
|
||||
config *HealthMonitorConfig
|
||||
|
||||
// State
|
||||
healthy atomic.Bool
|
||||
running atomic.Bool
|
||||
lastCheckTime atomic.Int64 // Unix timestamp
|
||||
|
||||
// Metrics
|
||||
pool *ConnectionPool
|
||||
config *HealthMonitorConfig
|
||||
stopChan chan struct{}
|
||||
wg sync.WaitGroup
|
||||
lastCheckTime atomic.Int64
|
||||
consecutiveFailures atomic.Int64
|
||||
totalChecks atomic.Int64
|
||||
totalFailures atomic.Int64
|
||||
|
||||
// Lifecycle
|
||||
stopChan chan struct{}
|
||||
wg sync.WaitGroup
|
||||
healthy atomic.Bool
|
||||
running atomic.Bool
|
||||
}
|
||||
|
||||
// HealthMonitorConfig configures the health monitor
|
||||
type HealthMonitorConfig struct {
|
||||
CheckInterval time.Duration // How often to check health
|
||||
Timeout time.Duration // Timeout for health check
|
||||
UnhealthyThreshold int // Consecutive failures before marking unhealthy
|
||||
OnHealthChange func(healthy bool)
|
||||
CheckInterval time.Duration
|
||||
Timeout time.Duration
|
||||
UnhealthyThreshold int
|
||||
}
|
||||
|
||||
// DefaultHealthMonitorConfig returns default health monitor configuration
|
||||
|
||||
Vendored
+5
-5
@@ -15,8 +15,8 @@ import (
|
||||
func TestRESPWriter_WriteCommand(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
expected string
|
||||
args []string
|
||||
}{
|
||||
{
|
||||
name: "Simple command",
|
||||
@@ -205,9 +205,9 @@ func TestRESPReader_ReadInteger(t *testing.T) {
|
||||
// TestRESPReader_ReadBulkString tests reading bulk strings
|
||||
func TestRESPReader_ReadBulkString(t *testing.T) {
|
||||
tests := []struct {
|
||||
expected interface{}
|
||||
name string
|
||||
input string
|
||||
expected interface{}
|
||||
wantErr bool
|
||||
isNil bool
|
||||
}{
|
||||
@@ -440,10 +440,10 @@ func TestRESPHelpers(t *testing.T) {
|
||||
// TestRESPRoundTrip tests full round-trip encoding/decoding
|
||||
func TestRESPRoundTrip(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
command []string
|
||||
response string
|
||||
expected interface{}
|
||||
name string
|
||||
response string
|
||||
command []string
|
||||
}{
|
||||
{
|
||||
name: "PING command",
|
||||
|
||||
Vendored
+28
-40
@@ -33,21 +33,19 @@ type Logger interface {
|
||||
|
||||
// Config provides configuration for the cache
|
||||
type Config struct {
|
||||
Logger Logger
|
||||
JWKConfig *JWKConfig
|
||||
MetadataConfig *MetadataConfig
|
||||
TokenConfig *TokenConfig
|
||||
Type Type
|
||||
MaxSize int
|
||||
MaxMemoryBytes int64
|
||||
DefaultTTL time.Duration
|
||||
CleanupInterval time.Duration
|
||||
EnableCompression bool
|
||||
MaxMemoryBytes int64
|
||||
MaxSize int
|
||||
EnableMetrics bool
|
||||
EnableAutoCleanup bool
|
||||
EnableMemoryLimit bool
|
||||
Logger Logger
|
||||
|
||||
// Type-specific configurations
|
||||
TokenConfig *TokenConfig
|
||||
MetadataConfig *MetadataConfig
|
||||
JWKConfig *JWKConfig
|
||||
EnableCompression bool
|
||||
}
|
||||
|
||||
// TokenConfig provides token-specific cache configuration
|
||||
@@ -59,11 +57,11 @@ type TokenConfig struct {
|
||||
|
||||
// MetadataConfig provides metadata-specific cache configuration
|
||||
type MetadataConfig struct {
|
||||
SecurityCriticalFields []string
|
||||
GracePeriod time.Duration
|
||||
ExtendedGracePeriod time.Duration
|
||||
MaxGracePeriod time.Duration
|
||||
SecurityCriticalMaxGracePeriod time.Duration
|
||||
SecurityCriticalFields []string
|
||||
}
|
||||
|
||||
// JWKConfig provides JWK-specific cache configuration
|
||||
@@ -75,45 +73,35 @@ type JWKConfig struct {
|
||||
|
||||
// Item represents a single cache entry
|
||||
type Item struct {
|
||||
Key string
|
||||
Value interface{}
|
||||
Size int64
|
||||
ExpiresAt time.Time
|
||||
LastAccessed time.Time
|
||||
AccessCount int64
|
||||
Value interface{}
|
||||
Metadata map[string]interface{}
|
||||
element *list.Element
|
||||
Key string
|
||||
CacheType Type
|
||||
|
||||
// Type-specific metadata
|
||||
Metadata map[string]interface{}
|
||||
|
||||
// LRU list element reference
|
||||
element *list.Element
|
||||
Size int64
|
||||
AccessCount int64
|
||||
}
|
||||
|
||||
// Cache provides a single, unified cache implementation
|
||||
type Cache struct {
|
||||
mu sync.RWMutex
|
||||
items map[string]*Item
|
||||
lruList *list.List
|
||||
config Config
|
||||
logger Logger
|
||||
|
||||
// Memory management
|
||||
config Config
|
||||
ctx context.Context
|
||||
logger Logger
|
||||
cancel context.CancelFunc
|
||||
lruList *list.List
|
||||
items map[string]*Item
|
||||
stopCleanup chan bool
|
||||
wg sync.WaitGroup
|
||||
currentSize int64
|
||||
currentMemory int64
|
||||
|
||||
// Metrics
|
||||
hits int64
|
||||
misses int64
|
||||
evictions int64
|
||||
sets int64
|
||||
|
||||
// Lifecycle management
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
wg sync.WaitGroup
|
||||
stopCleanup chan bool
|
||||
closed int32
|
||||
hits int64
|
||||
misses int64
|
||||
evictions int64
|
||||
sets int64
|
||||
mu sync.RWMutex
|
||||
closed int32
|
||||
}
|
||||
|
||||
// DefaultConfig returns a default cache configuration
|
||||
|
||||
Vendored
+11
-11
@@ -1750,19 +1750,19 @@ func TestAdvancedEdgeCases(t *testing.T) {
|
||||
|
||||
// Test with various data types
|
||||
testCases := []struct {
|
||||
key string
|
||||
value interface{}
|
||||
key string
|
||||
}{
|
||||
{"string", "test string"},
|
||||
{"int", 42},
|
||||
{"float", 3.14159},
|
||||
{"bool", true},
|
||||
{"slice", []string{"a", "b", "c"}},
|
||||
{"map", map[string]int{"one": 1, "two": 2}},
|
||||
{"nil", nil},
|
||||
{"empty-string", ""},
|
||||
{"empty-slice", []string{}},
|
||||
{"empty-map", map[string]interface{}{}},
|
||||
{key: "string", value: "test string"},
|
||||
{key: "int", value: 42},
|
||||
{key: "float", value: 3.14159},
|
||||
{key: "bool", value: true},
|
||||
{key: "slice", value: []string{"a", "b", "c"}},
|
||||
{key: "map", value: map[string]int{"one": 1, "two": 2}},
|
||||
{key: "nil", value: nil},
|
||||
{key: "empty-string", value: ""},
|
||||
{key: "empty-slice", value: []string{}},
|
||||
{key: "empty-map", value: map[string]interface{}{}},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
||||
Vendored
+2
-7
@@ -7,22 +7,17 @@ import (
|
||||
|
||||
// Manager manages multiple cache instances with singleton pattern
|
||||
type Manager struct {
|
||||
mu sync.RWMutex
|
||||
|
||||
// Core caches
|
||||
logger Logger
|
||||
tokenCache *Cache
|
||||
metadataCache *Cache
|
||||
jwkCache *Cache
|
||||
sessionCache *Cache
|
||||
generalCache *Cache
|
||||
|
||||
// Typed wrappers
|
||||
typedToken *TokenCache
|
||||
typedMetadata *MetadataCache
|
||||
typedJWK *JWKCache
|
||||
typedSession *SessionCache
|
||||
|
||||
logger Logger
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
+23
-42
@@ -48,23 +48,12 @@ func (s State) String() string {
|
||||
|
||||
// CircuitBreakerConfig holds configuration for the circuit breaker
|
||||
type CircuitBreakerConfig struct {
|
||||
// MaxFailures is the number of consecutive failures before opening the circuit
|
||||
MaxFailures int
|
||||
|
||||
// FailureThreshold is the failure rate threshold (0.0 to 1.0)
|
||||
FailureThreshold float64
|
||||
|
||||
// Timeout is how long the circuit stays open before trying half-open
|
||||
Timeout time.Duration
|
||||
|
||||
// HalfOpenMaxRequests is the number of requests allowed in half-open state
|
||||
OnStateChange func(from, to State)
|
||||
MaxFailures int
|
||||
FailureThreshold float64
|
||||
Timeout time.Duration
|
||||
HalfOpenMaxRequests int
|
||||
|
||||
// ResetTimeout is how long to wait before resetting counters in closed state
|
||||
ResetTimeout time.Duration
|
||||
|
||||
// OnStateChange is called when the circuit breaker changes state
|
||||
OnStateChange func(from, to State)
|
||||
ResetTimeout time.Duration
|
||||
}
|
||||
|
||||
// DefaultCircuitBreakerConfig returns default configuration
|
||||
@@ -80,28 +69,20 @@ func DefaultCircuitBreakerConfig() *CircuitBreakerConfig {
|
||||
|
||||
// CircuitBreaker implements the circuit breaker pattern
|
||||
type CircuitBreaker struct {
|
||||
config *CircuitBreakerConfig
|
||||
|
||||
// State management
|
||||
state atomic.Int32
|
||||
lastStateChange time.Time
|
||||
stateMu sync.RWMutex
|
||||
|
||||
// Failure tracking
|
||||
consecutiveFailures atomic.Int32
|
||||
totalRequests atomic.Int64
|
||||
nextRetryTime time.Time
|
||||
lastStateChange time.Time
|
||||
lastSuccessTime time.Time
|
||||
lastFailureTime time.Time
|
||||
config *CircuitBreakerConfig
|
||||
totalFailures atomic.Int64
|
||||
totalRequests atomic.Int64
|
||||
stateTransitions atomic.Int64
|
||||
rejectedRequests atomic.Int64
|
||||
stateMu sync.RWMutex
|
||||
timeMu sync.RWMutex
|
||||
halfOpenRequests atomic.Int32
|
||||
|
||||
// Timing
|
||||
lastFailureTime time.Time
|
||||
lastSuccessTime time.Time
|
||||
nextRetryTime time.Time
|
||||
timeMu sync.RWMutex
|
||||
|
||||
// Metrics
|
||||
stateTransitions atomic.Int64
|
||||
rejectedRequests atomic.Int64
|
||||
consecutiveFailures atomic.Int32
|
||||
state atomic.Int32
|
||||
}
|
||||
|
||||
// NewCircuitBreaker creates a new circuit breaker
|
||||
@@ -313,17 +294,17 @@ func (cb *CircuitBreaker) Stats() CircuitBreakerStats {
|
||||
|
||||
// CircuitBreakerStats holds statistics for the circuit breaker
|
||||
type CircuitBreakerStats struct {
|
||||
State State
|
||||
ConsecutiveFailures int32
|
||||
LastFailureTime time.Time
|
||||
LastSuccessTime time.Time
|
||||
LastStateChange time.Time
|
||||
NextRetryTime time.Time
|
||||
TotalRequests int64
|
||||
TotalFailures int64
|
||||
SuccessRate float64
|
||||
RejectedRequests int64
|
||||
StateTransitions int64
|
||||
LastFailureTime time.Time
|
||||
LastSuccessTime time.Time
|
||||
LastStateChange time.Time
|
||||
NextRetryTime time.Time
|
||||
State State
|
||||
ConsecutiveFailures int32
|
||||
}
|
||||
|
||||
// IsHealthy returns true if the circuit breaker is in a healthy state
|
||||
|
||||
@@ -28,8 +28,8 @@ type mockBackend struct {
|
||||
}
|
||||
|
||||
type mockEntry struct {
|
||||
value []byte
|
||||
expiresAt time.Time
|
||||
value []byte
|
||||
}
|
||||
|
||||
func newMockBackend() *mockBackend {
|
||||
|
||||
+28
-49
@@ -41,26 +41,13 @@ func (h HealthStatus) String() string {
|
||||
|
||||
// HealthCheckConfig holds configuration for the health checker
|
||||
type HealthCheckConfig struct {
|
||||
// CheckInterval is how often to check health
|
||||
CheckInterval time.Duration
|
||||
|
||||
// Timeout is the timeout for each health check
|
||||
Timeout time.Duration
|
||||
|
||||
// HealthyThreshold is the number of consecutive successes to become healthy
|
||||
HealthyThreshold int
|
||||
|
||||
// UnhealthyThreshold is the number of consecutive failures to become unhealthy
|
||||
OnStatusChange func(from, to HealthStatus)
|
||||
CheckFunc func(ctx context.Context) error
|
||||
CheckInterval time.Duration
|
||||
Timeout time.Duration
|
||||
HealthyThreshold int
|
||||
UnhealthyThreshold int
|
||||
|
||||
// DegradedThreshold is the latency threshold in ms to mark as degraded
|
||||
DegradedThreshold time.Duration
|
||||
|
||||
// OnStatusChange is called when health status changes
|
||||
OnStatusChange func(from, to HealthStatus)
|
||||
|
||||
// CheckFunc is the function to check health
|
||||
CheckFunc func(ctx context.Context) error
|
||||
DegradedThreshold time.Duration
|
||||
}
|
||||
|
||||
// DefaultHealthCheckConfig returns default configuration
|
||||
@@ -76,31 +63,23 @@ func DefaultHealthCheckConfig() *HealthCheckConfig {
|
||||
|
||||
// HealthChecker monitors the health of a backend
|
||||
type HealthChecker struct {
|
||||
config *HealthCheckConfig
|
||||
|
||||
// Status tracking
|
||||
status atomic.Int32
|
||||
consecutiveSuccesses atomic.Int32
|
||||
lastCheckTime time.Time
|
||||
lastSuccessTime time.Time
|
||||
lastFailureTime time.Time
|
||||
config *HealthCheckConfig
|
||||
stopChan chan struct{}
|
||||
ticker *time.Ticker
|
||||
wg sync.WaitGroup
|
||||
statusChanges atomic.Int64
|
||||
totalChecks atomic.Int64
|
||||
totalSuccesses atomic.Int64
|
||||
totalFailures atomic.Int64
|
||||
averageLatency atomic.Int64
|
||||
timeMu sync.RWMutex
|
||||
consecutiveFailures atomic.Int32
|
||||
|
||||
// Timing
|
||||
lastCheckTime time.Time
|
||||
lastSuccessTime time.Time
|
||||
lastFailureTime time.Time
|
||||
averageLatency atomic.Int64
|
||||
timeMu sync.RWMutex
|
||||
|
||||
// Metrics
|
||||
totalChecks atomic.Int64
|
||||
totalSuccesses atomic.Int64
|
||||
totalFailures atomic.Int64
|
||||
statusChanges atomic.Int64
|
||||
|
||||
// Lifecycle
|
||||
ticker *time.Ticker
|
||||
stopChan chan struct{}
|
||||
stopped atomic.Bool
|
||||
wg sync.WaitGroup
|
||||
consecutiveSuccesses atomic.Int32
|
||||
stopped atomic.Bool
|
||||
status atomic.Int32
|
||||
}
|
||||
|
||||
// NewHealthChecker creates a new health checker
|
||||
@@ -342,19 +321,19 @@ func (hc *HealthChecker) Stats() HealthCheckerStats {
|
||||
|
||||
// HealthCheckerStats holds statistics for the health checker
|
||||
type HealthCheckerStats struct {
|
||||
Status HealthStatus
|
||||
ConsecutiveSuccesses int32
|
||||
ConsecutiveFailures int32
|
||||
LastCheckTime time.Time
|
||||
LastFailureTime time.Time
|
||||
LastSuccessTime time.Time
|
||||
TotalChecks int64
|
||||
TotalSuccesses int64
|
||||
TotalFailures int64
|
||||
SuccessRate float64
|
||||
AverageLatency time.Duration
|
||||
StatusChanges int64
|
||||
LastCheckTime time.Time
|
||||
LastSuccessTime time.Time
|
||||
LastFailureTime time.Time
|
||||
HealthScore float64
|
||||
Status HealthStatus
|
||||
ConsecutiveFailures int32
|
||||
ConsecutiveSuccesses int32
|
||||
}
|
||||
|
||||
// Reset resets the health checker statistics
|
||||
|
||||
+7
-11
@@ -12,20 +12,16 @@ import (
|
||||
|
||||
// HealthCheckBackend wraps a cache backend with health checking
|
||||
type HealthCheckBackend struct {
|
||||
backend backends.CacheBackend
|
||||
config *HealthCheckConfig
|
||||
|
||||
// Health tracking
|
||||
lastCheck time.Time
|
||||
backend backends.CacheBackend
|
||||
ctx context.Context
|
||||
config *HealthCheckConfig
|
||||
cancel context.CancelFunc
|
||||
wg sync.WaitGroup
|
||||
checkMutex sync.RWMutex
|
||||
status atomic.Int32
|
||||
consecutiveFails atomic.Int32
|
||||
consecutiveOK atomic.Int32
|
||||
lastCheck time.Time
|
||||
checkMutex sync.RWMutex
|
||||
|
||||
// Lifecycle
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
// NewHealthCheckBackend creates a new health check wrapped backend
|
||||
|
||||
Vendored
+2
-2
@@ -292,12 +292,12 @@ type SessionCache struct {
|
||||
|
||||
// SessionData represents session information
|
||||
type SessionData struct {
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
Claims map[string]interface{} `json:"claims"`
|
||||
ID string `json:"id"`
|
||||
UserID string `json:"user_id"`
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
Claims map[string]interface{} `json:"claims"`
|
||||
}
|
||||
|
||||
// NewSessionCache creates a new session cache
|
||||
|
||||
@@ -11,10 +11,10 @@ import (
|
||||
|
||||
// Mock logger for testing
|
||||
type mockLogger struct {
|
||||
mu sync.Mutex
|
||||
logs []string
|
||||
errLogs []string
|
||||
debugLog []string
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (m *mockLogger) Logf(format string, args ...interface{}) {
|
||||
|
||||
+11
-11
@@ -19,20 +19,20 @@ type Logger interface {
|
||||
|
||||
// BackgroundTask represents a recurring background task
|
||||
type BackgroundTask struct {
|
||||
name string
|
||||
interval time.Duration
|
||||
taskFunc func()
|
||||
lastRun time.Time
|
||||
logger Logger
|
||||
ctx context.Context
|
||||
ticker *time.Ticker
|
||||
stopChan chan bool
|
||||
isRunning int32
|
||||
logger Logger
|
||||
waitGroup *sync.WaitGroup
|
||||
lastRun time.Time
|
||||
taskFunc func()
|
||||
cancelFunc context.CancelFunc
|
||||
name string
|
||||
runCount int64
|
||||
errorCount int64
|
||||
interval time.Duration
|
||||
mu sync.RWMutex
|
||||
ctx context.Context
|
||||
cancelFunc context.CancelFunc
|
||||
isRunning int32
|
||||
}
|
||||
|
||||
// NewBackgroundTask creates a new background task
|
||||
@@ -183,11 +183,11 @@ func (bt *BackgroundTask) IsRunning() bool {
|
||||
|
||||
// TaskRegistry manages all background tasks
|
||||
type TaskRegistry struct {
|
||||
tasks map[string]*BackgroundTask
|
||||
mu sync.RWMutex
|
||||
logger Logger
|
||||
maxTasks int
|
||||
tasks map[string]*BackgroundTask
|
||||
circuitBreaker *TaskCircuitBreaker
|
||||
maxTasks int
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// globalTaskRegistry is the singleton task registry
|
||||
|
||||
+13
-13
@@ -11,14 +11,14 @@ import (
|
||||
|
||||
// TaskCircuitBreaker prevents task creation failures from cascading
|
||||
type TaskCircuitBreaker struct {
|
||||
lastFailureTime time.Time
|
||||
logger Logger
|
||||
taskFailures map[string]int32
|
||||
timeout time.Duration
|
||||
mu sync.RWMutex
|
||||
failureThreshold int32
|
||||
failureCount int32
|
||||
lastFailureTime time.Time
|
||||
timeout time.Duration
|
||||
state int32 // 0: closed, 1: open
|
||||
logger Logger
|
||||
mu sync.RWMutex
|
||||
taskFailures map[string]int32
|
||||
state int32
|
||||
}
|
||||
|
||||
// CircuitBreakerState represents the state of the circuit breaker
|
||||
@@ -140,14 +140,14 @@ func (cb *TaskCircuitBreaker) GetState() CircuitBreakerState {
|
||||
|
||||
// TaskMemoryMonitor monitors memory usage and can trigger cleanup
|
||||
type TaskMemoryMonitor struct {
|
||||
lastCheck time.Time
|
||||
logger Logger
|
||||
registry *TaskRegistry
|
||||
stopChan chan bool
|
||||
memoryThreshold uint64
|
||||
checkInterval time.Duration
|
||||
isMonitoring int32
|
||||
stopChan chan bool
|
||||
lastCheck time.Time
|
||||
mu sync.RWMutex
|
||||
isMonitoring int32
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -310,13 +310,13 @@ func (tmm *TaskMemoryMonitor) GetStats() map[string]interface{} {
|
||||
|
||||
// WorkerPool manages a pool of worker goroutines for task execution
|
||||
type WorkerPool struct {
|
||||
workers int
|
||||
taskQueue chan func()
|
||||
workerWg sync.WaitGroup
|
||||
isRunning int32
|
||||
logger Logger
|
||||
taskQueue chan func()
|
||||
stopChan chan bool
|
||||
metrics WorkerPoolMetrics
|
||||
workerWg sync.WaitGroup
|
||||
workers int
|
||||
isRunning int32
|
||||
}
|
||||
|
||||
// WorkerPoolMetrics tracks worker pool performance
|
||||
|
||||
@@ -12,9 +12,9 @@ import (
|
||||
type FeatureFlag struct {
|
||||
name string
|
||||
description string
|
||||
enabled atomic.Bool
|
||||
mu sync.RWMutex
|
||||
callbacks []func(bool)
|
||||
mu sync.RWMutex
|
||||
enabled atomic.Bool
|
||||
}
|
||||
|
||||
// FeatureManager manages all feature flags in the application
|
||||
|
||||
+19
-28
@@ -14,50 +14,41 @@ import (
|
||||
// and resource leaks. It provides centralized management of HTTP client transports with
|
||||
// proper lifecycle management and security controls.
|
||||
type TransportPool struct {
|
||||
mu sync.RWMutex
|
||||
transports map[string]*sharedTransport
|
||||
maxConns int
|
||||
ctx context.Context
|
||||
transports map[string]*sharedTransport
|
||||
cancel context.CancelFunc
|
||||
clientCount int32 // Track total HTTP clients
|
||||
maxClients int32 // Limit total clients
|
||||
maxConns int
|
||||
mu sync.RWMutex
|
||||
clientCount int32
|
||||
maxClients int32
|
||||
}
|
||||
|
||||
// sharedTransport wraps an HTTP transport with reference counting
|
||||
type sharedTransport struct {
|
||||
transport *http.Transport
|
||||
refCount int32
|
||||
lastUsed time.Time
|
||||
transport *http.Transport
|
||||
config TransportConfig
|
||||
refCount int32
|
||||
}
|
||||
|
||||
// TransportConfig defines configuration for HTTP transports
|
||||
type TransportConfig struct {
|
||||
// Timeouts
|
||||
DialTimeout time.Duration
|
||||
TLSHandshakeTimeout time.Duration
|
||||
MaxConnsPerHost int
|
||||
WriteBufferSize int
|
||||
ResponseHeaderTimeout time.Duration
|
||||
ExpectContinueTimeout time.Duration
|
||||
IdleConnTimeout time.Duration
|
||||
KeepAlive time.Duration
|
||||
|
||||
// Connection limits
|
||||
MaxIdleConns int
|
||||
MaxIdleConnsPerHost int
|
||||
MaxConnsPerHost int
|
||||
|
||||
// Features
|
||||
ForceHTTP2 bool
|
||||
DisableKeepAlives bool
|
||||
DisableCompression bool
|
||||
|
||||
// Buffer sizes
|
||||
WriteBufferSize int
|
||||
ReadBufferSize int
|
||||
|
||||
// TLS
|
||||
InsecureSkipVerify bool
|
||||
MinTLSVersion uint16
|
||||
TLSHandshakeTimeout time.Duration
|
||||
MaxIdleConns int
|
||||
DialTimeout time.Duration
|
||||
MaxIdleConnsPerHost int
|
||||
ReadBufferSize int
|
||||
MinTLSVersion uint16
|
||||
ForceHTTP2 bool
|
||||
DisableCompression bool
|
||||
InsecureSkipVerify bool
|
||||
DisableKeepAlives bool
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -154,10 +154,10 @@ func TestAzureProvider_ValidateTokens(t *testing.T) {
|
||||
provider := NewAzureProvider()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
session *mockSession
|
||||
verifierError error
|
||||
session *mockSession
|
||||
cacheData map[string]interface{}
|
||||
name string
|
||||
expectedResult ValidationResult
|
||||
}{
|
||||
{
|
||||
@@ -369,9 +369,9 @@ func TestAzureProvider_OfflineAccessHandling(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
inputScopes []string
|
||||
expectedCount int // Expected number of offline_access scopes (should be 1)
|
||||
description string
|
||||
inputScopes []string
|
||||
expectedCount int
|
||||
}{
|
||||
{
|
||||
name: "No offline_access - should add one",
|
||||
|
||||
@@ -8,10 +8,10 @@ import (
|
||||
|
||||
// Mock implementations for testing
|
||||
type mockSession struct {
|
||||
authenticated bool
|
||||
idToken string
|
||||
accessToken string
|
||||
refreshToken string
|
||||
authenticated bool
|
||||
}
|
||||
|
||||
func (s *mockSession) GetIDToken() string { return s.idToken }
|
||||
@@ -338,10 +338,10 @@ func TestBaseProvider_ValidateTokenExpiry(t *testing.T) {
|
||||
gracePeriod := 5 * time.Minute
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
claims map[string]interface{}
|
||||
cacheFound bool
|
||||
name string
|
||||
expectedResult ValidationResult
|
||||
cacheFound bool
|
||||
}{
|
||||
{
|
||||
name: "Token not found in cache, has refresh token",
|
||||
@@ -438,10 +438,10 @@ func TestBaseProvider_ValidateTokenExpiry_NoRefreshToken(t *testing.T) {
|
||||
gracePeriod := 5 * time.Minute
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
claims map[string]interface{}
|
||||
cacheFound bool
|
||||
name string
|
||||
expectedResult ValidationResult
|
||||
cacheFound bool
|
||||
}{
|
||||
{
|
||||
name: "Token not found in cache, no refresh token",
|
||||
|
||||
@@ -25,9 +25,9 @@ func TestProviderFactory_CreateProvider(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
issuerURL string
|
||||
errMsg string
|
||||
expectedType ProviderType
|
||||
wantErr bool
|
||||
errMsg string
|
||||
}{
|
||||
{
|
||||
name: "Google provider",
|
||||
@@ -158,10 +158,10 @@ func TestProviderFactory_CreateProviderByType(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
errMsg string
|
||||
providerType ProviderType
|
||||
expectedType ProviderType
|
||||
wantErr bool
|
||||
errMsg string
|
||||
}{
|
||||
{
|
||||
name: "Generic provider",
|
||||
|
||||
@@ -136,9 +136,9 @@ func TestGenericProvider_ValidateTokens(t *testing.T) {
|
||||
provider := NewGenericProvider()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
session *mockSession
|
||||
verifierError error
|
||||
session *mockSession
|
||||
name string
|
||||
expectedResult ValidationResult
|
||||
}{
|
||||
{
|
||||
|
||||
@@ -172,8 +172,8 @@ func TestGoogleProvider_OfflineAccessFiltering(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
inputScopes []string
|
||||
description string
|
||||
inputScopes []string
|
||||
}{
|
||||
{
|
||||
name: "Multiple offline_access occurrences",
|
||||
|
||||
@@ -82,9 +82,9 @@ func TestProviderRegistry_GetProviderByType(t *testing.T) {
|
||||
registry.RegisterProvider(googleProvider)
|
||||
|
||||
tests := []struct {
|
||||
expected OIDCProvider
|
||||
name string
|
||||
providerType ProviderType
|
||||
expected OIDCProvider
|
||||
}{
|
||||
{
|
||||
name: "Get Generic provider",
|
||||
@@ -180,9 +180,9 @@ func TestProviderRegistry_DetectProvider(t *testing.T) {
|
||||
registry.RegisterProvider(gitlabProvider)
|
||||
|
||||
tests := []struct {
|
||||
expected OIDCProvider
|
||||
name string
|
||||
issuerURL string
|
||||
expected OIDCProvider
|
||||
}{
|
||||
{
|
||||
name: "Google provider detection",
|
||||
@@ -640,9 +640,9 @@ func TestProviderRegistry_GitLabDetection_RealWorldURLs(t *testing.T) {
|
||||
registry.RegisterProvider(githubProvider)
|
||||
|
||||
realWorldTests := []struct {
|
||||
expected OIDCProvider
|
||||
name string
|
||||
issuerURL string
|
||||
expected OIDCProvider
|
||||
}{
|
||||
// Actual self-hosted GitLab examples from issue #61
|
||||
{
|
||||
|
||||
@@ -20,8 +20,8 @@ func TestValidateIssuerURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
issuerURL string
|
||||
wantErr bool
|
||||
errMsg string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid https URL",
|
||||
@@ -106,8 +106,8 @@ func TestValidateClientID(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
clientID string
|
||||
wantErr bool
|
||||
errMsg string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid client ID",
|
||||
@@ -173,9 +173,9 @@ func TestValidateClientID(t *testing.T) {
|
||||
func TestValidateScopes(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
errMsg string
|
||||
scopes []string
|
||||
wantErr bool
|
||||
errMsg string
|
||||
}{
|
||||
{
|
||||
name: "valid scopes with openid",
|
||||
@@ -248,8 +248,8 @@ func TestValidateRedirectURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
redirectURL string
|
||||
wantErr bool
|
||||
errMsg string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid https redirect URL",
|
||||
@@ -315,11 +315,11 @@ func TestValidateRedirectURL(t *testing.T) {
|
||||
// TestValidateProviderSpecificConfig tests provider-specific configuration validation
|
||||
func TestValidateProviderSpecificConfig(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
provider OIDCProvider
|
||||
config map[string]interface{}
|
||||
wantErr bool
|
||||
name string
|
||||
errMsg string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid Google config",
|
||||
@@ -458,8 +458,8 @@ func TestValidateGoogleConfig_EdgeCases(t *testing.T) {
|
||||
googleProvider := NewGoogleProvider()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
config map[string]interface{}
|
||||
name string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
@@ -502,10 +502,10 @@ func TestValidateAzureConfig_EdgeCases(t *testing.T) {
|
||||
azureProvider := NewAzureProvider()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
config map[string]interface{}
|
||||
wantErr bool
|
||||
name string
|
||||
errMsg string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid tenant ID format",
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
|
||||
// ProviderWarning represents a warning about provider limitations or requirements.
|
||||
type ProviderWarning struct {
|
||||
ProviderType ProviderType
|
||||
Level string // "info", "warning", "error"
|
||||
Level string
|
||||
Message string
|
||||
ProviderType ProviderType
|
||||
}
|
||||
|
||||
// GetProviderWarnings returns warnings about provider-specific limitations.
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
func TestGetProviderWarnings(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
checkContent string
|
||||
providerType ProviderType
|
||||
expectCount int
|
||||
checkContent string
|
||||
}{
|
||||
{
|
||||
name: "GitHub has OAuth 2.0 warning",
|
||||
|
||||
@@ -34,21 +34,15 @@ type Logger interface {
|
||||
// for all recovery mechanism implementations. It handles request counting,
|
||||
// success/failure tracking, and timestamp management in a thread-safe manner.
|
||||
type BaseRecoveryMechanism struct {
|
||||
// name identifies the recovery mechanism instance
|
||||
name string
|
||||
// logger provides structured logging capabilities
|
||||
logger Logger
|
||||
|
||||
// Metrics tracked with atomic operations for thread safety
|
||||
logger Logger
|
||||
name string
|
||||
lastSuccessStr string
|
||||
lastFailureStr string
|
||||
totalRequests int64
|
||||
successCount int64
|
||||
failureCount int64
|
||||
lastSuccessStr string
|
||||
lastFailureStr string
|
||||
|
||||
// mutexes for thread-safe timestamp updates
|
||||
successMutex sync.RWMutex
|
||||
failureMutex sync.RWMutex
|
||||
successMutex sync.RWMutex
|
||||
failureMutex sync.RWMutex
|
||||
}
|
||||
|
||||
// NewBaseRecoveryMechanism creates a new base recovery mechanism with the given name and logger.
|
||||
@@ -182,10 +176,10 @@ const (
|
||||
|
||||
// HTTPError represents an HTTP error with status code and message
|
||||
type HTTPError struct {
|
||||
StatusCode int
|
||||
Headers map[string]string
|
||||
Message string
|
||||
Body []byte
|
||||
Headers map[string]string
|
||||
StatusCode int
|
||||
}
|
||||
|
||||
// Error implements the error interface
|
||||
|
||||
@@ -60,20 +60,14 @@ func DefaultCircuitBreakerConfig() CircuitBreakerConfig {
|
||||
// CircuitBreaker implements the circuit breaker pattern for fault tolerance.
|
||||
// It prevents cascading failures by temporarily blocking requests to a failing service.
|
||||
type CircuitBreaker struct {
|
||||
*BaseRecoveryMechanism
|
||||
config CircuitBreakerConfig
|
||||
|
||||
// State management
|
||||
state int32 // atomic: CircuitBreakerState
|
||||
lastStateChange time.Time
|
||||
stateMutex sync.RWMutex
|
||||
|
||||
// Failure tracking
|
||||
consecutiveFailures int32 // atomic
|
||||
consecutiveSuccesses int32 // atomic
|
||||
|
||||
// Half-open state management
|
||||
halfOpenRequests int32 // atomic
|
||||
*BaseRecoveryMechanism
|
||||
config CircuitBreakerConfig
|
||||
stateMutex sync.RWMutex
|
||||
state int32
|
||||
consecutiveFailures int32
|
||||
consecutiveSuccesses int32
|
||||
halfOpenRequests int32
|
||||
}
|
||||
|
||||
// NewCircuitBreaker creates a new circuit breaker with the given configuration
|
||||
|
||||
@@ -15,20 +15,13 @@ import (
|
||||
|
||||
// RetryConfig defines configuration for the retry executor
|
||||
type RetryConfig struct {
|
||||
// MaxAttempts is the maximum number of retry attempts
|
||||
MaxAttempts int
|
||||
// InitialDelay is the initial delay between retries
|
||||
InitialDelay time.Duration
|
||||
// MaxDelay is the maximum delay between retries
|
||||
MaxDelay time.Duration
|
||||
// Multiplier is the backoff multiplier
|
||||
Multiplier float64
|
||||
// RandomizationFactor adds jitter to delays (0.0 to 1.0)
|
||||
RandomizationFactor float64
|
||||
// RetryableErrors defines which errors should trigger a retry
|
||||
RetryableErrors []string
|
||||
// RetryableStatusCodes defines which HTTP status codes should trigger a retry
|
||||
RetryableErrors []string
|
||||
RetryableStatusCodes []int
|
||||
MaxAttempts int
|
||||
InitialDelay time.Duration
|
||||
MaxDelay time.Duration
|
||||
Multiplier float64
|
||||
RandomizationFactor float64
|
||||
}
|
||||
|
||||
// DefaultRetryConfig returns sensible default retry configuration
|
||||
@@ -46,13 +39,11 @@ func DefaultRetryConfig() RetryConfig {
|
||||
|
||||
// RetryExecutor implements retry logic with exponential backoff
|
||||
type RetryExecutor struct {
|
||||
lastRetryTime time.Time
|
||||
*BaseRecoveryMechanism
|
||||
config RetryConfig
|
||||
|
||||
// Metrics
|
||||
config RetryConfig
|
||||
totalRetries int64
|
||||
maxRetriesHit int64
|
||||
lastRetryTime time.Time
|
||||
retryTimeMutex sync.RWMutex
|
||||
}
|
||||
|
||||
|
||||
@@ -273,17 +273,17 @@ func TestRetryExecutor_isRetryableError(t *testing.T) {
|
||||
executor := NewRetryExecutor(config, logger)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
err error
|
||||
name string
|
||||
expected bool
|
||||
}{
|
||||
{"nil error", nil, false},
|
||||
{"connection refused", errors.New("connection refused"), true},
|
||||
{"timeout", errors.New("TIMEOUT"), true}, // case insensitive
|
||||
{"EOF", errors.New("EOF"), false},
|
||||
{"random error", errors.New("something else"), false},
|
||||
{"context cancelled", context.Canceled, false},
|
||||
{"context deadline exceeded", context.DeadlineExceeded, false},
|
||||
{name: "nil error", err: nil, expected: false},
|
||||
{name: "connection refused", err: errors.New("connection refused"), expected: true},
|
||||
{name: "timeout", err: errors.New("TIMEOUT"), expected: true}, // case insensitive
|
||||
{name: "EOF", err: errors.New("EOF"), expected: false},
|
||||
{name: "random error", err: errors.New("something else"), expected: false},
|
||||
{name: "context cancelled", err: context.Canceled, expected: false},
|
||||
{name: "context deadline exceeded", err: context.DeadlineExceeded, expected: false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -13,10 +13,10 @@ import (
|
||||
|
||||
// Mock logger for testing
|
||||
type mockLogger struct {
|
||||
mu sync.Mutex
|
||||
logs []string
|
||||
errLogs []string
|
||||
debugLog []string
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (m *mockLogger) Logf(format string, args ...interface{}) {
|
||||
@@ -202,13 +202,13 @@ func TestBaseRecoveryMechanism_ConcurrentAccess(t *testing.T) {
|
||||
// CircuitBreakerState tests
|
||||
func TestCircuitBreakerState_String(t *testing.T) {
|
||||
tests := []struct {
|
||||
state CircuitBreakerState
|
||||
expected string
|
||||
state CircuitBreakerState
|
||||
}{
|
||||
{CircuitBreakerClosed, "closed"},
|
||||
{CircuitBreakerOpen, "open"},
|
||||
{CircuitBreakerHalfOpen, "half-open"},
|
||||
{CircuitBreakerState(99), "unknown"},
|
||||
{state: CircuitBreakerClosed, expected: "closed"},
|
||||
{state: CircuitBreakerOpen, expected: "open"},
|
||||
{state: CircuitBreakerHalfOpen, expected: "half-open"},
|
||||
{state: CircuitBreakerState(99), expected: "unknown"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -29,26 +29,26 @@ type JWK struct {
|
||||
type TokenResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
TokenType string `json:"token_type"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
RefreshToken string `json:"refresh_token,omitempty"`
|
||||
IDToken string `json:"id_token,omitempty"`
|
||||
Scope string `json:"scope,omitempty"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
}
|
||||
|
||||
// IntrospectionResponse represents a token introspection response
|
||||
type IntrospectionResponse struct {
|
||||
Active bool `json:"active"`
|
||||
Scope string `json:"scope,omitempty"`
|
||||
ClientID string `json:"client_id,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
TokenType string `json:"token_type,omitempty"`
|
||||
Exp int64 `json:"exp,omitempty"`
|
||||
Iat int64 `json:"iat,omitempty"`
|
||||
Nbf int64 `json:"nbf,omitempty"`
|
||||
Sub string `json:"sub,omitempty"`
|
||||
Aud string `json:"aud,omitempty"`
|
||||
Iss string `json:"iss,omitempty"`
|
||||
Jti string `json:"jti,omitempty"`
|
||||
Exp int64 `json:"exp,omitempty"`
|
||||
Iat int64 `json:"iat,omitempty"`
|
||||
Nbf int64 `json:"nbf,omitempty"`
|
||||
Active bool `json:"active"`
|
||||
}
|
||||
|
||||
// JWKCache is a testify mock for JWK caching operations
|
||||
|
||||
@@ -8,16 +8,16 @@ import (
|
||||
|
||||
// SessionData represents session data for testing
|
||||
type SessionData struct {
|
||||
Claims map[string]interface{}
|
||||
Email string
|
||||
AccessToken string
|
||||
RefreshToken string
|
||||
IDToken string
|
||||
Expiry int64
|
||||
Nonce string
|
||||
State string
|
||||
CodeVerifier string
|
||||
RedirectURL string
|
||||
Claims map[string]interface{}
|
||||
Expiry int64
|
||||
}
|
||||
|
||||
// SessionManager is a testify mock for session management
|
||||
|
||||
@@ -16,45 +16,30 @@ import (
|
||||
|
||||
// OIDCServerConfig configures the mock OIDC server behavior
|
||||
type OIDCServerConfig struct {
|
||||
// Identity
|
||||
Issuer string
|
||||
|
||||
// Discovery
|
||||
ScopesSupported []string
|
||||
ResponseTypesSupported []string
|
||||
JWKSResponse map[string]interface{}
|
||||
TokenFixture *fixtures.TokenFixture
|
||||
UserinfoError *OIDCError
|
||||
UserinfoResponse map[string]interface{}
|
||||
IntrospectionResponse map[string]interface{}
|
||||
JWKSError *OIDCError
|
||||
RefreshError *OIDCError
|
||||
TokenResponse map[string]interface{}
|
||||
TokenError *OIDCError
|
||||
IntrospectionError *OIDCError
|
||||
RefreshResponse map[string]interface{}
|
||||
Issuer string
|
||||
GrantTypesSupported []string
|
||||
ClaimsSupported []string
|
||||
TokenEndpointAuthMethods []string
|
||||
|
||||
// Token fixture for signing
|
||||
TokenFixture *fixtures.TokenFixture
|
||||
|
||||
// Token endpoint behavior
|
||||
TokenResponse map[string]interface{}
|
||||
TokenError *OIDCError
|
||||
TokenDelay time.Duration
|
||||
RefreshResponse map[string]interface{}
|
||||
RefreshError *OIDCError
|
||||
|
||||
// JWKS behavior
|
||||
JWKSResponse map[string]interface{}
|
||||
JWKSError *OIDCError
|
||||
JWKSDelay time.Duration
|
||||
|
||||
// Introspection behavior
|
||||
IntrospectionResponse map[string]interface{}
|
||||
IntrospectionError *OIDCError
|
||||
|
||||
// Userinfo behavior
|
||||
UserinfoResponse map[string]interface{}
|
||||
UserinfoError *OIDCError
|
||||
|
||||
// Simulation flags
|
||||
SimulateTimeout bool
|
||||
TimeoutDuration time.Duration
|
||||
RateLimitAfter int
|
||||
FailAfterN int
|
||||
FailWithStatus int
|
||||
ScopesSupported []string
|
||||
ClaimsSupported []string
|
||||
ResponseTypesSupported []string
|
||||
FailAfterN int
|
||||
JWKSDelay time.Duration
|
||||
TimeoutDuration time.Duration
|
||||
RateLimitAfter int
|
||||
TokenDelay time.Duration
|
||||
FailWithStatus int
|
||||
SimulateTimeout bool
|
||||
}
|
||||
|
||||
// OIDCError represents an OAuth error response
|
||||
@@ -67,9 +52,9 @@ type OIDCError struct {
|
||||
type OIDCServer struct {
|
||||
*httptest.Server
|
||||
Config *OIDCServerConfig
|
||||
RequestCount int32
|
||||
mu sync.Mutex
|
||||
requests []*http.Request
|
||||
mu sync.Mutex
|
||||
RequestCount int32
|
||||
}
|
||||
|
||||
// NewOIDCServer creates a new mock OIDC server
|
||||
|
||||
@@ -135,9 +135,9 @@ func TestIsTestMode(t *testing.T) {
|
||||
// We'll test what we can control via environment variables.
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
setup func()
|
||||
cleanup func()
|
||||
name string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
@@ -206,8 +206,8 @@ func TestIsTestMode(t *testing.T) {
|
||||
func TestIsTestModeEdgeCases(t *testing.T) {
|
||||
// Test with various environment variable combinations
|
||||
tests := []struct {
|
||||
name string
|
||||
env map[string]string
|
||||
name string
|
||||
}{
|
||||
{
|
||||
name: "all env vars empty",
|
||||
@@ -560,11 +560,11 @@ func TestIsTestModeYaegiCompiler(t *testing.T) {
|
||||
|
||||
// mockLogger is a simple mock implementation for testing
|
||||
type mockLogger struct {
|
||||
lastFormat string
|
||||
lastArgs []interface{}
|
||||
infoCalls int
|
||||
debugCalls int
|
||||
errorCalls int
|
||||
lastFormat string
|
||||
lastArgs []interface{}
|
||||
}
|
||||
|
||||
func (m *mockLogger) Infof(format string, args ...interface{}) {
|
||||
|
||||
Reference in New Issue
Block a user