perf(desktop): cap aux backfill request concurrency

Finding L3-3: fetchChunkedAuxEvents awaited each auxiliary-event chunk sequentially, delaying visible reaction/edit/deletion backfills on large history windows.

Run aux chunks through a small shared concurrency helper so relay history requests overlap without unbounded fanout. The helper preserves chunk result order and the existing fail-fast semantics.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
This commit is contained in:
npub1t2tgm7d8f995uqvmnm8h88sg3wnpp9a5xysjf6dg3tjmgt3ltulqdp8ehr
2026-07-08 10:45:18 -04:00
co-authored by Tyler Longwell
parent 261e047d5a
commit 9da294968c
2 changed files with 28 additions and 5 deletions
+20
View File
@@ -0,0 +1,20 @@
export async function collectWithConcurrency<T, R>(
items: T[],
concurrency: number,
worker: (item: T) => Promise<R>,
): Promise<R[]> {
const workerCount = Math.min(Math.max(1, concurrency), items.length);
const results = new Array<R>(items.length);
let nextIndex = 0;
await Promise.all(
Array.from({ length: workerCount }, async () => {
while (nextIndex < items.length) {
const currentIndex = nextIndex++;
results[currentIndex] = await worker(items[currentIndex]);
}
}),
);
return results;
}
+8 -5
View File
@@ -29,6 +29,7 @@ import {
buildChannelMentionFilter,
buildGlobalStreamFilter,
} from "@/shared/api/relayChannelFilters";
import { collectWithConcurrency } from "@/shared/api/concurrency";
import { replayLiveSubscriptions } from "@/shared/api/relayReconnectReplay";
import { RelayConnectionStateEmitter } from "@/shared/api/relayConnectionStateEmitter";
import {
@@ -41,7 +42,8 @@ import { buildThreadReferenceTags } from "@/features/messages/lib/threading";
const RECONNECT_BASE_DELAY_MS = 1_000,
RECONNECT_MAX_DELAY_MS = 30_000,
EVENT_BATCH_MS = 16;
EVENT_BATCH_MS = 16,
AUX_BACKFILL_CONCURRENCY = 4;
/**
* Passive liveness check. The relay sends heartbeat pings every 30s; if no
@@ -217,10 +219,11 @@ export class RelayClient {
chunks.push(eventIds.slice(i, i + AUX_BACKFILL_CHUNK_SIZE));
}
const batches: RelayEvent[][] = [];
for (const ids of chunks) {
batches.push(await this.requestHistory(buildFilter(channelId, ids)));
}
const batches = await collectWithConcurrency(
chunks,
AUX_BACKFILL_CONCURRENCY,
(ids) => this.requestHistory(buildFilter(channelId, ids)),
);
return batches.flat();
}