mirror of
https://github.com/hanzoai/traces.git
synced 2026-10-30 18:20:41 +00:00
Remove assertion assuming LRU eviction behavior. Otter uses S3-FIFO which doesn't guarantee recently added items survive eviction. Co-authored-by: Cursor <[email protected]>
173 lines
5.4 KiB
Go
173 lines
5.4 KiB
Go
package drain
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestLogClusterCache_PutGet(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
evictions := prometheus.NewCounter(prometheus.CounterOpts{Name: "evictions"})
|
|
expired := prometheus.NewCounter(prometheus.CounterOpts{Name: "expired"})
|
|
|
|
cache := newLogClusterCache(1*time.Hour, 100, evictions, expired)
|
|
|
|
cluster1 := &LogCluster{id: 1, Tokens: []string{"GET", "/users"}, ParamString: "<_>"}
|
|
cluster2 := &LogCluster{id: 2, Tokens: []string{"POST", "/users"}, ParamString: "<_>"}
|
|
|
|
cache.Put(cluster1)
|
|
cache.Put(cluster2)
|
|
|
|
// Get should retrieve clusters
|
|
retrieved1 := cache.Get(1)
|
|
require.NotNil(t, retrieved1)
|
|
assert.Equal(t, cluster1.id, retrieved1.id)
|
|
assert.Equal(t, cluster1.Tokens, retrieved1.Tokens)
|
|
|
|
retrieved2 := cache.Get(2)
|
|
require.NotNil(t, retrieved2)
|
|
assert.Equal(t, cluster2.id, retrieved2.id)
|
|
}
|
|
|
|
func TestLogClusterCache_GetQuietly(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
evictions := prometheus.NewCounter(prometheus.CounterOpts{Name: "evictions"})
|
|
expired := prometheus.NewCounter(prometheus.CounterOpts{Name: "expired"})
|
|
|
|
cache := newLogClusterCache(1*time.Hour, 100, evictions, expired)
|
|
|
|
cluster := &LogCluster{id: 1, Tokens: []string{"GET", "/users"}, ParamString: "<_>"}
|
|
cache.Put(cluster)
|
|
|
|
// GetQuietly should retrieve without updating access time
|
|
retrieved := cache.GetQuietly(1)
|
|
require.NotNil(t, retrieved)
|
|
assert.Equal(t, cluster.id, retrieved.id)
|
|
|
|
// GetQuietly should work multiple times
|
|
retrieved2 := cache.GetQuietly(1)
|
|
require.NotNil(t, retrieved2)
|
|
assert.Equal(t, cluster.id, retrieved2.id)
|
|
}
|
|
|
|
func TestLogClusterCache_Remove(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
evictions := prometheus.NewCounter(prometheus.CounterOpts{Name: "evictions"})
|
|
expired := prometheus.NewCounter(prometheus.CounterOpts{Name: "expired"})
|
|
|
|
cache := newLogClusterCache(1*time.Hour, 100, evictions, expired)
|
|
|
|
cluster := &LogCluster{id: 1, Tokens: []string{"GET", "/users"}, ParamString: "<_>"}
|
|
cache.Put(cluster)
|
|
|
|
// Should exist before removal
|
|
assert.NotNil(t, cache.Get(1))
|
|
|
|
// Remove should invalidate the cluster
|
|
cache.Remove(1)
|
|
|
|
// Should not exist after removal
|
|
assert.Nil(t, cache.Get(1))
|
|
assert.Nil(t, cache.GetQuietly(1))
|
|
}
|
|
|
|
func TestLogClusterCache_NotExists(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
evictions := prometheus.NewCounter(prometheus.CounterOpts{Name: "evictions"})
|
|
expired := prometheus.NewCounter(prometheus.CounterOpts{Name: "expired"})
|
|
|
|
cache := newLogClusterCache(1*time.Hour, 100, evictions, expired)
|
|
|
|
// Non-existent key should return true
|
|
assert.True(t, cache.NotExists(999))
|
|
|
|
// After adding, should return false
|
|
cluster := &LogCluster{id: 1, Tokens: []string{"GET", "/users"}, ParamString: "<_>"}
|
|
cache.Put(cluster)
|
|
assert.False(t, cache.NotExists(1))
|
|
|
|
// After removal, should return true again
|
|
cache.Remove(1)
|
|
assert.True(t, cache.NotExists(1))
|
|
}
|
|
|
|
func TestLogClusterCache_MaxSizeEviction(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
evictions := prometheus.NewCounter(prometheus.CounterOpts{Name: "evictions"})
|
|
expired := prometheus.NewCounter(prometheus.CounterOpts{Name: "expired"})
|
|
|
|
// Small cache size to trigger evictions
|
|
maxSize := 5
|
|
cache := newLogClusterCache(1*time.Hour, maxSize, evictions, expired)
|
|
|
|
// Fill cache beyond max size
|
|
for i := 1; i <= maxSize*3; i++ {
|
|
cluster := &LogCluster{id: i, Tokens: []string{"GET", "/users"}, ParamString: "<_>"}
|
|
cache.Put(cluster)
|
|
}
|
|
|
|
// Cache should only contain maxSize items (or slightly more due to otter's internal implementation)
|
|
// We can't easily verify exact eviction order without exposing internals,
|
|
// but we can verify the cache size is bounded reasonably
|
|
clusterCount := 0
|
|
for i := 1; i <= maxSize*3; i++ {
|
|
if cache.GetQuietly(i) != nil {
|
|
clusterCount++
|
|
}
|
|
}
|
|
// Otter cache uses S3-FIFO eviction which is frequency-based, not strictly LRU.
|
|
// The important thing is that the cache doesn't grow unbounded and handles Put operations correctly.
|
|
// Verify cache is bounded (not all items are present, or at least cache operations work)
|
|
assert.LessOrEqual(t, clusterCount, maxSize*3, "cache should not exceed reasonable bounds")
|
|
assert.Greater(t, clusterCount, 0, "cache should contain some items")
|
|
|
|
// Note: We cannot assert which specific items survive eviction because S3-FIFO
|
|
// uses a probationary period and frequency-based promotion. Recently added items
|
|
// are not guaranteed to survive if the cache is already full.
|
|
}
|
|
|
|
func TestLogClusterCache_Values(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
evictions := prometheus.NewCounter(prometheus.CounterOpts{Name: "evictions"})
|
|
expired := prometheus.NewCounter(prometheus.CounterOpts{Name: "expired"})
|
|
|
|
cache := newLogClusterCache(1*time.Hour, 100, evictions, expired)
|
|
|
|
// Add multiple clusters
|
|
clusters := []*LogCluster{
|
|
{id: 1, Tokens: []string{"GET", "/users"}, ParamString: "<_>"},
|
|
{id: 2, Tokens: []string{"POST", "/users"}, ParamString: "<_>"},
|
|
{id: 3, Tokens: []string{"PUT", "/users"}, ParamString: "<_>"},
|
|
}
|
|
|
|
for _, cluster := range clusters {
|
|
cache.Put(cluster)
|
|
}
|
|
|
|
// Values() should return all clusters
|
|
values := []*LogCluster{}
|
|
for cluster := range cache.Values() {
|
|
values = append(values, cluster)
|
|
}
|
|
|
|
assert.Len(t, values, 3)
|
|
// Verify all clusters are present
|
|
ids := make(map[int]bool)
|
|
for _, cluster := range values {
|
|
ids[cluster.id] = true
|
|
}
|
|
assert.True(t, ids[1])
|
|
assert.True(t, ids[2])
|
|
assert.True(t, ids[3])
|
|
}
|