| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| import { db, providerLatencySamples } from "@workspace/db"; |
| import { sql } from "drizzle-orm"; |
| import { logger } from "../lib/logger"; |
| import { |
| _setSampleDirtyHook, |
| getRecentSamplesFor, |
| restoreRecentSamples, |
| } from "./gateway"; |
|
|
| const FLUSH_INTERVAL_MS = 5_000; |
|
|
| const dirty = new Set<string>(); |
| let flushTimer: NodeJS.Timeout | null = null; |
|
|
| function dirtyKey(adapter: string, source: string): string { |
| return `${adapter}\u0000${source}`; |
| } |
|
|
| |
| |
| |
| |
| |
| export async function loadPersistedLatencySamples(): Promise<void> { |
| try { |
| const rows = await db |
| .select({ |
| adapter: providerLatencySamples.adapter, |
| source: providerLatencySamples.source, |
| samples: providerLatencySamples.samples, |
| }) |
| .from(providerLatencySamples); |
| let restored = 0; |
| for (const r of rows) { |
| const samples = Array.isArray(r.samples) ? r.samples : []; |
| restoreRecentSamples(r.adapter, r.source, samples); |
| restored += samples.length; |
| } |
| logger.info( |
| { providers: rows.length, samples: restored }, |
| "restored persisted provider latency samples", |
| ); |
| } catch (err) { |
| logger.warn( |
| { err }, |
| "failed to load persisted provider latency samples; sparkline will start empty", |
| ); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| export function startLatencySamplePersistence(): void { |
| if (flushTimer) return; |
| _setSampleDirtyHook((adapter, source) => { |
| dirty.add(dirtyKey(adapter, source)); |
| }); |
| flushTimer = setInterval(() => { |
| void flushDirtySamples(); |
| }, FLUSH_INTERVAL_MS); |
| flushTimer.unref?.(); |
| } |
|
|
| |
| export function stopLatencySamplePersistence(): void { |
| if (flushTimer) { |
| clearInterval(flushTimer); |
| flushTimer = null; |
| } |
| _setSampleDirtyHook(null); |
| dirty.clear(); |
| } |
|
|
| |
| |
| |
| |
| |
| export async function flushDirtySamples(): Promise<void> { |
| if (dirty.size === 0) return; |
| const batch = Array.from(dirty); |
| dirty.clear(); |
| for (const key of batch) { |
| const [adapter, source] = key.split("\u0000"); |
| if (!adapter || !source) continue; |
| const samples = getRecentSamplesFor(adapter, source); |
| try { |
| await db |
| .insert(providerLatencySamples) |
| .values({ adapter, source, samples }) |
| .onConflictDoUpdate({ |
| target: [ |
| providerLatencySamples.adapter, |
| providerLatencySamples.source, |
| ], |
| set: { samples, updatedAt: sql`NOW()` }, |
| }); |
| } catch (err) { |
| logger.warn( |
| { err, adapter, source }, |
| "failed to persist provider latency samples", |
| ); |
| } |
| } |
| } |
|
|