Skip to content

Commit 1ba89f4

Browse files
committed
wip: periodic checks
1 parent dbd5554 commit 1ba89f4

File tree

4 files changed

+115
-22
lines changed

4 files changed

+115
-22
lines changed

src/cloud-sql-instance.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ interface CloudSQLInstanceOptions {
4545
ipType: IpAddressTypes;
4646
limitRateInterval?: number;
4747
sqlAdminFetcher: Fetcher;
48+
checkDomainInterval?: number
4849
}
4950

5051
interface RefreshResult {
@@ -77,9 +78,12 @@ export class CloudSQLInstance {
7778
// The ongoing refresh promise is referenced by the `next` property
7879
private next?: Promise<RefreshResult>;
7980
private scheduledRefreshID?: ReturnType<typeof setTimeout> | null = undefined;
81+
private checkDomainID?: ReturnType<typeof setTimeout> | null = undefined;
8082
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
8183
private throttle?: any;
8284
private closed = false;
85+
private checkDomainInterval:number;
86+
8387
public readonly instanceInfo: InstanceConnectionInfo;
8488
public ephemeralCert?: SslCert;
8589
public host?: string;
@@ -101,6 +105,7 @@ export class CloudSQLInstance {
101105
this.ipType = options.ipType || IpAddressTypes.PUBLIC;
102106
this.limitRateInterval = options.limitRateInterval || 30 * 1000; // 30 seconds
103107
this.sqlAdminFetcher = options.sqlAdminFetcher;
108+
this.checkDomainInterval = options.checkDomainInterval || 30* 1000;
104109
}
105110

106111
// p-throttle library has to be initialized in an async scope in order to
@@ -153,6 +158,11 @@ export class CloudSQLInstance {
153158
this.next = undefined;
154159
return Promise.reject('closed');
155160
}
161+
if(this?.instanceInfo?.domainName && ! this.checkDomainID){
162+
this.checkDomainID = setInterval(()=>{
163+
this.checkDomainChanged()
164+
}, this.checkDomainInterval || 30*1000)
165+
}
156166

157167
const currentRefreshId = this.scheduledRefreshID;
158168

@@ -297,8 +307,12 @@ export class CloudSQLInstance {
297307
// If refresh has not yet started, then cancel the setTimeout
298308
if (this.scheduledRefreshID) {
299309
clearTimeout(this.scheduledRefreshID);
310+
this.scheduledRefreshID = null
311+
}
312+
if(this.checkDomainID){
313+
clearInterval(this.checkDomainID);
314+
this.checkDomainID = null
300315
}
301-
this.scheduledRefreshID = null;
302316
}
303317

304318
// Mark this instance as having an active connection. This is important to

src/connector.ts

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ export declare interface ConnectionOptions {
4444
ipType?: IpAddressTypes;
4545
instanceConnectionName: string;
4646
domainName?: string;
47+
checkDomainInterval?: number
48+
limitRateInterval?: number
4749
}
4850

4951
export declare interface SocketConnectionOptions extends ConnectionOptions {
@@ -139,8 +141,9 @@ class CloudSQLInstanceMap extends Map<string, CacheEntry> {
139141
domainName: opts.domainName,
140142
authType: opts.authType || AuthTypes.PASSWORD,
141143
ipType: opts.ipType || IpAddressTypes.PUBLIC,
142-
limitRateInterval: 30 * 1000, // 30 sec
144+
limitRateInterval: opts.limitRateInterval || 30 * 1000, // 30 sec
143145
sqlAdminFetcher: this.sqlAdminFetcher,
146+
checkDomainInterval: opts.checkDomainInterval
144147
});
145148
this.set(this.cacheKey(opts), new CacheEntry(promise));
146149

@@ -203,28 +206,13 @@ export class Connector {
203206
// });
204207
// const pool = new Pool(opts)
205208
// const res = await pool.query('SELECT * FROM pg_catalog.pg_tables;')
206-
async getOptions({
207-
authType = AuthTypes.PASSWORD,
208-
ipType = IpAddressTypes.PUBLIC,
209-
instanceConnectionName,
210-
domainName,
211-
}: ConnectionOptions): Promise<DriverOptions> {
209+
async getOptions(opts: ConnectionOptions): Promise<DriverOptions> {
212210
const {instances} = this;
213-
await instances.loadInstance({
214-
ipType,
215-
authType,
216-
instanceConnectionName,
217-
domainName,
218-
});
211+
await instances.loadInstance(opts);
219212

220213
return {
221214
stream() {
222-
const cloudSqlInstance = instances.getInstance({
223-
ipType,
224-
instanceConnectionName,
225-
domainName,
226-
authType,
227-
});
215+
const cloudSqlInstance = instances.getInstance(opts);
228216
const {
229217
instanceInfo,
230218
ephemeralCert,

test/cloud-sql-instance.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ t.test('CloudSQLInstance', async t => {
6767
instanceConnectionName: 'my-project:us-east1:my-instance',
6868
sqlAdminFetcher: fetcher,
6969
});
70+
t.after(() =>instance.close())
7071

7172
t.same(
7273
instance.ephemeralCert.cert,
@@ -115,6 +116,7 @@ t.test('CloudSQLInstance', async t => {
115116
limitRateInterval: 50,
116117
},
117118
});
119+
t.after(() =>instance.close())
118120

119121
await t.rejects(
120122
instance.refresh(),
@@ -135,6 +137,7 @@ t.test('CloudSQLInstance', async t => {
135137
limitRateInterval: 50,
136138
},
137139
});
140+
t.after(() =>instance.close())
138141
instance.refresh = () => {
139142
if (refreshCount === 2) {
140143
const end = Date.now();
@@ -177,6 +180,7 @@ t.test('CloudSQLInstance', async t => {
177180
limitRateInterval: 50,
178181
},
179182
});
183+
t.after(() =>instance.close())
180184
await (() =>
181185
new Promise((res): void => {
182186
let refreshCount = 0;
@@ -233,6 +237,7 @@ t.test('CloudSQLInstance', async t => {
233237
limitRateInterval: 50,
234238
},
235239
});
240+
t.after(() =>instance.close())
236241
await (() =>
237242
new Promise((res): void => {
238243
let refreshCount = 0;
@@ -263,6 +268,7 @@ t.test('CloudSQLInstance', async t => {
263268
limitRateInterval: 50,
264269
},
265270
});
271+
t.after(() =>instance.close())
266272

267273
await instance.refresh();
268274

@@ -302,6 +308,7 @@ t.test('CloudSQLInstance', async t => {
302308
limitRateInterval: 50,
303309
},
304310
});
311+
t.after(() =>instance.close())
305312

306313
let cancelRefreshCalled = false;
307314
let refreshCalled = false;
@@ -340,6 +347,7 @@ t.test('CloudSQLInstance', async t => {
340347
sqlAdminFetcher: fetcher,
341348
},
342349
});
350+
t.after(() =>instance.close())
343351

344352
const start = Date.now();
345353
// starts regular refresh cycle
@@ -379,6 +387,7 @@ t.test('CloudSQLInstance', async t => {
379387
sqlAdminFetcher: fetcher,
380388
},
381389
});
390+
t.after(()=> instance.close())
382391
const start = Date.now();
383392
// starts out refresh logic
384393
let refreshCount = 1;
@@ -426,6 +435,7 @@ t.test('CloudSQLInstance', async t => {
426435
limitRateInterval: 50,
427436
},
428437
});
438+
t.after(() =>instance.close())
429439

430440
// starts a new refresh cycle but do not await on it
431441
instance.refresh();
@@ -453,6 +463,7 @@ t.test('CloudSQLInstance', async t => {
453463
limitRateInterval: 50,
454464
},
455465
});
466+
t.after(() =>instance.close())
456467

457468
// simulates an ongoing instance, already has data
458469
await instance.refresh();
@@ -489,6 +500,7 @@ t.test('CloudSQLInstance', async t => {
489500
limitRateInterval: 50,
490501
},
491502
});
503+
t.after(() =>instance.close())
492504

493505
await instance.refresh();
494506
instance.setEstablishedConnection();
@@ -524,6 +536,7 @@ t.test('CloudSQLInstance', async t => {
524536
limitRateInterval: 50,
525537
},
526538
});
539+
t.after(() =>instance.close())
527540

528541
await instance.refresh();
529542
instance.setEstablishedConnection();
@@ -592,6 +605,7 @@ t.test('CloudSQLInstance', async t => {
592605
limitRateInterval: 0,
593606
},
594607
});
608+
t.after(() =>instance.close())
595609
await (() =>
596610
new Promise((res): void => {
597611
let refreshCount = 0;
@@ -615,6 +629,5 @@ t.test('CloudSQLInstance', async t => {
615629
instance.refresh();
616630
instance.setEstablishedConnection();
617631
}))();
618-
}
619-
);
632+
});
620633
});

test/connector.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,3 +638,81 @@ t.test('Connector by domain resolves and creates instance', async t => {
638638

639639
connector.close();
640640
});
641+
642+
643+
t.test('Connector by domain resolves and creates instance', async t => {
644+
const th = setupConnectorModule(t);
645+
const connector = new th.Connector();
646+
t.after(() => {
647+
connector.close();
648+
});
649+
650+
// Get options loads the instance
651+
await connector.getOptions({
652+
ipType: 'PUBLIC',
653+
authType: 'PASSWORD',
654+
domainName: 'db.example.com',
655+
});
656+
657+
// Ensure there is only one entry.
658+
t.same(connector.instances.size, 1);
659+
const oldInstance = connector.instances.get(
660+
'db.example.com-PASSWORD-PUBLIC'
661+
).instance;
662+
t.same(oldInstance.instanceInfo.domainName, 'db.example.com');
663+
t.same(oldInstance.instanceInfo.instanceId, 'instance');
664+
665+
// getOptions after DNS response changes closes the old instance
666+
// and loads a new one.
667+
th.resolveTxtResponse = 'project:region2:instance2';
668+
await connector.getOptions({
669+
ipType: 'PUBLIC',
670+
authType: 'PASSWORD',
671+
domainName: 'db.example.com',
672+
});
673+
t.same(connector.instances.size, 1);
674+
const newInstance = connector.instances.get(
675+
'db.example.com-PASSWORD-PUBLIC'
676+
).instance;
677+
t.same(newInstance.instanceInfo.domainName, 'db.example.com');
678+
t.same(newInstance.instanceInfo.instanceId, 'instance2');
679+
t.same(oldInstance.isClosed(), true, 'old instance is closed');
680+
681+
connector.close();
682+
});
683+
684+
685+
t.test('Connector checks if name changes in background and closes connector', async t => {
686+
const th = setupConnectorModule(t);
687+
const connector = new th.Connector();
688+
t.after(() => {
689+
connector.close();
690+
});
691+
692+
// Get options loads the instance
693+
await connector.getOptions({
694+
ipType: 'PUBLIC',
695+
authType: 'PASSWORD',
696+
domainName: 'db.example.com',
697+
checkDomainInterval: 10, // 10ms for testing
698+
});
699+
700+
// Ensure there is only one entry.
701+
t.same(connector.instances.size, 1);
702+
const oldInstance = connector.instances.get(
703+
'db.example.com-PASSWORD-PUBLIC'
704+
).instance;
705+
t.same(oldInstance.instanceInfo.domainName, 'db.example.com');
706+
t.same(oldInstance.instanceInfo.instanceId, 'instance');
707+
708+
// getOptions after DNS response changes closes the old instance
709+
// and loads a new one.
710+
th.resolveTxtResponse = 'project:region2:instance2';
711+
await new Promise((res) =>{
712+
setTimeout(res, 50);
713+
})
714+
715+
t.same(oldInstance.isClosed(), true, 'old instance is closed');
716+
717+
connector.close();
718+
});

0 commit comments

Comments
 (0)