Skip to content

fix: connector close leaks for in-flight queries#566

Open
panavenue wants to merge 1 commit into
mainfrom
fix-562
Open

fix: connector close leaks for in-flight queries#566
panavenue wants to merge 1 commit into
mainfrom
fix-562

Conversation

@panavenue
Copy link
Copy Markdown
Contributor

Change Description

Problem

When a Node.js process receives a termination signal (e.g., SIGTERM), typical graceful shutdown sequences call await pool.end() followed by connector.close(). However, as reported in #562, if there are active database queries mid-flight when shutdown begins, the TLS sockets remain alive in the event loop indefinitely, preventing the Node.js process from exiting cleanly.

There were three root causes contributing to this leak:

  • Ignored Sockets: CloudSQLInstance.addSocket explicitly ignored sockets unless they connected via custom domain name routing (if (!this.instanceInfo.domainName) return;). Standard connections using an instance connection name were never tracked.
  • Asynchronous Cleanup Race: Connector.close() resolved instance closure asynchronously in the promise microtask queue (instance.promise.then(inst => inst.close())). This ran too late, racing with database pool closures and failing to terminate active network handles synchronously.
  • Incorrect Socket Event Listener: The connector registered a listener for the non-existent 'closed' socket event (socket.once('closed', ...)) instead of the standard Node.js 'close' event. As a result, sockets were never pruned from the tracking set even when closed normally, causing a silent reference memory leak.

Solution

This PR resolves the socket leaks and ensures clean shutdown of standard connection pools:

  1. Universal Socket Tracking: Removed the domainName guard clause in CloudSQLInstance.addSocket to track all active TLS sockets regardless of the connection route.
  2. Synchronous Instance Teardown: Updated Connector.close() to check if a cached instance is already resolved (entry.isResolved()) and close it synchronously. Unresolved instances fall back safely to asynchronous promise resolution.
  3. Correct Socket Close Event: Updated the cleanup event listener to hook into Node's 'close' event, ensuring that normally terminated sockets are properly pruned from the internal set.
  4. Safe Stream Teardown: Added a check to ensure socket.destroy exists and is a function before calling it, maintaining backward compatibility with mock socket streams used in some test environments.

Checklist

  • Make sure to open an issue as a
    bug/issue
    before writing your code! That way we can discuss the change, evaluate
    designs, and agree on the general idea.
  • Ensure the tests and linter pass
  • Appropriate documentation is updated (if necessary)

Relevant issues:

  • Fixes #<issue_number_goes_here>

@panavenue panavenue requested a review from a team as a code owner May 19, 2026 04:19
@panavenue panavenue requested a review from hessjcg May 19, 2026 04:24
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.

3 participants