Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 16 additions & 14 deletions src/cloud-sql-instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.',
})
);
}
}
}

Expand All @@ -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);
});
}
Expand Down
13 changes: 11 additions & 2 deletions src/connector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Loading