From b6ed7627240cac679401ccf8aa213d87a51be846 Mon Sep 17 00:00:00 2001 From: Owen Date: Thu, 9 Jul 2026 10:18:26 -0400 Subject: [PATCH] Sort before inserting --- server/routers/newt/pingAccumulator.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/server/routers/newt/pingAccumulator.ts b/server/routers/newt/pingAccumulator.ts index f82190790..6d788cc84 100644 --- a/server/routers/newt/pingAccumulator.ts +++ b/server/routers/newt/pingAccumulator.ts @@ -94,7 +94,13 @@ async function flushSitePingsToDb(): Promise { const pingsToFlush = new Map(pendingSitePings); pendingSitePings.clear(); - const entries = Array.from(pingsToFlush.entries()); + // Sort by id ascending so concurrent writers (other app replicas, or + // unrelated single-row updates elsewhere in the app) always acquire row + // locks in the same order. Without this, overlapping batches/updates + // that touch the same rows in different orders can deadlock each other. + const entries = Array.from(pingsToFlush.entries()).sort( + ([a], [b]) => a - b + ); const BATCH_SIZE = 50; for (let i = 0; i < entries.length; i += BATCH_SIZE) { @@ -193,7 +199,11 @@ async function flushClientPingsToDb(): Promise { // ── Flush client pings ───────────────────────────────────────────── if (pingsToFlush.size > 0) { - const entries = Array.from(pingsToFlush.entries()); + // Sort ascending for consistent lock ordering (see note in + // flushSitePingsToDb). + const entries = Array.from(pingsToFlush.entries()).sort( + ([a], [b]) => a - b + ); const BATCH_SIZE = 50; for (let i = 0; i < entries.length; i += BATCH_SIZE) {