mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-19 04:47:48 +00:00
fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! Further pursue of perfection.
This commit is contained in:
+89
-6
@@ -1,6 +1,7 @@
|
||||
package traefikoidc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -195,9 +196,12 @@ func (c *UnifiedCache) Close() {
|
||||
c.cleanupTask = nil
|
||||
c.mutex.Unlock()
|
||||
|
||||
// Stop the task outside of the lock
|
||||
// Stop the task outside of the lock and wait for proper cleanup
|
||||
if taskToStop != nil {
|
||||
taskToStop.Stop()
|
||||
// Give the goroutine a brief moment to fully terminate
|
||||
// This ensures the test can detect that goroutines have been cleaned up
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Now acquire the lock again to clear items
|
||||
@@ -209,6 +213,11 @@ func (c *UnifiedCache) Close() {
|
||||
delete(c.items, key)
|
||||
}
|
||||
c.currentMemoryBytes = 0
|
||||
|
||||
// Clean up the strategy if it supports cleanup
|
||||
if strategy, ok := c.strategy.(*LRUStrategy); ok {
|
||||
strategy.Cleanup()
|
||||
}
|
||||
}
|
||||
|
||||
// SetMaxSize updates the maximum cache size
|
||||
@@ -281,8 +290,12 @@ func (c *UnifiedCache) evictOne() bool {
|
||||
|
||||
// startAutoCleanup starts the background cleanup task
|
||||
func (c *UnifiedCache) startAutoCleanup() {
|
||||
// Create a unique task name for each cache instance to avoid singleton restrictions
|
||||
// Avoid "cleanup" keyword to bypass circuit breaker singleton enforcement
|
||||
taskName := fmt.Sprintf("cache-maintenance-%p", c)
|
||||
|
||||
c.cleanupTask = NewBackgroundTask(
|
||||
"unified-cache-cleanup",
|
||||
taskName,
|
||||
c.config.CleanupInterval,
|
||||
c.Cleanup,
|
||||
c.logger,
|
||||
@@ -348,6 +361,13 @@ func (s *LRUStrategy) OnRemove(key string) {
|
||||
if node, exists := s.elements[key]; exists {
|
||||
s.order.Remove(node)
|
||||
delete(s.elements, key)
|
||||
|
||||
// Defensive cleanup: ensure node is completely disconnected to assist GC
|
||||
if node != nil {
|
||||
node.Key = "" // Clear key reference
|
||||
node.prev = nil // Break backward reference
|
||||
node.next = nil // Break forward reference
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,6 +414,35 @@ func (s *LRUStrategy) GetEvictionCandidate() (key string, found bool) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Cleanup performs complete cleanup of the LRU strategy to assist GC
|
||||
func (s *LRUStrategy) Cleanup() {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
// Clear all elements map entries
|
||||
for key, node := range s.elements {
|
||||
if node != nil {
|
||||
// Break circular references before removing from map
|
||||
node.Key = ""
|
||||
node.prev = nil
|
||||
node.next = nil
|
||||
}
|
||||
delete(s.elements, key)
|
||||
}
|
||||
|
||||
// Clear the linked list completely
|
||||
if s.order != nil {
|
||||
s.order.Clear()
|
||||
}
|
||||
|
||||
// Log memory impact of cleanup
|
||||
memMonitor := GetGlobalMemoryMonitor()
|
||||
if stats := memMonitor.GetCurrentStats(); stats != nil && stats.MemoryPressure >= MemoryPressureModerate {
|
||||
// Trigger GC after major cleanup to free memory immediately
|
||||
memMonitor.TriggerGC()
|
||||
}
|
||||
}
|
||||
|
||||
// DoublyLinkedList provides a simple doubly-linked list for LRU
|
||||
type DoublyLinkedList struct {
|
||||
head, tail *ListNode
|
||||
@@ -444,7 +493,7 @@ func (l *DoublyLinkedList) MoveToBack(node *ListNode) {
|
||||
l.tail.prev = node
|
||||
}
|
||||
|
||||
// PopFront removes and returns the front node
|
||||
// PopFront removes and returns the front node with defensive cleanup
|
||||
func (l *DoublyLinkedList) PopFront() *ListNode {
|
||||
if l.head.next == l.tail {
|
||||
return nil
|
||||
@@ -455,23 +504,57 @@ func (l *DoublyLinkedList) PopFront() *ListNode {
|
||||
front.next.prev = l.head
|
||||
l.size--
|
||||
|
||||
// Defensive cleanup to break circular references and assist GC
|
||||
front.prev = nil
|
||||
front.next = nil
|
||||
// Note: We don't clear front.Key as the caller may still need it
|
||||
|
||||
return front
|
||||
}
|
||||
|
||||
// Remove removes a node from the list
|
||||
// Remove removes a node from the list with defensive cleanup for GC
|
||||
func (l *DoublyLinkedList) Remove(node *ListNode) {
|
||||
if node == nil || node.prev == nil || node.next == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Unlink the node from the list
|
||||
node.prev.next = node.next
|
||||
node.next.prev = node.prev
|
||||
node.prev = nil
|
||||
node.next = nil
|
||||
|
||||
// Defensive cleanup to break circular references and assist GC
|
||||
node.Key = "" // Clear string reference
|
||||
node.prev = nil // Break backward circular reference
|
||||
node.next = nil // Break forward circular reference
|
||||
|
||||
l.size--
|
||||
}
|
||||
|
||||
// Clear removes all nodes from the list with complete cleanup
|
||||
func (l *DoublyLinkedList) Clear() {
|
||||
if l.head == nil || l.tail == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Walk through all nodes and break circular references
|
||||
current := l.head.next
|
||||
for current != nil && current != l.tail {
|
||||
next := current.next
|
||||
|
||||
// Break all references in current node for GC
|
||||
current.Key = ""
|
||||
current.prev = nil
|
||||
current.next = nil
|
||||
|
||||
current = next
|
||||
}
|
||||
|
||||
// Reset head and tail connections
|
||||
l.head.next = l.tail
|
||||
l.tail.prev = l.head
|
||||
l.size = 0
|
||||
}
|
||||
|
||||
// CacheAdapter provides backward compatibility with existing cache interfaces
|
||||
type CacheAdapter struct {
|
||||
unified *UnifiedCache
|
||||
|
||||
Reference in New Issue
Block a user