Sessions 5-7a: 955 tests, deployment ready

This commit is contained in:
Kev
2026-06-08 18:35:13 -04:00
parent 06b82624a2
commit 1fa04dc776
371 changed files with 49366 additions and 955 deletions
+78 -2
View File
@@ -1,12 +1,88 @@
const Redis = require('ioredis');
// Redis is a cache, not a source of truth. If it's unreachable, callers
// should fall back to the underlying data store (Supabase). All higher-level
// helpers here return null on connection failure rather than throwing —
// that keeps every consumer single-branch (`if (cached) return cached`).
let client = null;
let degraded = false;
function getRedisClient() {
if (!client) {
client = new Redis(process.env.REDIS_URL || 'redis://127.0.0.1:6379');
client = new Redis(process.env.REDIS_URL || 'redis://127.0.0.1:6379', {
// Don't block the app starting up if Redis is down at boot. Failed
// commands fall through to the cache-miss path immediately.
lazyConnect: false,
enableOfflineQueue: false,
maxRetriesPerRequest: 1,
retryStrategy(times) {
// Exponential backoff up to 30s. ioredis keeps trying forever on its
// own; we just slow it down so the logs aren't a hose.
return Math.min(times * 1000, 30_000);
},
});
client.on('error', (err) => {
// Only log the first failure to avoid log spam during outages.
if (!degraded) {
console.warn('[redis] entering degraded mode:', err.message);
degraded = true;
}
});
client.on('ready', () => {
if (degraded) console.info('[redis] reconnected, leaving degraded mode');
degraded = false;
});
}
return client;
}
module.exports = { getRedisClient };
function isDegraded() {
return degraded;
}
async function cacheGet(key) {
if (degraded) return null;
try {
const raw = await getRedisClient().get(key);
if (!raw) return null;
try {
return JSON.parse(raw);
} catch {
// Non-JSON values are returned as-is (some callers store plain strings).
return raw;
}
} catch (err) {
if (!degraded) console.warn('[redis] cacheGet failed:', err.message);
return null;
}
}
async function cacheSet(key, value, ttlSeconds) {
if (degraded) return false;
try {
const payload = typeof value === 'string' ? value : JSON.stringify(value);
if (ttlSeconds && ttlSeconds > 0) {
await getRedisClient().set(key, payload, 'EX', ttlSeconds);
} else {
await getRedisClient().set(key, payload);
}
return true;
} catch (err) {
if (!degraded) console.warn('[redis] cacheSet failed:', err.message);
return false;
}
}
async function cacheDel(key) {
if (degraded) return false;
try {
await getRedisClient().del(key);
return true;
} catch (err) {
if (!degraded) console.warn('[redis] cacheDel failed:', err.message);
return false;
}
}
module.exports = { getRedisClient, cacheGet, cacheSet, cacheDel, isDegraded };