diff --git a/server/routers/gerbil/receiveBandwidth.ts b/server/routers/gerbil/receiveBandwidth.ts index 042c844aa..787b7b702 100644 --- a/server/routers/gerbil/receiveBandwidth.ts +++ b/server/routers/gerbil/receiveBandwidth.ts @@ -96,6 +96,14 @@ async function dbQueryRows>( return (await anyDb.all(query)) as T[]; } +/** + * Returns true when the active database driver is SQLite (better-sqlite3). + * Used to select the appropriate bulk-update strategy. + */ +function isSQLite(): boolean { + return typeof (db as any).execute !== "function"; +} + /** * Flush all accumulated site bandwidth data to the database. * @@ -141,19 +149,37 @@ export async function flushSiteBandwidthToDb(): Promise { const chunk = sortedEntries.slice(i, i + BATCH_CHUNK_SIZE); const chunkEnd = i + chunk.length - 1; - // Build a parameterised VALUES list: (pubKey, bytesIn, bytesOut), ... - // Both PostgreSQL and SQLite (≥ 3.33.0, which better-sqlite3 bundles) - // support UPDATE … FROM (VALUES …), letting us update the whole chunk - // in a single query instead of N individual round-trips. - const valuesList = chunk.map(([publicKey, { bytesIn, bytesOut }]) => - sql`(${publicKey}, ${bytesIn}, ${bytesOut})` - ); - const valuesClause = sql.join(valuesList, sql`, `); - let rows: { orgId: string; pubKey: string }[] = []; try { rows = await withDeadlockRetry(async () => { + if (isSQLite()) { + // SQLite: one UPDATE per row — no need for batch efficiency here. + const results: { orgId: string; pubKey: string }[] = []; + for (const [publicKey, { bytesIn, bytesOut }] of chunk) { + const result = await dbQueryRows<{ + orgId: string; + pubKey: string; + }>(sql` + UPDATE sites + SET + "bytesOut" = COALESCE("bytesOut", 0) + ${bytesIn}, + "bytesIn" = COALESCE("bytesIn", 0) + ${bytesOut}, + "lastBandwidthUpdate" = ${currentTime} + WHERE "pubKey" = ${publicKey} + RETURNING "orgId", "pubKey" + `); + results.push(...result); + } + return results; + } + + // PostgreSQL: batch UPDATE … FROM (VALUES …) — single round-trip per chunk. + const valuesList = chunk.map( + ([publicKey, { bytesIn, bytesOut }]) => + sql`(${publicKey}, ${bytesIn}, ${bytesOut})` + ); + const valuesClause = sql.join(valuesList, sql`, `); return dbQueryRows<{ orgId: string; pubKey: string }>(sql` UPDATE sites SET