Skip to content

Commit f821da2

Browse files
authored
stream-management: Debounce ack requests (#1062)
So that they don't send after every stanza goes out but only when idle.
1 parent 1165c33 commit f821da2

File tree

2 files changed

+40
-1
lines changed

2 files changed

+40
-1
lines changed

packages/stream-management/index.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export default function streamManagement({
3030
}) {
3131
let timeoutTimeout = null;
3232
let requestAckTimeout = null;
33+
let requestAckDebounce = null;
3334

3435
const sm = new EventEmitter();
3536
Object.assign(sm, {
@@ -42,6 +43,7 @@ export default function streamManagement({
4243
max: null,
4344
timeout: 60_000,
4445
requestAckInterval: 30_000,
46+
requestAckDebounce: 250,
4547
});
4648

4749
async function sendAck() {
@@ -53,6 +55,7 @@ export default function streamManagement({
5355
entity.on("disconnect", () => {
5456
clearTimeout(timeoutTimeout);
5557
clearTimeout(requestAckTimeout);
58+
clearTimeout(requestAckDebounce);
5659
sm.enabled = false;
5760
});
5861

@@ -160,6 +163,7 @@ export default function streamManagement({
160163
function requestAck() {
161164
clearTimeout(timeoutTimeout);
162165
clearTimeout(requestAckTimeout);
166+
clearTimeout(requestAckDebounce);
163167

164168
if (!sm.enabled) return;
165169

@@ -181,7 +185,9 @@ export default function streamManagement({
181185

182186
sm.outbound_q.push({ stanza, stamp: datetime() });
183187
// Debounce requests so we send only one after a big run of stanza together
184-
queueMicrotask(requestAck);
188+
clearTimeout(requestAckTimeout);
189+
clearTimeout(requestAckDebounce);
190+
requestAckDebounce = setTimeout(requestAck, sm.requestAckDebounce);
185191

186192
return next();
187193
});

packages/stream-management/stream-features.test.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,39 @@ test("resume - failed with something in queue", async () => {
309309
expect(entity.streamManagement.outbound_q).toBeEmpty();
310310
});
311311

312+
test("sends an <r/> after stanzas, debounced", async () => {
313+
const { entity } = mockClient();
314+
315+
entity.streamManagement.enabled = true;
316+
317+
let r = 0;
318+
const onSend = (stanza) => {
319+
if (stanza.name === "r") r++;
320+
};
321+
entity.on("send", onSend);
322+
323+
jest.useFakeTimers();
324+
325+
let promise = entity.send(<message id="a" />);
326+
jest.advanceTimersByTime(50);
327+
await promise;
328+
expect(r).toBe(0);
329+
330+
promise = entity.send(<message id="b" />);
331+
jest.advanceTimersByTime(50);
332+
await promise;
333+
expect(r).toBe(0);
334+
335+
jest.advanceTimersByTime(1000);
336+
jest.useRealTimers();
337+
await tick();
338+
339+
expect(r).toBe(1);
340+
341+
entity.removeListener("send", onSend);
342+
await entity.disconnect();
343+
});
344+
312345
test("sends an <a/> element before closing", async () => {
313346
const { entity, streamManagement } = mockClient();
314347
streamManagement.enabled = true;

0 commit comments

Comments
 (0)