- Valkey client that fails commands at once while Valkey is down instead of queueing them, and logs connection errors once - ValkeyLiveGameStore: live game snapshots and finished games waiting to be saved, loaded with SCAN on startup - ValkeyCache: JSON values with a TTL; errors count as cache misses - Integration tests against a real Valkey, including an unreachable one
41 lines
1.3 KiB
TypeScript
41 lines
1.3 KiB
TypeScript
import { Valkey } from "iovalkey";
|
|
|
|
const PREFIX = "mancala:cache:";
|
|
|
|
// Short-lived cache of JSON values. Never fails: errors count as a miss, so callers just compute the value.
|
|
export interface Cache {
|
|
getJson<T>(key: string): Promise<T | undefined>;
|
|
setJson(key: string, value: unknown, ttlSeconds: number): Promise<void>;
|
|
delete(...keys: string[]): Promise<void>;
|
|
}
|
|
|
|
export class ValkeyCache implements Cache {
|
|
constructor(private readonly client: Valkey) { }
|
|
|
|
async getJson<T>(key: string): Promise<T | undefined> {
|
|
try {
|
|
const value = await this.client.get(PREFIX + key);
|
|
return value === null ? undefined : JSON.parse(value) as T;
|
|
} catch (err) {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
async setJson(key: string, value: unknown, ttlSeconds: number): Promise<void> {
|
|
try {
|
|
await this.client.set(PREFIX + key, JSON.stringify(value), "EX", ttlSeconds);
|
|
} catch (err) {
|
|
// A missing cache entry only costs a database query.
|
|
}
|
|
}
|
|
|
|
async delete(...keys: string[]): Promise<void> {
|
|
if (keys.length === 0) return;
|
|
try {
|
|
await this.client.del(...keys.map((key) => PREFIX + key));
|
|
} catch (err) {
|
|
// Entries expire on their own.
|
|
}
|
|
}
|
|
}
|