versioned API

versioned

package

API reference for the versioned package.

S
struct

Cache

Cache wraps a cache.Cache of VersionedValue to keep multiple versions per key.
It exposes the cache.Cache interface for merge.Value while maintaining
up to limit historical versions per key.

v1/cache/versioned/versioned.go:19-33
type Cache struct

Fields

Name Type Description
base cache.Cache[merge.VersionedValue[T]]
limit int
mu sync.Mutex
order *list.List
entries map[string]*list.Element
maxEntries int
hits atomic.Uint64
misses atomic.Uint64
evictions atomic.Uint64
hitCounter prometheus.Counter
missCounter prometheus.Counter
evictionCounter prometheus.Counter
T
type

Option

Option configures the versioned cache.

v1/cache/versioned/versioned.go:36-36
type Option func(*Cache[T])
F
function

WithMaxEntries

WithMaxEntries sets a global LRU limit on the number of keys stored.

Parameters

n
int

Returns

Option[T]
v1/cache/versioned/versioned.go:39-41
func WithMaxEntries[T any](n int) Option[T]

{
	return func(c *Cache[T]) { c.maxEntries = n }
}
F
function

WithMetrics

WithMetrics enables Prometheus metrics collection using the provided registerer.

Parameters

Returns

Option[T]
v1/cache/versioned/versioned.go:44-60
func WithMetrics[T any](reg prometheus.Registerer) Option[T]

{
	return func(c *Cache[T]) {
		c.hitCounter = prometheus.NewCounter(prometheus.CounterOpts{
			Name: "warp_versioned_cache_hits_total",
			Help: "Total number of versioned cache hits",
		})
		c.missCounter = prometheus.NewCounter(prometheus.CounterOpts{
			Name: "warp_versioned_cache_misses_total",
			Help: "Total number of versioned cache misses",
		})
		c.evictionCounter = prometheus.NewCounter(prometheus.CounterOpts{
			Name: "warp_versioned_cache_evictions_total",
			Help: "Total number of versioned cache evictions",
		})
		reg.MustRegister(c.hitCounter, c.missCounter, c.evictionCounter)
	}
}
F
function

New

New creates a new versioned cache with the provided base cache and history limit.

Parameters

limit
int
opts
...Option[T]

Returns

*Cache[T]
v1/cache/versioned/versioned.go:63-74
func New[T any](base cache.Cache[merge.VersionedValue[T]], limit int, opts ...Option[T]) *Cache[T]

{
	c := &Cache[T]{
		base:    base,
		limit:   limit,
		order:   list.New(),
		entries: make(map[string]*list.Element),
	}
	for _, opt := range opts {
		opt(c)
	}
	return c
}
S
struct

Metrics

Metrics exposes cache statistics.

v1/cache/versioned/versioned.go:191-195
type Metrics struct

Fields

Name Type Description
Hits uint64
Misses uint64
Evictions uint64
F
function

TestCacheSetGetHistory

Parameters

v1/cache/versioned/versioned_test.go:12-39
func TestCacheSetGetHistory(t *testing.T)

{
	base := cache.NewInMemory[merge.VersionedValue[int]]()
	c := New(base, 3)
	ctx := context.Background()
	v1 := merge.Value[int]{Data: 1, Timestamp: time.Now().Add(-3 * time.Minute)}
	v2 := merge.Value[int]{Data: 2, Timestamp: time.Now().Add(-2 * time.Minute)}
	v3 := merge.Value[int]{Data: 3, Timestamp: time.Now().Add(-1 * time.Minute)}

	if err := c.Set(ctx, "k", v1, time.Minute); err != nil {
		t.Fatalf("set v1: %v", err)
	}
	if err := c.Set(ctx, "k", v2, time.Minute); err != nil {
		t.Fatalf("set v2: %v", err)
	}
	if err := c.Set(ctx, "k", v3, time.Minute); err != nil {
		t.Fatalf("set v3: %v", err)
	}

	got, ok, err := c.Get(ctx, "k")
	if err != nil || !ok || got.Data != 3 {
		t.Fatalf("expected latest=3 got %v ok %v err %v", got.Data, ok, err)
	}

	at, ok, err := c.GetAt(ctx, "k", v2.Timestamp.Add(time.Millisecond))
	if err != nil || !ok || at.Data != 2 {
		t.Fatalf("expected value at v2, got %v ok %v err %v", at.Data, ok, err)
	}
}
F
function

TestCacheHistoryLimit

Parameters

v1/cache/versioned/versioned_test.go:41-58
func TestCacheHistoryLimit(t *testing.T)

{
	base := cache.NewInMemory[merge.VersionedValue[int]]()
	c := New(base, 2)
	ctx := context.Background()
	now := time.Now()
	v1 := merge.Value[int]{Data: 1, Timestamp: now.Add(-3 * time.Minute)}
	v2 := merge.Value[int]{Data: 2, Timestamp: now.Add(-2 * time.Minute)}
	v3 := merge.Value[int]{Data: 3, Timestamp: now.Add(-1 * time.Minute)}

	_ = c.Set(ctx, "k", v1, time.Minute)
	_ = c.Set(ctx, "k", v2, time.Minute)
	_ = c.Set(ctx, "k", v3, time.Minute)

	// v1 should be evicted due to limit 2
	if _, ok, _ := c.GetAt(ctx, "k", v1.Timestamp); ok {
		t.Fatal("expected oldest version evicted")
	}
}
F
function

TestCacheInvalidate

Parameters

v1/cache/versioned/versioned_test.go:60-74
func TestCacheInvalidate(t *testing.T)

{
	base := cache.NewInMemory[merge.VersionedValue[int]]()
	c := New(base, 1)
	ctx := context.Background()
	mv := merge.Value[int]{Data: 1, Timestamp: time.Now()}
	if err := c.Set(ctx, "k", mv, time.Minute); err != nil {
		t.Fatalf("set: %v", err)
	}
	if err := c.Invalidate(ctx, "k"); err != nil {
		t.Fatalf("invalidate: %v", err)
	}
	if _, ok, _ := c.Get(ctx, "k"); ok {
		t.Fatal("expected key removed after invalidate")
	}
}
F
function

TestCacheGlobalEviction

Parameters

v1/cache/versioned/versioned_test.go:76-94
func TestCacheGlobalEviction(t *testing.T)

{
	base := cache.NewInMemory[merge.VersionedValue[int]]()
	c := New(base, 1, WithMaxEntries[int](1))
	ctx := context.Background()
	mv := merge.Value[int]{Data: 1, Timestamp: time.Now()}
	if err := c.Set(ctx, "k1", mv, time.Minute); err != nil {
		t.Fatalf("set k1: %v", err)
	}
	if err := c.Set(ctx, "k2", mv, time.Minute); err != nil {
		t.Fatalf("set k2: %v", err)
	}
	if _, ok, _ := c.Get(ctx, "k1"); ok {
		t.Fatal("expected k1 evicted due to max entries")
	}
	m := c.Metrics()
	if m.Evictions != 1 {
		t.Fatalf("expected 1 eviction got %d", m.Evictions)
	}
}
F
function

TestCacheMetricsCounters

Parameters

v1/cache/versioned/versioned_test.go:96-120
func TestCacheMetricsCounters(t *testing.T)

{
	base := cache.NewInMemory[merge.VersionedValue[int]]()
	c := New(base, 1)
	ctx := context.Background()
	now := time.Now()

	if err := c.Set(ctx, "present", merge.Value[int]{Data: 1, Timestamp: now}, time.Minute); err != nil {
		t.Fatalf("set present: %v", err)
	}
	if _, ok, err := c.Get(ctx, "present"); err != nil || !ok {
		t.Fatalf("expected cache hit, got ok=%v err=%v", ok, err)
	}

	if _, ok, err := c.Get(ctx, "missing"); err != nil || ok {
		t.Fatalf("expected cache miss, got ok=%v err=%v", ok, err)
	}

	metrics := c.Metrics()
	if metrics.Hits != 1 {
		t.Fatalf("expected 1 hit, got %d", metrics.Hits)
	}
	if metrics.Misses != 1 {
		t.Fatalf("expected 1 miss, got %d", metrics.Misses)
	}
}