Skip to content

Fix unhandled rejections when Cloudflare sockets close - #1251

Closed
advitrocks9 wants to merge 2 commits into
porsager:masterfrom
advitrocks9:fix/cf-socket-close
Closed

advitrocks9 wants to merge 2 commits into
porsager:masterfrom
advitrocks9:fix/cf-socket-close

Conversation

@advitrocks9

@advitrocks9 advitrocks9 commented Oct 10, 2026 •

Copy link
Copy Markdown

Every sql.end() on the Cloudflare build leaves two unhandled rejections.

end() closed the socket right after sending Terminate, which cancelled the pending read. The read then emitted 'error' after connection.js had removed its listeners, and workerd also reports the cancelled read itself.

Now end() sends Terminate and closes only the write side, like socket.end() in Node, and the socket closes when the read reaches EOF. This works with servers that close on Terminate and with ones that wait for EOF, like PGlite. error() is ignored once the socket is closed, and readFirst() gets the same catch as read().

Tested in workerd with one query per client, then await sql.end():

Postgres, 20 clients PGlite, 10 clients
master 40 unhandled 20 unhandled
this PR 0 0

Fixes #1196, fixes #1202

@matthew-gizmo

Copy link
Copy Markdown

Thanks for this fix. We backported the readFirst() catch and the error() changes (the closed-state guard and calling close()) onto postgres@3.4.9. In our workerd/Hyperdrive/PGlite test setup, those changes eliminated the unhandled socket errors on intentional shutdown.

One compatibility finding: we tried the end(data) change from tcp.write(data, () => tcp.raw.close()) to tcp.write(data), but in our PGlite-backed setup the server did not close its side after Terminate. This left clients open and caused persistence timeouts of around 4 seconds. We therefore retained the existing explicit raw-socket close while keeping the error-handling fixes.

Sharing this as a PGlite-backed test compatibility observation; we have not established that the same behavior occurs against a regular PostgreSQL server. Our regression executes select 1 through a connected Hyperdrive client and then awaits sql.end() with no timeout option; the full integration suite passed with zero unhandled errors using the partial backport.

@advitrocks9

Copy link
Copy Markdown
Author

Thanks, good catch. b0922e4 closes only the write side after Terminate, so PGlite closes too.

@porsager

Copy link
Copy Markdown
Owner

Thanks! This is part of v4 (06cf284), so I am closing it here. The changelog has the details: https://github.com/porsager/postgres/blob/master/CHANGELOG.md

@porsager porsager closed this Oct 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants