mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-20 04:57:02 +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)
|
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
|
// 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)
|
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) {
|
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 {
|
if interval <= 0 {
|
||||||
interval = 30 * time.Second
|
interval = 30 * time.Second
|
||||||
}
|
}
|
||||||
@@ -329,9 +346,12 @@ func (mm *MemoryMonitor) StartMonitoring(ctx context.Context, interval time.Dura
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only start if task was newly created or we're sure it's not already running
|
||||||
task.Start()
|
task.Start()
|
||||||
|
globalMonitoringStarted = true
|
||||||
|
|
||||||
if !isTestMode() {
|
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
|
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
|
// Global memory monitor instance
|
||||||
var (
|
var (
|
||||||
globalMemoryMonitor *MemoryMonitor
|
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