fix(calls): one delayed-event restart in flight per id, with a timeout (#318)
CI / Build & Quality Checks (pull_request) Successful in 3m12s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 9s
CI / Playwright smoke (e2e) (pull_request) Successful in 15m31s

During a homeserver stall Element Call retries update_delayed_event every
2 s, and the host opened a new HTTP request each time: 12 pending in a
60 s stall, all landing together on recovery (404 x5, 429 x7). Share the
in-flight restart per delay id and abort it after 5 s.

Refs #318

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
This commit is contained in:
Lotus CI
2026-10-17 19:35:06 -04:00
co-authored by Claude Opus 5.5
parent 3878a85339
commit 4ddcda122a
3 changed files with 60 additions and 1 deletions
+32
View File
@@ -0,0 +1,32 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { dedupeInFlight } from './inFlight';
test('dedupeInFlight: one request per key while pending, a new one after (#318)', async () => {
const once = dedupeInFlight<number>();
let calls = 0;
let release!: () => void;
const gate = new Promise<void>((r) => {
release = r;
});
const run = async () => {
calls += 1;
await gate;
return calls;
};
const a = once('d1', run);
const b = once('d1', run);
const c = once('d2', run);
assert.equal(a, b);
release();
assert.deepEqual(await Promise.all([a, b, c]), [2, 2, 2]);
assert.equal(calls, 2);
await once('d1', run);
assert.equal(calls, 3, 'settled requests are not reused');
});
test('dedupeInFlight: a rejection clears the key', async () => {
const once = dedupeInFlight<void>();
await assert.rejects(once('k', () => Promise.reject(new Error('boom'))));
await once('k', () => Promise.resolve());
});
+14
View File
@@ -0,0 +1,14 @@
/**
* Share one in-flight promise per key: callers that ask again while a request
* for the same key is still pending get that request instead of a new one.
*/
export function dedupeInFlight<T>(): (key: string, run: () => Promise<T>) => Promise<T> {
const pending = new Map<string, Promise<T>>();
return (key, run) => {
const existing = pending.get(key);
if (existing) return existing;
const p = run().finally(() => pending.delete(key));
pending.set(key, p);
return p;
};
}