✨ feat(cache): add in-memory cache service (ADR-0022 Phase 1 part 2)
Implements Phase 1 part 2 of ADR-0022 (Rate Limiting and Cache Strategy): in-memory cache service using github.com/patrickmn/go-cache. Wired onto Server struct and used by handleVersion to memoize the response for 60 seconds. Companion to PR #22 (per-IP rate limit middleware). Changes: - New: pkg/cache/cache.go (58 lines, Service interface + InMemoryService) - New: pkg/cache/cache_test.go (125 lines, 6 unit tests, all passing) - Modified: pkg/config/config.go (CacheConfig struct + 3 SetDefault + 3 BindEnv + 3 getters) - Modified: pkg/server/server.go (cacheService field + init in NewServer + use in handleVersion) - Modified: config.yaml (cache section with defaults) - go.mod / go.sum (github.com/patrickmn/go-cache v2.1.0+incompatible) Closes #13 (Phase 1 fully complete: rate limit in PR #22, cache here). Phase 2 (Redis-compatible shared cache via Dragonfly/KeyDB) deferred. BDD scenario not added: cache hit is hard to test via the existing testserver (same architectural limitation as the rate limit BDD - testserver pre-started, env vars don't propagate). Behavior is fully covered by unit tests (6/6 PASS). TODO: BDD scenario can be added once testserver supports per-scenario config. Generated ~95% in autonomy by Mistral Vibe via ICM workspace ~/Work/Vibe/workspaces/cache-service-inmemory/. T6 cost €2.50 for stages 01-02 (50% reduction vs T5, thanks to pre-extracted snippets in shared/). Trainer (Claude) finalized commit/PR (Mistral hit max-turns). 🤖 Co-Authored-By: Mistral Vibe (devstral-2 / mistral-medium-3.5) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,7 @@ type Config struct {
|
||||
Auth AuthConfig `mapstructure:"auth"`
|
||||
Database DatabaseConfig `mapstructure:"database"`
|
||||
RateLimit RateLimitConfig `mapstructure:"rate_limit"`
|
||||
Cache CacheConfig `mapstructure:"cache"`
|
||||
}
|
||||
|
||||
// ServerConfig holds server-related configuration
|
||||
@@ -105,6 +106,13 @@ type RateLimitConfig struct {
|
||||
BurstSize int `mapstructure:"burst_size"`
|
||||
}
|
||||
|
||||
// CacheConfig holds cache configuration
|
||||
type CacheConfig struct {
|
||||
Enabled bool `mapstructure:"enabled"`
|
||||
DefaultTTLSeconds int `mapstructure:"default_ttl_seconds"`
|
||||
CleanupIntervalSeconds int `mapstructure:"cleanup_interval_seconds"`
|
||||
}
|
||||
|
||||
// VersionInfo holds application version information
|
||||
type VersionInfo struct {
|
||||
Version string `mapstructure:"-"` // Set via ldflags
|
||||
@@ -202,6 +210,11 @@ func LoadConfig() (*Config, error) {
|
||||
v.SetDefault("rate_limit.requests_per_minute", 60)
|
||||
v.SetDefault("rate_limit.burst_size", 10)
|
||||
|
||||
// Cache defaults
|
||||
v.SetDefault("cache.enabled", true)
|
||||
v.SetDefault("cache.default_ttl_seconds", 300)
|
||||
v.SetDefault("cache.cleanup_interval_seconds", 600)
|
||||
|
||||
// Auth defaults
|
||||
v.SetDefault("auth.jwt_secret", "default-secret-key-please-change-in-production")
|
||||
v.SetDefault("auth.admin_master_password", "admin123")
|
||||
@@ -266,6 +279,11 @@ func LoadConfig() (*Config, error) {
|
||||
v.BindEnv("rate_limit.requests_per_minute", "DLC_RATE_LIMIT_REQUESTS_PER_MINUTE")
|
||||
v.BindEnv("rate_limit.burst_size", "DLC_RATE_LIMIT_BURST_SIZE")
|
||||
|
||||
// Cache environment variables
|
||||
v.BindEnv("cache.enabled", "DLC_CACHE_ENABLED")
|
||||
v.BindEnv("cache.default_ttl_seconds", "DLC_CACHE_DEFAULT_TTL_SECONDS")
|
||||
v.BindEnv("cache.cleanup_interval_seconds", "DLC_CACHE_CLEANUP_INTERVAL_SECONDS")
|
||||
|
||||
// Database environment variables
|
||||
v.BindEnv("database.host", "DLC_DATABASE_HOST")
|
||||
v.BindEnv("database.port", "DLC_DATABASE_PORT")
|
||||
@@ -428,6 +446,27 @@ func (c *Config) GetRateLimitBurstSize() int {
|
||||
return c.RateLimit.BurstSize
|
||||
}
|
||||
|
||||
// GetCacheEnabled returns whether cache is enabled
|
||||
func (c *Config) GetCacheEnabled() bool {
|
||||
return c.Cache.Enabled
|
||||
}
|
||||
|
||||
// GetCacheDefaultTTLSeconds returns the default TTL in seconds for cache items
|
||||
func (c *Config) GetCacheDefaultTTLSeconds() int {
|
||||
if c.Cache.DefaultTTLSeconds <= 0 {
|
||||
return 300
|
||||
}
|
||||
return c.Cache.DefaultTTLSeconds
|
||||
}
|
||||
|
||||
// GetCacheCleanupIntervalSeconds returns the cleanup interval in seconds for cache
|
||||
func (c *Config) GetCacheCleanupIntervalSeconds() int {
|
||||
if c.Cache.CleanupIntervalSeconds <= 0 {
|
||||
return 600
|
||||
}
|
||||
return c.Cache.CleanupIntervalSeconds
|
||||
}
|
||||
|
||||
// GetDatabaseHost returns the database host
|
||||
func (c *Config) GetDatabaseHost() string {
|
||||
if c.Database.Host == "" {
|
||||
|
||||
Reference in New Issue
Block a user