mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-15 04:18:03 +00:00
fixup! Weekend fun with further optimisations.
This commit is contained in:
@@ -553,6 +553,12 @@ func (tr *TaskRegistry) CreateSingletonTask(name string, interval time.Duration,
|
||||
}
|
||||
return nil, fmt.Errorf("singleton task already exists: %s (requested: %s)", taskName, name)
|
||||
}
|
||||
if strings.Contains(taskName, "memory-monitor") && strings.Contains(name, "memory-monitor") {
|
||||
if tr.logger != nil {
|
||||
tr.logger.Debug("Singleton enforcement: memory-monitor task %s already exists, rejecting %s", taskName, name)
|
||||
}
|
||||
return nil, fmt.Errorf("singleton memory-monitor task already exists: %s (requested: %s)", taskName, name)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if exact task already exists
|
||||
|
||||
+50
-2
@@ -304,8 +304,25 @@ func (mm *MemoryMonitor) LogMemoryStats(stats *MemoryStats) {
|
||||
stats.SessionCount, stats.TaskCount, stats.CacheSize, stats.ConnectionPools)
|
||||
}
|
||||
|
||||
// StartMonitoring starts continuous memory monitoring
|
||||
// Global monitoring state
|
||||
var (
|
||||
globalMonitoringStarted bool
|
||||
globalMonitoringMutex sync.Mutex
|
||||
)
|
||||
|
||||
// StartMonitoring starts continuous memory monitoring as a global singleton
|
||||
func (mm *MemoryMonitor) StartMonitoring(ctx context.Context, interval time.Duration) {
|
||||
globalMonitoringMutex.Lock()
|
||||
defer globalMonitoringMutex.Unlock()
|
||||
|
||||
// Check if monitoring is already started
|
||||
if globalMonitoringStarted {
|
||||
if !isTestMode() {
|
||||
mm.logger.Debug("Memory monitoring already started, skipping duplicate start")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if interval <= 0 {
|
||||
interval = 30 * time.Second
|
||||
}
|
||||
@@ -329,9 +346,12 @@ func (mm *MemoryMonitor) StartMonitoring(ctx context.Context, interval time.Dura
|
||||
return
|
||||
}
|
||||
|
||||
// Only start if task was newly created or we're sure it's not already running
|
||||
task.Start()
|
||||
globalMonitoringStarted = true
|
||||
|
||||
if !isTestMode() {
|
||||
mm.logger.Info("Started memory monitoring with %v interval", interval)
|
||||
mm.logger.Info("Started global memory monitoring with %v interval", interval)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,6 +406,34 @@ func (mm *MemoryMonitor) GetMemoryPressure() MemoryPressureLevel {
|
||||
return MemoryPressureNone
|
||||
}
|
||||
|
||||
// StopMonitoring stops the global memory monitoring if it's running
|
||||
func (mm *MemoryMonitor) StopMonitoring() {
|
||||
globalMonitoringMutex.Lock()
|
||||
defer globalMonitoringMutex.Unlock()
|
||||
|
||||
if !globalMonitoringStarted {
|
||||
return
|
||||
}
|
||||
|
||||
registry := GetGlobalTaskRegistry()
|
||||
if task, exists := registry.GetTask("memory-monitor"); exists {
|
||||
task.Stop()
|
||||
globalMonitoringStarted = false
|
||||
if !isTestMode() {
|
||||
mm.logger.Info("Stopped global memory monitoring")
|
||||
}
|
||||
} else {
|
||||
mm.logger.Errorf("Failed to find memory monitoring task to stop")
|
||||
}
|
||||
}
|
||||
|
||||
// IsMonitoringActive returns true if global memory monitoring is currently active
|
||||
func (mm *MemoryMonitor) IsMonitoringActive() bool {
|
||||
globalMonitoringMutex.Lock()
|
||||
defer globalMonitoringMutex.Unlock()
|
||||
return globalMonitoringStarted
|
||||
}
|
||||
|
||||
// Global memory monitor instance
|
||||
var (
|
||||
globalMemoryMonitor *MemoryMonitor
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
package traefikoidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestMemoryMonitorSingleton(t *testing.T) {
|
||||
// Reset global state
|
||||
globalMonitoringMutex.Lock()
|
||||
globalMonitoringStarted = false
|
||||
globalMonitoringMutex.Unlock()
|
||||
|
||||
// Clear task registry for clean test
|
||||
registry := GetGlobalTaskRegistry()
|
||||
registry.StopAllTasks()
|
||||
|
||||
// Test that multiple StartMonitoring calls don't create multiple monitors
|
||||
monitor := GetGlobalMemoryMonitor()
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
goroutinesBefore := runtime.NumGoroutine()
|
||||
|
||||
// Call StartMonitoring multiple times (simulating multiple middleware instances)
|
||||
var wg sync.WaitGroup
|
||||
numCalls := 10
|
||||
|
||||
for i := 0; i < numCalls; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
monitor.StartMonitoring(ctx, 100*time.Millisecond)
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
// Give some time for goroutines to start
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
goroutinesAfter := runtime.NumGoroutine()
|
||||
|
||||
// Should only create one additional goroutine for the singleton task
|
||||
goroutineDiff := goroutinesAfter - goroutinesBefore
|
||||
if goroutineDiff > 2 { // Allow some tolerance for test runner goroutines
|
||||
t.Errorf("Expected at most 2 additional goroutines, got %d", goroutineDiff)
|
||||
}
|
||||
|
||||
// Check that monitoring is active
|
||||
if !monitor.IsMonitoringActive() {
|
||||
t.Error("Memory monitoring should be active")
|
||||
}
|
||||
|
||||
// Verify only one task is registered
|
||||
taskCount := registry.GetTaskCount()
|
||||
if taskCount != 1 {
|
||||
t.Errorf("Expected 1 task, got %d", taskCount)
|
||||
}
|
||||
|
||||
// Check that the task is specifically memory-monitor
|
||||
if task, exists := registry.GetTask("memory-monitor"); !exists {
|
||||
t.Error("memory-monitor task should exist")
|
||||
} else if task == nil {
|
||||
t.Error("memory-monitor task should not be nil")
|
||||
}
|
||||
|
||||
// Stop monitoring
|
||||
monitor.StopMonitoring()
|
||||
|
||||
// Verify monitoring is no longer active
|
||||
if monitor.IsMonitoringActive() {
|
||||
t.Error("Memory monitoring should be inactive after stopping")
|
||||
}
|
||||
|
||||
// Clean up
|
||||
registry.StopAllTasks()
|
||||
}
|
||||
|
||||
func TestMemoryMonitorSingletonTaskCreation(t *testing.T) {
|
||||
// Reset global state
|
||||
globalMonitoringMutex.Lock()
|
||||
globalMonitoringStarted = false
|
||||
globalMonitoringMutex.Unlock()
|
||||
|
||||
registry := GetGlobalTaskRegistry()
|
||||
registry.StopAllTasks()
|
||||
|
||||
monitor1 := GetGlobalMemoryMonitor()
|
||||
monitor2 := GetGlobalMemoryMonitor()
|
||||
|
||||
// Should be the same instance
|
||||
if monitor1 != monitor2 {
|
||||
t.Error("GetGlobalMemoryMonitor should return the same instance")
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// First start should succeed
|
||||
monitor1.StartMonitoring(ctx, 50*time.Millisecond)
|
||||
|
||||
if !monitor1.IsMonitoringActive() {
|
||||
t.Error("First StartMonitoring should activate monitoring")
|
||||
}
|
||||
|
||||
// Second start should be skipped
|
||||
monitor2.StartMonitoring(ctx, 50*time.Millisecond)
|
||||
|
||||
// Should still only have one task
|
||||
taskCount := registry.GetTaskCount()
|
||||
if taskCount != 1 {
|
||||
t.Errorf("Expected 1 task after duplicate start, got %d", taskCount)
|
||||
}
|
||||
|
||||
// Clean up
|
||||
monitor1.StopMonitoring()
|
||||
registry.StopAllTasks()
|
||||
}
|
||||
|
||||
func TestMemoryMonitorTaskRegistryEnforcement(t *testing.T) {
|
||||
// Reset global state
|
||||
globalMonitoringMutex.Lock()
|
||||
globalMonitoringStarted = false
|
||||
globalMonitoringMutex.Unlock()
|
||||
|
||||
registry := GetGlobalTaskRegistry()
|
||||
registry.StopAllTasks()
|
||||
|
||||
// Try to create memory-monitor task directly through registry
|
||||
task1, err1 := registry.CreateSingletonTask("memory-monitor", 50*time.Millisecond,
|
||||
func() {}, GetSingletonNoOpLogger(), nil)
|
||||
|
||||
if err1 != nil {
|
||||
t.Fatalf("First memory-monitor task creation should succeed: %v", err1)
|
||||
}
|
||||
|
||||
// Try to create another memory-monitor task - should fail
|
||||
task2, err2 := registry.CreateSingletonTask("memory-monitor-2", 50*time.Millisecond,
|
||||
func() {}, GetSingletonNoOpLogger(), nil)
|
||||
|
||||
if err2 == nil {
|
||||
t.Error("Second memory-monitor task creation should fail due to singleton enforcement")
|
||||
if task2 != nil {
|
||||
task2.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up
|
||||
if task1 != nil {
|
||||
task1.Stop()
|
||||
}
|
||||
registry.StopAllTasks()
|
||||
}
|
||||
Reference in New Issue
Block a user