feat(content): eager invalidation and optimistic lock for cqrs
M4: ADR-038 Eager Invalidation + ADR-039 Optimistic Lock - EagerInvalidationService: Redis DEL after MySQL commit - Cache key conventions for textbook/chapter/kp/question - Version header check (If-Match/X-Expected-Version) on write endpoints - 409 Conflict on version mismatch - All write endpoints return updatedAt timestamp
This commit is contained in:
44
services/content/src/config/redis.ts
Normal file
44
services/content/src/config/redis.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { Redis } from "ioredis";
|
||||
import type { Redis as RedisClient } from "ioredis";
|
||||
import { env } from "./env.js";
|
||||
|
||||
// Redis Client 惰性初始化:未配置 REDIS_URL 时 client 保持 null,
|
||||
// 服务仍可正常启动。所有依赖 Redis 的功能(Eager Invalidation)在
|
||||
// client 为 null 时优雅降级(跳过失效,由 Kafka projector 兜底),
|
||||
// 不会阻塞主流程。
|
||||
let client: RedisClient | null = null;
|
||||
let initAttempted = false;
|
||||
|
||||
export function getRedis(): RedisClient | null {
|
||||
if (initAttempted) {
|
||||
return client;
|
||||
}
|
||||
initAttempted = true;
|
||||
if (!env.REDIS_URL) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
client = new Redis(env.REDIS_URL, {
|
||||
maxRetriesPerRequest: 1,
|
||||
enableReadyCheck: true,
|
||||
lazyConnect: false,
|
||||
});
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
"Redis client init failed, running without Redis:",
|
||||
err instanceof Error ? err.message : String(err),
|
||||
);
|
||||
client = null;
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭 Redis Client 连接。
|
||||
*/
|
||||
export async function closeRedis(): Promise<void> {
|
||||
if (client) {
|
||||
await client.quit();
|
||||
client = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user