Skip to content

cherry-pick: fix(queue): waitForDrain poll fallback for drains that never emit (#1643)#1733

Merged
kriszyp merged 1 commit into
v5.1from
cherry-pick/v5.1/pr-1643-redo
Jul 9, 2026
Merged

cherry-pick: fix(queue): waitForDrain poll fallback for drains that never emit (#1643)#1733
kriszyp merged 1 commit into
v5.1from
cherry-pick/v5.1/pr-1643-redo

Conversation

@kriszyp

@kriszyp kriszyp commented Jul 9, 2026

Copy link
Copy Markdown
Member

Manual redo of the automated cherry-pick (#1692), which left unresolved conflict markers from a stale branch point (pre-#1621/#1709/#1584).

Clean cherry-pick of #1643's single squash commit onto current v5.1 tip.

Verified: unitTests/resources/IterableEventQueue.test.js — 5/5 passing.

Closes the stalled #1692.

)

* fix(queue): poll fallback so waitForDrain cannot hang on a queue emptied without a drained emit

* feat(table): expose evictionMS beside expirationMS

* fix(queue): remove the drained listener when settle fires via the poll (review)

* chore: format Table.ts with the pinned prettier

---------

Co-authored-by: Kris Zyp <kriszyp@gmail.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a polling fallback mechanism in IterableEventQueue.ts to prevent waitForDrain from hanging when the queue empties without emitting a drained event, adds a static getter evictionMS to Table.ts, and includes a unit test for the new queue draining behavior. The reviewer identified a potential ReferenceError (Temporal Dead Zone) and memory leak in IterableEventQueue.ts if settle is executed synchronously before the poll interval is initialized, and provided a code suggestion to resolve this issue.

Comment on lines +45 to +56
const settle = (drained: boolean) => {
clearInterval(poll);
this.removeListener('drained', onDrained);
resolve(drained);
};
const onDrained = () => settle(true);
this.once('drained', onDrained);
this.currentDrainResolver = settle;
const poll = setInterval(() => {
if (!this.queue || this.queue.length === 0) settle(true);
}, 100);
poll.unref?.();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

If settle is called synchronously (for example, if drained is emitted synchronously during this.once('drained', onDrained) or if close is emitted synchronously), calling clearInterval(poll) will throw a ReferenceError: Cannot access 'poll' before initialization due to the Temporal Dead Zone (TDZ) because poll is declared with const below settle.

Additionally, if settle runs synchronously, the interval poll will still be created afterwards and leak.

To fix this, we should declare poll with let at the top of the block, use a settled flag to avoid starting the interval if settle has already run, and clear this.currentDrainResolver to prevent memory leaks.

				let poll: any;
				let settled = false;
				const settle = (drained: boolean) => {
					settled = true;
					if (poll) clearInterval(poll);
					this.removeListener('drained', onDrained);
					if (this.currentDrainResolver === settle) {
						this.currentDrainResolver = null;
					}
					resolve(drained);
				};
				const onDrained = () => settle(true);
				this.once('drained', onDrained);
				this.currentDrainResolver = settle;
				if (!settled) {
					poll = setInterval(() => {
						if (!this.queue || this.queue.length === 0) settle(true);
					}, 100);
					poll.unref?.();
				}

@claude

claude Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Reviewed; no blockers found.

@kriszyp kriszyp merged commit 04866ad into v5.1 Jul 9, 2026
49 checks passed
@kriszyp kriszyp deleted the cherry-pick/v5.1/pr-1643-redo branch July 9, 2026 03:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants