Keep HTTP responsive during confluence replay.
CI / Test (push) Canceled after 0s
CI / Build and push (push) Canceled after 0s

Boot replay was a tight CPU loop that wedged the Unraid backend, so login hit the 8s client timeout. Yield every few as-of dates, shorten the boot ledger fill, and make /health a cheap liveness check.
This commit is contained in:
Investor Flow Build
2026-09-09 10:16:36 -04:00
parent 7426103d73
commit 0b901fc190
3 changed files with 12 additions and 3 deletions
@@ -301,7 +301,12 @@ export async function runConfluenceReplay(
const weeklyFull = await provider.resolveAsOf(symbol, '1wk', '9999-12-31');
const benchFull = await provider.resolveAsOf(BENCHMARK_SYMBOL, '1d', '9999-12-31');
let asOfN = 0;
for (const asOf of slice) {
asOfN += 1;
if (asOfN % 4 === 0) {
await new Promise<void>((r) => setImmediate(r));
}
const dailyCut = daily.candles.filter((c) => (c.ts ?? '').slice(0, 10) <= asOf);
const weeklyCut = weeklyFull.candles.filter((c) => (c.ts ?? '').slice(0, 10) <= asOf);
const benchCut = benchFull.candles.filter((c) => (c.ts ?? '').slice(0, 10) <= asOf);
@@ -391,6 +396,7 @@ export async function fillLearningLedger(
complete: batch.complete,
};
if (batch.evaluationsStored === 0) break;
await new Promise<void>((r) => setImmediate(r));
}
return { ...last, ledger: await learningLedgerStatus(db, cache) };
}
+4 -2
View File
@@ -264,7 +264,7 @@ async function confluenceTick(label: string): Promise<void> {
} = await import('./confluence/confluenceEngine.ts');
const { CONFLUENCE_LEARNING_UNIVERSE } = await import('./confluence/confluenceSeed.ts');
const replay = label === 'boot'
? await fillLearningLedger(database, cache, 75_000)
? await fillLearningLedger(database, cache, 12_000)
: await runConfluenceReplay(database, cache, {
symbols: CONFLUENCE_LEARNING_UNIVERSE,
symbolsPerTick: LEARNING_REPLAY_SYMBOLS,
@@ -331,7 +331,9 @@ const server = createServer(async (req, res) => {
const url = new URL(req.url ?? '/', `http://localhost:${PORT}`);
if (url.pathname === '/health') {
res.writeHead(200, { 'content-type': 'application/json' });
res.end(JSON.stringify({ ok: true, queue: queue.health() }));
// Cheap liveness only. queue.health() is a full SQLite scan and must not
// ride the docker healthcheck / login path.
res.end(JSON.stringify({ ok: true }));
return;
}
if (url.pathname.startsWith('/api/trpc')) {
+2 -1
View File
@@ -1008,7 +1008,8 @@ export const api = {
},
auth: {
signup: (email: string, password: string) => trpcMutate<{ userId: string }>("auth.signup", { email, password }),
login: (email: string, password: string, totp?: string) => trpcMutate<{ userId: string }>("auth.login", { email, password, ...(totp ? { totp } : {}) }),
login: (email: string, password: string, totp?: string) =>
trpcMutate<{ userId: string }>("auth.login", { email, password, ...(totp ? { totp } : {}) }, 20_000),
logout: () => trpcMutate<{ ok: boolean }>("auth.logout", {}),
changePassword: (current: string, next: string) =>
trpcMutate<{ ok: boolean }>("auth.changePassword", { current, next }),