67abbf19b5
CI / Generate TypeScript types (push) Successful in 10s
CI / Server unit tests (push) Successful in 20s
CI / Build server (push) Successful in 26s
CI / Build client (push) Successful in 35s
CI / Playwright E2E tests (push) Failing after 1m56s
CI / Build and push Docker image (push) Has been skipped
CI / Notify (push) Successful in 1s
- Socket.io Redis adapter pro sdílený stav přes repliky - graceful shutdown serveru - WATCH/MULTI v updateData pro race-condition-safe aktualizace - lease mechanismus pro push reminder (zabrání duplicitnímu odesílání) - k8s/ manifesty pro testovací kind cluster - Dockerfile: opraven EXPOSE port na 3001 - .gitignore: ignorovány Claude pracovní soubory
68 lines
2.1 KiB
TypeScript
68 lines
2.1 KiB
TypeScript
import { RedisClientType, createClient } from 'redis';
|
|
import { StorageInterface } from "./StorageInterface";
|
|
|
|
let client: RedisClientType;
|
|
|
|
/**
|
|
* Implementace úložiště využívající Redis server.
|
|
*/
|
|
export default class RedisStorage implements StorageInterface {
|
|
constructor() {
|
|
const HOST = process.env.REDIS_HOST ?? 'localhost';
|
|
const PORT = process.env.REDIS_PORT ?? 6379;
|
|
client = createClient({ url: `redis://${HOST}:${PORT}` }) as RedisClientType;
|
|
}
|
|
|
|
async initialize() {
|
|
await client.connect();
|
|
}
|
|
|
|
async hasData(key: string) {
|
|
const data = await client.json.get(key);
|
|
return (!!data);
|
|
}
|
|
|
|
async getData<Type>(key: string) {
|
|
const data = await client.json.get(key, { path: '.' });
|
|
return data as Type;
|
|
}
|
|
|
|
async setData<Type>(key: string, data: Type) {
|
|
await client.json.set(key, '.', data as any);
|
|
}
|
|
|
|
async updateData<Type>(key: string, mutator: (current: Type | undefined) => Type): Promise<Type> {
|
|
return (client as any).executeIsolated(async (c: any) => {
|
|
for (let attempt = 0; attempt < 10; attempt++) {
|
|
await c.watch(key);
|
|
const current = await c.json.get(key, { path: '.' }) as Type | undefined;
|
|
const next = mutator(current);
|
|
const multi = c.multi();
|
|
multi.json.set(key, '.', next);
|
|
const result = await multi.exec();
|
|
if (result !== null) return next;
|
|
}
|
|
throw new Error(`updateData: optimistic lock failed after 10 retries for key: ${key}`);
|
|
});
|
|
}
|
|
|
|
async healthCheck(): Promise<boolean> {
|
|
try {
|
|
const pong = await client.ping();
|
|
return pong === 'PONG';
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Vrátí hlavní Redis klient — používá se pro lease připomínkovače a shutdown. */
|
|
export function getRedisClient(): RedisClientType | undefined {
|
|
return client;
|
|
}
|
|
|
|
/** Zavře připojení k Redisu. Volá se při graceful shutdown. */
|
|
export async function shutdownRedisStorage(): Promise<void> {
|
|
await client?.quit();
|
|
}
|