diff --git a/src/cloud-sql-instance.ts b/src/cloud-sql-instance.ts index 23b1506b..3dc2e329 100644 --- a/src/cloud-sql-instance.ts +++ b/src/cloud-sql-instance.ts @@ -364,12 +364,14 @@ export class CloudSQLInstance { this.checkDomainID = null; } for (const socket of this.sockets) { - socket.destroy( - new CloudSQLConnectorError({ - code: 'ERRCLOSED', - message: 'The connector was closed.', - }) - ); + if (typeof socket.destroy === 'function') { + socket.destroy( + new CloudSQLConnectorError({ + code: 'ERRCLOSED', + message: 'The connector was closed.', + }) + ); + } } } @@ -391,15 +393,15 @@ export class CloudSQLInstance { } } addSocket(socket: DestroyableSocket) { - if (!this.instanceInfo.domainName) { - // This was not connected by domain name. Ignore all sockets. - return; - } - - // Add the socket to the list + // Track all active sockets created by this instance so they can + // be forcefully cleaned up during a domain change or when + // the connector is explicitly closed. this.sockets.add(socket); - // When the socket is closed, remove it. - socket.once('closed', () => { + + // When the socket is closed by the driver or peer, remove it + // from our tracking set to prevent reference memory leaks. + // Note: Node.js TLSSocket/Socket emits 'close', not 'closed'. + socket.once('close', () => { this.sockets.delete(socket); }); } diff --git a/src/connector.ts b/src/connector.ts index 37848473..111ad141 100644 --- a/src/connector.ts +++ b/src/connector.ts @@ -358,8 +358,17 @@ export class Connector { // // Also clear up any local proxy servers and socket connections. close(): void { - for (const instance of this.instances.values()) { - instance.promise.then(inst => inst.close()); + for (const entry of this.instances.values()) { + if (entry.isResolved() && entry.instance) { + // If the instance is already resolved, close it synchronously. + // This prevents a race condition with immediate connection pool close + // (e.g., pool.end()) where asynchronous microtasks would execute too late. + entry.instance.close(); + } else { + // Otherwise, close the instance asynchronously once its initial + // refresh has finished. + entry.promise.then(inst => inst.close()).catch(() => {}); + } } for (const server of this.localProxies) { server.close();