This is an automated email from the ASF dual-hosted git repository.

Similarityoung pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/dubbo-go-pixiu.git


The following commit(s) were added to refs/heads/develop by this push:
     new 55d841c94 perf(llm): make cooldown store eviction O(1) with 
container/list LRU (#966)
55d841c94 is described below

commit 55d841c94d2af8e9ae55a02430cf5b29ec056189
Author: 承潜 <[email protected]>
AuthorDate: Sat Aug 15 14:08:30 2026 +0800

    perf(llm): make cooldown store eviction O(1) with container/list LRU (#966)
    
    * perf(llm): make cooldown store eviction O(1) with container/list LRU
    
    The bounded cooldown store evicted its oldest entry by scanning the
    whole map to find the minimum lastFailure, so eviction on the failure
    path was O(N) in the number of tracked endpoints.
    
    Back the store with a container/list ordered oldest-at-front,
    newest-at-back, and key the map to *list.Element. markFailure now moves
    a refreshed entry to the back and inserts new entries at the back;
    eviction pops the front in O(1). Each cooldownEntry carries its own key
    so a front pop can delete the matching map entry without a reverse
    lookup. A shared removeLocked helper keeps the map and list in sync on
    every delete, sweep, and eviction, so no stale list element survives.
    
    Caller-visible cooldown behavior (key, TTL, idempotent replay, lazy
    sweep) is unchanged. No new dependency, no background goroutine.
    
    New tests cover the LRU-specific invariants: refresh updates recency,
    the TTL sweep clears both map and list state, and deleting an expired
    cooldown leaves no stale list element.
    
    Closes #943
    
    * refactor(llm): drop dead eviction guard in cooldown store
    
    evictOldestIfFullLocked is only called for brand-new keys, so the
    oldestKey == current guard can never fire. Worse, if it ever did it
    would skip eviction and let markFailure push the store past capacity.
    Remove the guard and the now-unused current parameter.
    
    * perf(llm): inject nowFn into cooldownStore to fix timestamp/list ordering 
under concurrency
    
    markFailure previously sampled time.Now() before acquiring the mutex, so
    concurrent callers could record a newer timestamp but insert earlier in the
    recency list, causing eviction to drop a still-active cooldown entry.
    
    Fix: add nowFn func() time.Time to cooldownStore (default time.Now), called
    inside the mutex so timestamp and PushBack order are always consistent.
    markFailure signature drops the lastFailure parameter; callers no longer
    supply a timestamp. Tests inject a controlled clock via store.nowFn.
---
 pkg/filter/llm/proxy/filter.go      |  93 ++++++++++++++++-----------
 pkg/filter/llm/proxy/filter_test.go | 121 +++++++++++++++++++++++++++++++++---
 2 files changed, 168 insertions(+), 46 deletions(-)

diff --git a/pkg/filter/llm/proxy/filter.go b/pkg/filter/llm/proxy/filter.go
index 5118a67b4..7783c2f6b 100644
--- a/pkg/filter/llm/proxy/filter.go
+++ b/pkg/filter/llm/proxy/filter.go
@@ -19,6 +19,7 @@ package proxy
 
 import (
        "bytes"
+       "container/list"
        "crypto/sha256"
        "errors"
        "fmt"
@@ -127,12 +128,24 @@ type (
        }
 
        cooldownStore struct {
-               mu                    sync.Mutex
-               lastFailureByEndpoint map[cooldownKey]cooldownEntry
-               lastSweep             time.Time
+               mu sync.Mutex
+               // lastFailureByEndpoint indexes each tracked endpoint to its 
node in
+               // recencyOrder, so lookups stay O(1) and the node can be 
repositioned
+               // or removed without scanning.
+               lastFailureByEndpoint map[cooldownKey]*list.Element
+               // recencyOrder holds *cooldownEntry values ordered 
oldest-at-front,
+               // newest-at-back. Eviction pops the front in O(1); refreshing 
an entry
+               // moves its node to the back.
+               recencyOrder *list.List
+               lastSweep    time.Time
+               // nowFn is called inside the mutex so the recorded timestamp 
and the
+               // recency-list insertion order are always consistent, even 
under
+               // concurrent writes. Overridable in tests.
+               nowFn func() time.Time
        }
 
        cooldownEntry struct {
+               key         cooldownKey
                lastFailure time.Time
                ttl         time.Duration
        }
@@ -459,12 +472,14 @@ func (executor *RequestExecutor) 
markEndpointCooldown(endpoint *model.Endpoint)
        if store == nil || endpoint == nil {
                return
        }
-       store.markFailure(executor.clusterName, endpoint, time.Now())
+       store.markFailure(executor.clusterName, endpoint)
 }
 
 func newCooldownStore() *cooldownStore {
        return &cooldownStore{
-               lastFailureByEndpoint: map[cooldownKey]cooldownEntry{},
+               lastFailureByEndpoint: map[cooldownKey]*list.Element{},
+               recencyOrder:          list.New(),
+               nowFn:                 time.Now,
        }
 }
 
@@ -477,33 +492,41 @@ func (s *cooldownStore) 
lastFailureWithCurrentTTL(clusterName string, endpoint *
        key := newCooldownKey(clusterName, endpoint)
        now := time.Now()
        s.sweepExpiredIfNeededLocked(now, key)
-       entry, ok := s.lastFailureByEndpoint[key]
+       element, ok := s.lastFailureByEndpoint[key]
        if !ok {
                return time.Time{}, 0, false
        }
+       entry := element.Value.(*cooldownEntry)
        currentTTL := endpointCooldownInterval(endpoint)
        if entry.ttl != currentTTL {
                entry.ttl = currentTTL
-               s.lastFailureByEndpoint[key] = entry
        }
        return entry.lastFailure, entry.ttl, true
 }
 
-func (s *cooldownStore) markFailure(clusterName string, endpoint 
*model.Endpoint, lastFailure time.Time) {
+func (s *cooldownStore) markFailure(clusterName string, endpoint 
*model.Endpoint) {
        if s == nil || endpoint == nil {
                return
        }
        s.mu.Lock()
        defer s.mu.Unlock()
+       lastFailure := s.nowFn()
        key := newCooldownKey(clusterName, endpoint)
        s.sweepExpiredIfNeededLocked(time.Now(), key)
-       if _, ok := s.lastFailureByEndpoint[key]; !ok {
-               s.evictOldestIfFullLocked(key)
+       ttl := endpointCooldownInterval(endpoint)
+       if element, ok := s.lastFailureByEndpoint[key]; ok {
+               entry := element.Value.(*cooldownEntry)
+               entry.lastFailure = lastFailure
+               entry.ttl = ttl
+               s.recencyOrder.MoveToBack(element)
+               return
        }
-       s.lastFailureByEndpoint[key] = cooldownEntry{
+       s.evictOldestIfFullLocked()
+       s.lastFailureByEndpoint[key] = s.recencyOrder.PushBack(&cooldownEntry{
+               key:         key,
                lastFailure: lastFailure,
-               ttl:         endpointCooldownInterval(endpoint),
-       }
+               ttl:         ttl,
+       })
 }
 
 func (s *cooldownStore) deleteLastFailureIfMatches(clusterName string, 
endpoint *model.Endpoint, expected time.Time) bool {
@@ -513,14 +536,21 @@ func (s *cooldownStore) 
deleteLastFailureIfMatches(clusterName string, endpoint
        s.mu.Lock()
        defer s.mu.Unlock()
        key := newCooldownKey(clusterName, endpoint)
-       current, ok := s.lastFailureByEndpoint[key]
-       if !ok || current.lastFailure != expected {
+       element, ok := s.lastFailureByEndpoint[key]
+       if !ok || element.Value.(*cooldownEntry).lastFailure != expected {
                return false
        }
-       delete(s.lastFailureByEndpoint, key)
+       s.removeLocked(key, element)
        return true
 }
 
+// removeLocked drops one entry from both the map and the recency list,
+// keeping the two structures in sync. Callers must hold s.mu.
+func (s *cooldownStore) removeLocked(key cooldownKey, element *list.Element) {
+       delete(s.lastFailureByEndpoint, key)
+       s.recencyOrder.Remove(element)
+}
+
 func (s *cooldownStore) sweepExpiredIfNeededLocked(now time.Time, current 
cooldownKey) {
        if len(s.lastFailureByEndpoint) < cooldownStoreSweepAt &&
                !s.lastSweep.IsZero() &&
@@ -532,39 +562,28 @@ func (s *cooldownStore) sweepExpiredIfNeededLocked(now 
time.Time, current cooldo
 }
 
 func (s *cooldownStore) sweepExpiredExceptLocked(now time.Time, current 
cooldownKey) {
-       for key, entry := range s.lastFailureByEndpoint {
+       for key, element := range s.lastFailureByEndpoint {
                if key == current {
                        continue
                }
+               entry := element.Value.(*cooldownEntry)
                if now.Sub(entry.lastFailure) >= entry.ttl {
-                       delete(s.lastFailureByEndpoint, key)
+                       s.removeLocked(key, element)
                }
        }
 }
 
-func (s *cooldownStore) evictOldestIfFullLocked(current cooldownKey) {
+// evictOldestIfFullLocked removes the least-recently-failed entry when the
+// store is at capacity, in O(1) via the front of the recency list.
+func (s *cooldownStore) evictOldestIfFullLocked() {
        if len(s.lastFailureByEndpoint) < maxCooldownStoreEntries {
                return
        }
-
-       var (
-               oldestKey   cooldownKey
-               oldestEntry cooldownEntry
-               found       bool
-       )
-       for key, entry := range s.lastFailureByEndpoint {
-               if key == current {
-                       continue
-               }
-               if !found || entry.lastFailure.Before(oldestEntry.lastFailure) {
-                       oldestKey = key
-                       oldestEntry = entry
-                       found = true
-               }
-       }
-       if found {
-               delete(s.lastFailureByEndpoint, oldestKey)
+       oldest := s.recencyOrder.Front()
+       if oldest == nil {
+               return
        }
+       s.removeLocked(oldest.Value.(*cooldownEntry).key, oldest)
 }
 
 func newCooldownKey(clusterName string, endpoint *model.Endpoint) cooldownKey {
diff --git a/pkg/filter/llm/proxy/filter_test.go 
b/pkg/filter/llm/proxy/filter_test.go
index deaca8492..dc4d04e1f 100644
--- a/pkg/filter/llm/proxy/filter_test.go
+++ b/pkg/filter/llm/proxy/filter_test.go
@@ -188,7 +188,9 @@ func 
TestRequestExecutorEndpointInCooldownClearsExpiredCooldownFromProxyStore(t
                clusterName: clusterName,
                cooldowns:   store,
        }
-       store.markFailure(clusterName, endpoint, time.Now().Add(-time.Hour))
+       store.nowFn = func() time.Time { return time.Now().Add(-time.Hour) }
+       store.markFailure(clusterName, endpoint)
+       store.nowFn = time.Now
 
        assert.False(t, executor.endpointInCooldown(endpoint))
 
@@ -269,7 +271,9 @@ func 
TestRequestExecutorCooldownSurvivesNonIdentityLLMConfigChanges(t *testing.T
                clusterName: clusterName,
                cooldowns:   store,
        }
-       store.markFailure(clusterName, oldEndpoint, 
time.Now().Add(-50*time.Millisecond))
+       store.nowFn = func() time.Time { return time.Now().Add(-50 * 
time.Millisecond) }
+       store.markFailure(clusterName, oldEndpoint)
+       store.nowFn = time.Now
 
        assert.True(t, executor.endpointInCooldown(replacement))
 }
@@ -288,7 +292,9 @@ func 
TestCooldownStoreLazySweepKeepsEntryAfterEndpointIntervalExtends(t *testing
                clusterName: clusterName,
                cooldowns:   store,
        }
-       store.markFailure(clusterName, oldEndpoint, 
time.Now().Add(-50*time.Millisecond))
+       store.nowFn = func() time.Time { return time.Now().Add(-50 * 
time.Millisecond) }
+       store.markFailure(clusterName, oldEndpoint)
+       store.nowFn = time.Now
 
        assert.True(t, executor.endpointInCooldown(replacement))
        _, _, _ = store.lastFailureWithCurrentTTL(clusterName, activeEndpoint)
@@ -310,8 +316,10 @@ func 
TestCooldownStoreLazySweepRemovesExpiredChurnedEndpointEntry(t *testing.T)
        activeEndpoint := testLLMEndpoint("ep-2", 18086)
        store := newCooldownStore()
 
-       store.markFailure(clusterName, oldEndpoint, time.Now().Add(-time.Hour))
-       store.markFailure(clusterName, movedEndpoint, time.Now())
+       store.nowFn = func() time.Time { return time.Now().Add(-time.Hour) }
+       store.markFailure(clusterName, oldEndpoint)
+       store.nowFn = time.Now
+       store.markFailure(clusterName, movedEndpoint)
 
        store.mu.Lock()
        _, oldExistsAfterMove := 
store.lastFailureByEndpoint[newCooldownKey(clusterName, oldEndpoint)]
@@ -336,7 +344,9 @@ func 
TestCooldownStoreLazySweepRemovesExpiredEndpointFromDifferentCluster(t *tes
        activeEndpoint := testLLMEndpoint("ep-2", 18093)
        store := newCooldownStore()
 
-       store.markFailure("old-cluster", expiredEndpoint, 
time.Now().Add(-time.Hour))
+       store.nowFn = func() time.Time { return time.Now().Add(-time.Hour) }
+       store.markFailure("old-cluster", expiredEndpoint)
+       store.nowFn = time.Now
        store.mu.Lock()
        store.lastSweep = time.Now().Add(-cooldownStoreSweepAfter - 
time.Millisecond)
        store.mu.Unlock()
@@ -351,6 +361,8 @@ func 
TestCooldownStoreLazySweepRemovesExpiredEndpointFromDifferentCluster(t *tes
 func TestCooldownStoreEvictsOldestEntryWhenCapacityExceeded(t *testing.T) {
        store := newCooldownStore()
        now := time.Now()
+       current := now
+       store.nowFn = func() time.Time { return current }
        oldestEndpoint := testLLMEndpoint("ep-0", 19000)
 
        for i := 0; i < maxCooldownStoreEntries; i++ {
@@ -358,11 +370,13 @@ func 
TestCooldownStoreEvictsOldestEntryWhenCapacityExceeded(t *testing.T) {
                if i == 0 {
                        oldestEndpoint = endpoint
                }
-               store.markFailure("capacity-cluster", endpoint, 
now.Add(time.Duration(i)*time.Millisecond))
+               current = now.Add(time.Duration(i) * time.Millisecond)
+               store.markFailure("capacity-cluster", endpoint)
        }
 
        newestEndpoint := testLLMEndpoint("ep-new", 21000)
-       store.markFailure("capacity-cluster", newestEndpoint, 
now.Add(time.Hour))
+       current = now.Add(time.Hour)
+       store.markFailure("capacity-cluster", newestEndpoint)
 
        store.mu.Lock()
        _, oldestExists := 
store.lastFailureByEndpoint[newCooldownKey("capacity-cluster", oldestEndpoint)]
@@ -375,6 +389,95 @@ func 
TestCooldownStoreEvictsOldestEntryWhenCapacityExceeded(t *testing.T) {
        assert.Equal(t, maxCooldownStoreEntries, entryCount)
 }
 
+// TestCooldownStoreRefreshUpdatesRecency verifies that re-failing an existing
+// endpoint moves it to the newest side of the LRU, so a later eviction drops a
+// genuinely older entry instead of the just-refreshed one.
+func TestCooldownStoreRefreshUpdatesRecency(t *testing.T) {
+       store := newCooldownStore()
+       now := time.Now()
+       current := now
+       store.nowFn = func() time.Time { return current }
+
+       first := testLLMEndpoint("ep-first", 19000)
+       second := testLLMEndpoint("ep-second", 19001)
+       store.markFailure("recency-cluster", first)
+       current = now.Add(time.Millisecond)
+       store.markFailure("recency-cluster", second)
+
+       // Refresh the first endpoint so it becomes the most recently failed.
+       current = now.Add(2 * time.Millisecond)
+       store.markFailure("recency-cluster", first)
+
+       store.mu.Lock()
+       front := store.recencyOrder.Front().Value.(*cooldownEntry)
+       back := store.recencyOrder.Back().Value.(*cooldownEntry)
+       listLen := store.recencyOrder.Len()
+       mapLen := len(store.lastFailureByEndpoint)
+       store.mu.Unlock()
+
+       assert.Equal(t, newCooldownKey("recency-cluster", second), front.key, 
"least-recent should be the un-refreshed entry")
+       assert.Equal(t, newCooldownKey("recency-cluster", first), back.key, 
"most-recent should be the refreshed entry")
+       assert.Equal(t, 2, listLen)
+       assert.Equal(t, mapLen, listLen, "map and recency list must stay in 
sync")
+}
+
+// TestCooldownStoreSweepRemovesMapAndListState verifies the lazy TTL sweep
+// drops an expired entry from both the map and the recency list, leaving no
+// stale list element behind.
+func TestCooldownStoreSweepRemovesMapAndListState(t *testing.T) {
+       clusterName := "sweep-cluster"
+       expiredEndpoint := testLLMEndpoint("ep-expired", 19000)
+       activeEndpoint := testLLMEndpoint("ep-active", 19001)
+       store := newCooldownStore()
+
+       store.nowFn = func() time.Time { return time.Now().Add(-time.Hour) }
+       store.markFailure(clusterName, expiredEndpoint)
+       store.nowFn = time.Now
+       store.mu.Lock()
+       store.lastSweep = time.Now().Add(-cooldownStoreSweepAfter - 
time.Millisecond)
+       store.mu.Unlock()
+
+       // Touching an unrelated endpoint triggers the lazy sweep.
+       store.lastFailureWithCurrentTTL(clusterName, activeEndpoint)
+
+       store.mu.Lock()
+       _, expiredExists := 
store.lastFailureByEndpoint[newCooldownKey(clusterName, expiredEndpoint)]
+       listLen := store.recencyOrder.Len()
+       mapLen := len(store.lastFailureByEndpoint)
+       store.mu.Unlock()
+
+       assert.False(t, expiredExists)
+       assert.Equal(t, 0, listLen, "expired entry must be removed from the 
recency list, not just the map")
+       assert.Equal(t, mapLen, listLen, "map and recency list must stay in 
sync")
+}
+
+// TestCooldownStoreDeleteExpiredLeavesNoStaleListElement verifies that 
clearing
+// an expired cooldown via the request path removes both the map entry and its
+// recency list element.
+func TestCooldownStoreDeleteExpiredLeavesNoStaleListElement(t *testing.T) {
+       clusterName := "delete-expired-cluster"
+       endpoint := testLLMEndpoint("ep-1", 19000)
+       store := newCooldownStore()
+       executor := &RequestExecutor{
+               clusterName: clusterName,
+               cooldowns:   store,
+       }
+       store.nowFn = func() time.Time { return time.Now().Add(-time.Hour) }
+       store.markFailure(clusterName, endpoint)
+       store.nowFn = time.Now
+
+       // endpointInCooldown observes the entry as expired and deletes it.
+       assert.False(t, executor.endpointInCooldown(endpoint))
+
+       store.mu.Lock()
+       mapLen := len(store.lastFailureByEndpoint)
+       listLen := store.recencyOrder.Len()
+       store.mu.Unlock()
+
+       assert.Equal(t, 0, mapLen)
+       assert.Equal(t, 0, listLen, "deleting an expired cooldown must not 
leave a stale list element")
+}
+
 func TestStrategyExecuteIgnoresUnhealthyPreferredEndpoint(t *testing.T) {
        clusterName := "llm-preferred-health"
        healthyEndpoint := testLLMEndpoint("ep-1", 18086)
@@ -468,7 +571,7 @@ func BenchmarkCooldown_EndpointInCooldown(b *testing.B) {
                endpoint.LLMMeta.APIKey = fmt.Sprintf("api-key-%d", i)
                endpoint.LLMMeta.HealthCheckInterval = cooldownTTLMillis
                endpoints[i] = endpoint
-               store.markFailure(clusterName, endpoint, time.Now())
+               store.markFailure(clusterName, endpoint)
        }
 
        b.ReportAllocs()

Reply via email to