fix(admin): bound owner-email enrichment concurrency to avoid self-throttle
A large concurrent burst of getUserById calls during the tunnels-list email enrichment self-inflicts an upstream throttle (truncated/empty bodies) that even the per-call retry can't fully escape, intermittently rendering owner_email as '—'. Add mapWithConcurrency and resolve owner emails at most a few at a time so each lookup stays inside the throttle allowance; retry + null fallback preserved.
This commit is contained in:
@@ -126,3 +126,33 @@ export async function withAdminRetry<R extends { error: MaybeError }>(
|
||||
if (threw) throw lastThrown;
|
||||
return lastResult as R;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map over `items` running at most `concurrency` async tasks at a time, while
|
||||
* preserving result order.
|
||||
*
|
||||
* Firing every GoTrue admin lookup at once (e.g. one `getUserById` per tunnel
|
||||
* row) can self-inflict an upstream throttle: the proxy truncates the tail of a
|
||||
* large concurrent burst, producing the very empty-body responses we retry on —
|
||||
* and because the retries fire back into the same saturated window, a few rows
|
||||
* can still fail. Bounding the concurrency keeps each lookup inside the
|
||||
* throttle's allowance so {@link withAdminRetry} reliably resolves every row.
|
||||
*/
|
||||
export async function mapWithConcurrency<T, R>(
|
||||
items: readonly T[],
|
||||
concurrency: number,
|
||||
task: (item: T, index: number) => Promise<R>,
|
||||
): Promise<R[]> {
|
||||
const results = new Array<R>(items.length);
|
||||
const limit = Math.max(1, Math.min(concurrency, items.length || 1));
|
||||
let next = 0;
|
||||
async function worker(): Promise<void> {
|
||||
for (;;) {
|
||||
const i = next++;
|
||||
if (i >= items.length) return;
|
||||
results[i] = await task(items[i], i);
|
||||
}
|
||||
}
|
||||
await Promise.all(Array.from({ length: limit }, () => worker()));
|
||||
return results;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user