vault-video-processor / src /features /discovery /services /featured-creators-cache.service.ts
dvijaykrishnan's picture
Deployment fix for HF
ceb943f
Raw
History Blame Contribute Delete
3.38 kB
// Story 7.3: Redis caching service for featured creators
// Implements stale-while-revalidate pattern from Story 7.2
import { redis } from '@/lib/redis';
import type { FeaturedCreator, CachedFeaturedCreators } from '../types/discovery.types';
// Dual-key caching strategy (same pattern as trending)
const CACHE_TTL_PRIMARY = 7200; // 2 hours in seconds
const CACHE_TTL_STALE = 14400; // 4 hours in seconds
const CACHE_KEY = 'featured:creators:v1';
const CACHE_KEY_STALE_SUFFIX = ':stale';
/**
* Get cached featured creators with stale-while-revalidate pattern
*
* Flow:
* 1. Check primary cache first (2-hour TTL)
* 2. If miss, check stale cache (4-hour TTL)
* 3. If stale hit, return stale data (caller should trigger background refresh)
* 4. If both miss, return null
*
* Returns: { data, isStale } where isStale indicates if serving from stale cache
*/
export async function getCachedFeaturedCreators(): Promise<{ data: FeaturedCreator[]; isStale: boolean } | null> {
try {
// Try primary cache first
const primaryCached = await redis.get<CachedFeaturedCreators>(CACHE_KEY);
if (primaryCached) {
return { data: primaryCached.data, isStale: false };
}
// Primary cache miss - try stale cache
const staleCached = await redis.get<CachedFeaturedCreators>(
`${CACHE_KEY}${CACHE_KEY_STALE_SUFFIX}`
);
if (staleCached) {
// Serving stale data - caller should trigger background revalidation
return { data: staleCached.data, isStale: true };
}
// Both caches missed
return null;
} catch (error) {
console.error('Error reading from Redis cache:', error);
// Gracefully degrade to database on Redis failure
return null;
}
}
/**
* Set cached featured creators with dual-key strategy
* Writes to both primary (2h TTL) and stale (4h TTL) caches
*/
export async function setCachedFeaturedCreators(data: FeaturedCreator[]): Promise<void> {
try {
const now = Date.now();
const cachedData: CachedFeaturedCreators = {
data,
cachedAt: now,
expiresAt: now + (CACHE_TTL_PRIMARY * 1000),
};
// Write to primary cache (2 hour TTL)
await redis.set(CACHE_KEY, cachedData, {
ex: CACHE_TTL_PRIMARY,
});
// Write to stale cache (4 hour TTL)
await redis.set(
`${CACHE_KEY}${CACHE_KEY_STALE_SUFFIX}`,
cachedData,
{
ex: CACHE_TTL_STALE,
}
);
} catch (error) {
console.error('Error writing to Redis cache:', error);
// Don't throw - caching is optional, app should work without it
}
}
/**
* Invalidate featured creators cache (both primary and stale)
* Useful for manual cache busting or after data updates
*/
export async function invalidateFeaturedCreatorsCache(): Promise<void> {
try {
await redis.del(CACHE_KEY);
await redis.del(`${CACHE_KEY}${CACHE_KEY_STALE_SUFFIX}`);
} catch (error) {
console.error('Error invalidating Redis cache:', error);
}
}
/**
* Check if primary cache exists and is fresh
*/
export async function isCacheFresh(): Promise<boolean> {
try {
const cached = await redis.get<CachedFeaturedCreators>(CACHE_KEY);
if (!cached) {
return false;
}
const now = Date.now();
return now <= cached.expiresAt;
} catch (error) {
console.error('Error checking cache freshness:', error);
return false;
}
}