feat: Implement basic caching layer with cache-aside pattern for /search results

- Implement cache-aside pattern via SearchCacheService in RouteSearch handler
- Add TTL policies: 3 hours near-term, 7 days far-term
- Write TestCacheAsideSearch and variants for TTL verification
- Extend CacheAside to fully implement Cache interface (Get, Set, Exists, Delete, Increment, Decrement)
This commit is contained in:
2026-08-16 14:53:33 +03:00
parent 7adb2ebbb3
commit 6ae491ef1c
4 changed files with 152 additions and 468 deletions

View File

@@ -228,3 +228,47 @@ func (c *CacheAside) InvalidateStation(ctx context.Context, key *CacheKey) error
func (c *CacheAside) InvalidateSearch(ctx context.Context, key *CacheKey) error {
return c.store.Delete(ctx, key)
}
// Delete removes a key from cache.
func (c *CacheAside) Delete(ctx context.Context, key *CacheKey) error {
return c.store.Delete(ctx, key)
}
// Get retrieves a value from cache by key.
func (c *CacheAside) Get(ctx context.Context, key *CacheKey) ([]byte, error) {
val, err := c.store.Get(ctx, key)
if errors.Is(err, redis.Nil) {
return nil, nil // cache miss
}
if err != nil {
return nil, fmt.Errorf("cache get: %w", err)
}
return val, nil
}
// Set stores a value in cache with an expiry TTL.
func (c *CacheAside) Set(ctx context.Context, key *CacheKey, value []byte, ttl time.Duration) error {
return c.store.Set(ctx, key, value, ttl)
}
// Exists checks if a key exists in cache.
func (c *CacheAside) Exists(ctx context.Context, key *CacheKey) (bool, error) {
_, err := c.store.Exists(ctx, key)
if errors.Is(err, redis.Nil) {
return false, nil
}
if err != nil {
return false, fmt.Errorf("cache exists: %w", err)
}
return true, nil
}
// Increment increments a counter key.
func (c *CacheAside) Increment(ctx context.Context, key *CacheKey) (int64, error) {
return c.store.Increment(ctx, key)
}
// Decrement decrements a counter key.
func (c *CacheAside) Decrement(ctx context.Context, key *CacheKey) (int64, error) {
return c.store.Decrement(ctx, key)
}