Estrategia de Arquitectura de Caché
De Wikiprompt, la enciclopedia libre de prompts
Estrategia de Arquitectura de Caché Un mensaje de sistema integral para diseñar arquitecturas de caché de múltiples capas de alto rendimiento utilizando Redis, Memcached, CDNs, etc. Incluye ejecución orientada a tareas, tareas principales, patrones de invalidación y listas de verificación detalladas.
Contenido del PromptGuardar
🌐
# Caching Architecture Plan
## Context
- **Performance Requirements**: The application requires sub-10ms read latency for hot data paths and p99 latency under 50ms. Current database latency averages 25ms (p50) and 80ms (p99), failing to meet SLAs.
- **Bottlenecks**: Database CPU saturation at 70% during peak loads; repetitive queries for identical data; slow response times for frequently accessed user profiles and product catalog items.
- **Access Patterns**: Read-heavy workload (95% reads / 5% writes) with high temporal locality on user sessions and product recommendations. Hot keys represent ~20% of data but receive ~80% of traffic.
- **Consistency Requirements**: User profile data requires strong consistency (max staleness: 0s). Product inventory requires eventual consistency (max staleness: 60s). Session data requires eventual consistency (max staleness: 5 minutes).
- **Infrastructure Constraints**: Existing AWS deployment with 3 application instances. Current caching: none in production. Database is PostgreSQL 14 (primary + 2 read replicas). Network latency between services: <1ms.
## Caching Architecture Plan
- [ ] **CACHE-PLAN-1.1 [CDN Layer Design]**:
- **Layer**: CDN
- **Technology**: Amazon CloudFront
- **Scope**: Static assets (images, CSS, JS), public product catalog pages (read-only)
- **Configuration**: TTL 24h for static assets, TTL 5m for catalog pages; use `Cache-Control: max-age=86400, stale-while-revalidate=86400` headers; enable compression (Brotli) and HTTP/2
- [ ] **CACHE-PLAN-1.2 [Application Cache Layer Design]**:
- **Layer**: Application (in-process)
- **Technology**: Caffeine (Java) - version 3.1.8
- **Scope**: Highly accessed user session data (hot keys), frequently used configuration data
- **Configuration**: Maximum size 10,000 entries per instance; TTL 5 minutes; eviction policy: LFU with maximum size; local caching only (no replication)
- [ ] **CACHE-PLAN-1.3 [Distributed Cache Layer Design]**:
- **Layer**: Distributed
- **Technology**: Redis 7.0 (ElastiCache, cluster mode enabled)
- **Scope**: User profile data, product catalog data, inventory counts, API response cache for aggregated endpoints
- **Configuration**: 3 shards, each with 1 replica; `maxmemory-policy: allkeys-lfu`; TTL per key type (see CACHE-ITEM sections); connection pooling with 50 connections per instance; TLS enabled; enable `notify-keyspace-events Ex` for expiration events
- [ ] **CACHE-PLAN-1.4 [Database Cache Layer Design]**:
- **Layer**: Database
- **Technology**: PostgreSQL 14 (read replicas) with statement-level caching
- **Scope**: Frequently executed join queries (product with category), aggregate queries (inventory levels)
- **Configuration**: 2 read replicas; circuit breaker for replica routing (fallback to primary); no TTL; invalidation via materialized view refresh every 30 seconds
## Caching Items
- [ ] **CACHE-ITEM-1.1 [User Profile Cache Implementation]**:
- **Description**: Cache user profile data in Redis to reduce database load. Key: `user:profile:{userId}`. Value: JSON-serialized profile (Protocol Buffers format).
- **Invalidation Strategy**: Write-through (update cache synchronously on profile change)
- **TTL and Eviction**: TTL 24h; eviction policy `allkeys-lfu`; jittered TTL (add ±30s random variation)
- **Validation**: Compare database and cache responses for 100 random users; assert equality and check p50/p99 latency against SLAs
- [ ] **CACHE-ITEM-1.2 [Product Catalog Cache Implementation]**:
- **Description**: Cache product catalog data (name, description, image URL) in Redis with CDN edge caching. Key: `product:catalog:{productId}`. Value: Protocol Buffer binary.
- **Invalidation Strategy**: Event-driven (publish invalidation event to Redis Stream on product update)
- **TTL and Eviction**: TTL 1h for Redis; TTL 24h for CDN; eviction policy `allkeys-lfu`; jittered TTL (add ±1 minute)
- **Validation**: Trigger product update API, verify CDN cache is purged within 30 seconds and Redis key is invalidated within 1 second
- [ ] **CACHE-ITEM-1.3 [Session Data Cache Implementation]**:
- **Description**: Cache user session data in Caffeine (local cache) with Redis as fallback. Key: `session:{sessionToken}`. Value: Serialized session object (MessagePack).
- **Invalidation Strategy**: Cache-aside (load from cache, fallback to database on miss); TTL-based expiration
- **TTL and Eviction**: TTL 5 minutes for local cache; TTL 30 minutes for Redis; LFU eviction for local; `volatile-ttl` for Redis expired keys
- **Validation**: Simulate session expiry and verify user is redirected to login; test cross-instance session persistence via Redis fallback
- [ ] **CACHE-ITEM-1.4 [Inventory Count Cache Implementation]**:
- **Description**: Cache inventory counts for product detail pages. Key: `inventory:count:{productId}`.
- **Invalidation Strategy**: Write-behind (asynchronously update cache after database write; publish event on change)
- **TTL and Eviction**: TTL 60 seconds; eviction policy `allkeys-lfu`; no jitter (short TTL)
- **Validation**: Place order, verify count is updated in cache within 1 second of database commit; verify no negative inventory is displayed
- [ ] **CACHE-ITEM-1.5 [API Response Cache Implementation]**:
- **Description**: Cache aggregated API responses (dashboard, recommendations) for anonymous users. Key: `api:response:{endpointHash}:{queryParamsHash}`.
- **Invalidation Strategy**: Cache-aside with short TTL; event-driven invalidation on underlying data change
- **TTL and Eviction**: TTL 5 minutes; eviction policy `allkeys-lfu`; jittered TTL (±15 seconds)
- **Validation**: Compare cached vs fresh API response; verify cache hit rate >90% for dashboard endpoint under load test
## Proposed Code Changes
### File: `src/main/java/com/example/cache/RedisConfig.java`
```diff
+@Configuration
+public class RedisConfig {
+ @Bean
+ public RedisConnectionFactory redisConnectionFactory() {
+ RedisClusterConfiguration clusterConfig = new RedisClusterConfiguration()
+ .clusterNode("redis-cluster-1.example.com", 6379)
+ .clusterNode("redis-cluster-2.example.com", 6379)
+ .clusterNode("redis-cluster-3.example.com", 6379);
+ clusterConfig.setPassword(RedisPassword.of("${REDIS_PASSWORD}"));
+ return new LettuceConnectionFactory(clusterConfig);
+ }
+
+ @Bean
+ public RedisTemplate<String, byte[]> redisTemplate() {
+ RedisTemplate<String, byte[]> template = new RedisTemplate<>();
+ template.setConnectionFactory(redisConnectionFactory());
+ template.setKeySerializer(new StringRedisSerializer());
+ template.setValueSerializer(new ByteArrayRedisSerializer());
+ return template;
+ }
+
+ @Bean
+ public CacheManager cacheManager(RedisConnectionFactory factory) {
+ RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
+ .entryTtl(Duration.ofMinutes(60))
+ .serializeValuesWith(SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
+ .disableCachingNullValues();
+ return RedisCacheManager.builder(factory)
+ .cacheDefaults(config)
+ .build();
+ }
+}
```
### File: `src/main/java/com/example/cache/UserProfileCacheService.java`
```diff
+@Service
+public class UserProfileCacheService {
+ private static final String KEY_PREFIX = "user:profile:";
+ private static final Duration TTL = Duration.ofHours(24);
+ private static final Duration TTL_JITTER = Duration.ofSeconds(30);
+
+ @Autowired
+ private RedisTemplate<String, byte[]> redisTemplate;
+
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ public UserProfile getProfile(String userId) {
+ byte[] cached = redisTemplate.opsForValue().get(KEY_PREFIX + userId);
+ if (cached != null) {
+ return deserialize(cached);
+ }
+ UserProfile profile = loadFromDatabase(userId);
+ if (profile != null) {
+ setProfile(userId, profile);
+ }
+ return profile;
+ }
+
+ public void setProfile(String userId, UserProfile profile) {
+ byte[] serialized = serialize(profile);
+ Duration ttlWithJitter = TTL.plus(Duration.ofMillis(ThreadLocalRandom.current().nextLong(-TTL_JITTER.toMillis(), TTL_JITTER.toMillis())));
+ redisTemplate.opsForValue().set(KEY_PREFIX + userId, serialized, ttlWithJitter);
+ }
+
+ @EventListener
+ public void handleProfileUpdate(UserProfileUpdatedEvent event) {
+ // Write-through invalidation
+ setProfile(event.getUserId(), event.getNewProfile());
+ // Publish invalidation event for other services
+ redisTemplate.convertAndSend("cache-invalidation", "user:" + event.getUserId());
+ }
+
+ private UserProfile loadFromDatabase(String userId) {
+ // Database query implementation
+ }
+
+ private byte[] serialize(UserProfile profile) {
+ // Protocol Buffers serialization
+ }
+
+ private UserProfile deserialize(byte[] data) {
+ // Protocol Buffers deserialization
+ }
+}
```
### File: `src/main/java/com/example/cache/CacheStampedePreventionAspect.java`
```diff
+@Aspect
+@Component
+public class CacheStampedePreventionAspect {
+ @Autowired
+ private RedisTemplate<String, byte[]> redisTemplate;
+
+ @Around("@annotation(CacheStampedeProtected)")
+ public Object preventStampede(ProceedingJoinPoint joinPoint) throws Throwable {
+ // Implement mutex lock with Redis SETNX
+ String lockKey = "lock:" + joinPoint.getSignature().getName();
+ Boolean acquired = redisTemplate.opsForValue().setIfAbsent(lockKey, "locked", Duration.ofSeconds(2));
+ if (acquired != null && acquired) {
+ try {
+ return joinPoint.proceed();
+ } finally {
+ redisTemplate.delete(lockKey);
+ }
+ }
+ // Wait and retry with exponential backoff
+ Thread.sleep(50);
+ return joinPoint.proceed();
+ }
+}
```
## Commands
### Local Development
```bash
# Run Redis locally for development
docker run -d -p 6379:6379 redis:7-alpine
# Run tests with local Redis
mvn test -Dspring.profiles.active=local
# Run integration tests with caching validation
mvn verify -Dspring.profiles.active=test -Dskip.unit.tests=false
# Load test with cache stampede scenario
./scripts/load-test.sh --scenario stampede --concurrency 100 --duration 60s
# Benchmark cache hit rates
./scripts/benchmark-cache.sh --endpoint /api/products --warmup 1000 --requests 10000
# Check Redis cluster health
redis-cli -h localhost -p 6379 cluster info
# Monitor cache metrics
./scripts/monitor-cache.sh --metrics hits,misses,evictions,memory
```
### CI Pipeline
```bash
# In CI: run with ephemeral Redis container
docker run -d --name redis-test -p 6379:6379 redis:7-alpine
mvn test -Dspring.profiles.active=ci
docker stop redis-test
# CI validation of cache invalidation
./scripts/validate-invalidation.sh --endpoint /api/users/{id} --change-frequency 100ms
# CI performance gates
mvn gatling:test -Dgatling.simulationClass=CachePerformanceSimulation
```
## Quality Assurance Task Checklist
- [ ] All cache layers are documented with technology, configuration, and data flow
- [ ] Invalidation strategies are defined for every cached data type (write-through for profiles, event-driven for catalog, cache-aside for sessions, write-behind for inventory)
- [ ] TTL values are justified with data volatility analysis (24h for stable profiles, 1h for catalog, 5m for sessions, 60s for inventory)
- [ ] Failure scenarios are handled with graceful degradation paths (fallback to database, circuit breakers, local cache as interim)
- [ ] Monitoring and alerting covers hit rates, latency, memory, and eviction metrics
- [ ] Cache key schema is documented with naming conventions and versioning (`user:profile:`, `product:catalog:`, `session:`, `inventory:count:`)
- [ ] Performance benchmarks validate that caching meets target SLAs (p50 < 10ms, p99 < 50ms)
- [ ] Security controls (TLS, authentication, network isolation) are applied to all cache endpoints
Iniciá sesión para ver el prompt completo
Continuar con:
Al iniciar sesión, aceptás nuestros Términos de uso y Política de privacidad
Uso
Este prompt está diseñado para usarse con coding. Copiá el contenido de arriba y pegalo en tu herramienta de IA preferida.
Para mejores resultados, personalizá los marcadores (indicados con corchetes o mayúsculas) con tus requisitos específicos.
Discusión
0 comentarios