Skip to content

feat(cache): implement dynamic interface caching abstraction#4

Merged
Pyr33x merged 10 commits into
masterfrom
feat/memory
Dec 5, 2025
Merged

feat(cache): implement dynamic interface caching abstraction#4
Pyr33x merged 10 commits into
masterfrom
feat/memory

Conversation

@Pyr33x

@Pyr33x Pyr33x commented Dec 5, 2025

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Cache backend configurable via PROXY_CACHE (memory or redis); default is in-memory.
    • Cache TTL configurable via PROXY_TTL (seconds); applied at cache creation.
  • Enhancements

    • Introduced interchangeable cache implementations (in-memory and Redis) with unified API and TTL support.
    • Runtime falls back to in-memory cache when Redis is unavailable.
  • Documentation

    • README updated with PROXY_CACHE/PROXY_TTL usage, Docker/Compose guidance, and Redis notes.

✏️ Tip: You can customize this high-level summary in your review settings.

@Pyr33x Pyr33x self-assigned this Dec 5, 2025
@Pyr33x Pyr33x added documentation Improvements or additions to documentation enhancement New feature or request labels Dec 5, 2025
@coderabbitai

coderabbitai Bot commented Dec 5, 2025

Copy link
Copy Markdown

Warning

Rate limit exceeded

@Pyr33x has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 8 minutes and 29 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 4336d80 and f9baf65.

📒 Files selected for processing (1)
  • internal/proxy/proxy.go (2 hunks)

Walkthrough

Adds a Store abstraction for caching, implements memory and Redis Store backends, updates Cache to use Store, and surfaces cache type and TTL via configuration and proxy initialization; README and proxy code updated to select and pass TTL to the new cache constructor.

Changes

Cohort / File(s) Summary
Cache core / Store abstraction
internal/cache/cache.go
Replaced direct Redis dependency with a Store interface (Get/Set/Clear). Cache now stores store Store and expiration time.Duration. Constructor changed to NewCacheRepository(store Store, logger *zap.Logger, ttl time.Duration). Get/Put/Clear delegate to Store.
In-memory store
internal/cache/memory.go
Added memoryStore using sync.Map and memoryEntry (value + expiration). Implements Get(ctx, key) ([]byte, error), Set(ctx, key, value []byte, ttl time.Duration) error, and Clear(ctx) error. Enforces TTL on access and removes expired entries.
Redis store
internal/cache/redis.go
Added redisStore wrapper around *redis.Client with NewRedisStore. Implements Get/Set/Clear operating on raw bytes and honoring TTL via Redis commands.
Proxy initialization
internal/proxy/proxy.go
Updated to select cache backend based on config (PROXY_CACHE) and to pass a cache.Store and explicit TTL to NewCacheRepository. Introduces fallback to memory store when Redis not configured or unavailable.
Configuration
pkg/config/config.go
Proxy struct extended with exported Cache string and TTL int64. New() reads PROXY_CACHE (default "memory") and PROXY_TTL (default 60) from environment.
Documentation / Examples
README.md
Docs updated to document PROXY_CACHE (redis

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Check proxy logic for any unconditional overwrite of a created Redis store by MemoryStore and intended fallback behavior.
  • Verify TTL unit conversion/propagation: int64 config → time.Duration used by stores and Cache.
  • Inspect memoryStore concurrency (sync.Map usage) and expiration race conditions.
  • Confirm consistent error semantics between memoryStore (nil on miss) and redisStore (propagates client errors).
  • Review marshalling/unmarshalling and nil-byte handling in cache.go Get/Put.

Poem

I nibble bytes from field to den,
A store in burrows built for men.
Memory hops, Redis hums,
TTL drums, the timeout drums.
Hop—cache set! —the rabbit grins 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: introducing a dynamic Store interface abstraction for caching, replacing direct Redis dependency with pluggable in-memory and Redis implementations.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (4)
pkg/config/config.go (1)

49-51: Consider adding validation or documentation for valid cache types.

The Cache field accepts any string but only "redis" and "memory" are valid values. Consider documenting valid values in a comment or adding validation to fail fast on invalid configuration.

 			Proxy: Proxy{
 				Port:  envy.GetString("PROXY_PORT", "1337"),
-				Cache: envy.GetString("PROXY_CACHE", "memory"),
+				Cache: envy.GetString("PROXY_CACHE", "memory"), // valid: "memory", "redis"
 				TTL:   envy.GetInt64("PROXY_TTL", 60), // in seconds
 			},
internal/cache/memory.go (1)

22-36: Lazy expiration means expired entries accumulate in memory.

Entries are only evicted when accessed. With many keys that are never re-accessed, memory usage will grow unbounded. This is acceptable for low-traffic caches but consider documenting this limitation or adding periodic cleanup for production use.

internal/cache/cache.go (2)

46-53: Error case logged as "cache miss" may obscure actual failures.

When store.Get returns an error (e.g., Redis connection failure), it's logged as a "MISS" which could mask infrastructure issues. Consider distinguishing between a store error and a cache miss.

 	raw, err := c.store.Get(ctx, key)
 	if err != nil {
-		c.logger.Info("cache miss",
+		c.logger.Warn("cache get error",
 			zap.String("key", key),
-			zap.String("state", "MISS"),
+			zap.Error(err),
 		)
 		return nil, false
 	}

61-64: Unmarshal errors are silently swallowed.

If cached data is corrupted or in an unexpected format, the error is not logged, making debugging difficult. Add logging for unmarshal failures.

 	var val CacheValue
 	if err := json.Unmarshal(raw, &val); err != nil {
+		c.logger.Error("failed to unmarshal cached value",
+			zap.String("key", key),
+			zap.Error(err),
+		)
 		return nil, false
 	}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 243dc35 and 0894285.

📒 Files selected for processing (5)
  • internal/cache/cache.go (6 hunks)
  • internal/cache/memory.go (1 hunks)
  • internal/cache/redis.go (1 hunks)
  • internal/proxy/proxy.go (2 hunks)
  • pkg/config/config.go (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
pkg/config/config.go (1)
internal/cache/cache.go (1)
  • Cache (20-24)
internal/proxy/proxy.go (5)
internal/cache/cache.go (3)
  • Store (14-18)
  • Cache (20-24)
  • NewCacheRepository (32-38)
pkg/config/config.go (4)
  • Server (11-14)
  • Proxy (20-24)
  • New (42-67)
  • Redis (30-33)
internal/adapter/redis/redis.go (1)
  • New (16-31)
internal/cache/redis.go (1)
  • NewRedisStore (14-16)
internal/cache/memory.go (1)
  • NewMemoryStore (18-20)
internal/cache/memory.go (1)
internal/cache/cache.go (1)
  • Store (14-18)
🪛 GitHub Actions: 🔨 Lint
internal/proxy/proxy.go

[error] 34-34: GolangCI-Lint: ineffectual assignment to cacheStorage (ineffassign)

🪛 GitHub Check: lint (stable, ubuntu-latest)
internal/proxy/proxy.go

[failure] 34-34:
ineffectual assignment to cacheStorage (ineffassign)

🔇 Additional comments (6)
internal/cache/redis.go (1)

10-24: LGTM!

The Redis store implementation correctly delegates to the Redis client and properly implements the Store interface. The Get and Set methods handle byte conversion and TTL appropriately.

pkg/config/config.go (1)

20-24: LGTM!

The new Cache and TTL fields extend the proxy configuration cleanly. The defaults (memory cache with 60-second TTL) are sensible.

internal/proxy/proxy.go (1)

45-45: LGTM!

The TTL conversion from int64 seconds to time.Duration is correctly applied when creating the cache repository.

internal/cache/memory.go (1)

38-50: LGTM!

The Set method correctly computes expiration time and stores entries atomically using sync.Map.Store.

internal/cache/cache.go (2)

14-18: LGTM!

The Store interface is well-designed with a minimal, focused API that cleanly abstracts the underlying storage mechanism.


32-38: LGTM!

The NewCacheRepository constructor and the Put/Clear methods are well-implemented with appropriate error handling and logging.

Also applies to: 69-94, 96-98

Comment thread internal/cache/memory.go
Comment thread internal/cache/redis.go
Comment thread internal/proxy/proxy.go Outdated
Comment thread internal/proxy/proxy.go
@Pyr33x Pyr33x merged commit 15337c1 into master Dec 5, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant