Skip to content

Conversation

@hessjcg
Copy link
Collaborator

@hessjcg hessjcg commented Mar 5, 2025

Add CloudSqlInstance.close() to stop the refresh cycle permanently.

Change CloudSqlInstance.forceRefresh() so that it schedules a refresh and returns immediately.
This should not return a promise. Update test cases to account for changes.

…nd return immediately.

This avoids the situation where a refresh process is forced to start but may never finish, causing the 
node process to hang indefiniately awaiting the refresh promise that does not resolve. This also adds
a new method used in the test cases: CloudSqlInstance.refreshComplete() which returns a promise that 
will resolve when the refresh operation has finished.
@hessjcg hessjcg requested a review from a team as a code owner March 5, 2025 16:40
@hessjcg hessjcg changed the title refactor: Improve the refresh to allow connectors to close. refactor: Improve the refresh to allow connectors to close, part of #421. Mar 5, 2025
@hessjcg hessjcg force-pushed the gh-421-refactor-refresh branch from 8743264 to 51dfbe4 Compare March 5, 2025 17:21
src/connector.ts Outdated
// The Connector class is the main public API to interact
// with the Cloud SQL Node.js Connector.
export class Connector {
private closed = false;
Copy link
Collaborator

Choose a reason for hiding this comment

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

is closed really needed at the Connector level? Instance makes sense but what is the benefit for Connector level...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This isn't necessary. I removed it.

// refreshComplete Returns a promise that resolves when the current refresh
// cycle has completed, either with success or failure. If no refresh is
// in progress, the promise will resolve immediately.
refreshComplete(): Promise<void> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't fully understand this... how is this different then awaiting the promise of forceRefresh?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good point. I've updated forceRefresh() to include all the logic.

Basically, forceRefresh() only needs to return a promise for testing. forceRefresh is used as fire-and-forget in the main connector code.

In tests, the promise should resolve regardless of whether it succeeds or fails. Otherwise, it could cause tests cases to hang where refresh fails.

@hessjcg hessjcg requested a review from kgala2 March 7, 2025 15:50
@hessjcg hessjcg force-pushed the gh-421-refactor-refresh branch from 51dfbe4 to 75fdb37 Compare March 10, 2025 16:49
src/connector.ts Outdated
// The Connector class is the main public API to interact
// with the Cloud SQL Node.js Connector.
export class Connector {
private closed = false;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This isn't necessary. I removed it.

});
tlsSocket.once('error', async () => {
await cloudSqlInstance.forceRefresh();
tlsSocket.once('error', () => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This should not be async. Otherwise it will block the exit of the connector until the refresh is successful. This causes some failure test cases to hang and never exit.

// refreshComplete Returns a promise that resolves when the current refresh
// cycle has completed, either with success or failure. If no refresh is
// in progress, the promise will resolve immediately.
refreshComplete(): Promise<void> {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good point. I've updated forceRefresh() to include all the logic.

Basically, forceRefresh() only needs to return a promise for testing. forceRefresh is used as fire-and-forget in the main connector code.

In tests, the promise should resolve regardless of whether it succeeds or fails. Otherwise, it could cause tests cases to hang where refresh fails.

@hessjcg hessjcg requested a review from jackwotherspoon March 10, 2025 16:50
@hessjcg hessjcg force-pushed the gh-421-refactor-refresh branch 2 times, most recently from 54ef048 to d838204 Compare March 10, 2025 16:55
this.scheduleRefresh(0);

return new Promise(resolve => {
// setImmediate() to yield execution to allow other refresh background
Copy link
Collaborator

Choose a reason for hiding this comment

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

this comment references setImmediate but we are using setTimeout...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed. setTimeout() is the correct function.

Comment on lines 216 to 217
// performRefresh() could be delayed by the rate limiter. So
// this instance may have been closed.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we update this wording a bit. "So this instance may have been closed" is a bit ambiguous...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.

Comment on lines +504 to +507
// starts a new refresh cycle but do not await on it
instance.close();
await instance.forceRefresh();
t.equal(metadataCount, 1, 'No refresh after close');
Copy link
Collaborator

Choose a reason for hiding this comment

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

does this mean we do not error on a closed instance?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We don't throw an error from forceRefresh(). The node connector will throw an error when trying to call connect() on a closed instance, but that's coming in a later PR.

@hessjcg hessjcg force-pushed the gh-421-refactor-refresh branch from d838204 to 330c007 Compare March 10, 2025 18:07
@hessjcg hessjcg force-pushed the gh-421-refactor-refresh branch from 330c007 to f4f69c1 Compare March 10, 2025 18:13
@hessjcg
Copy link
Collaborator Author

hessjcg commented Mar 10, 2025

Code Coverage / coverage decreased from 97% to 96% due to code paths in src/cloud-sql-instance.ts that are called after the CloudSQLInstance is closed. These code paths are impractical to test.

Copy link
Collaborator

@jackwotherspoon jackwotherspoon left a comment

Choose a reason for hiding this comment

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

LGTM ✅

@hessjcg hessjcg merged commit 0e4853a into main Mar 11, 2025
25 of 26 checks passed
@hessjcg hessjcg deleted the gh-421-refactor-refresh branch March 11, 2025 16:58
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