Skip to content

Commit 31ef100

Browse files
committed
wip: periodic checks
1 parent 45bb3c7 commit 31ef100

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
@@ -155,6 +160,11 @@ export class CloudSQLInstance {
155160
this.next = undefined;
156161
return Promise.reject('closed');
157162
}
163+
if(this?.instanceInfo?.domainName && ! this.checkDomainID){
164+
this.checkDomainID = setInterval(()=>{
165+
this.checkDomainChanged()
166+
}, this.checkDomainInterval || 30*1000)
167+
}
158168

159169
const currentRefreshId = this.scheduledRefreshID;
160170

@@ -299,8 +309,8 @@ export class CloudSQLInstance {
299309
// If refresh has not yet started, then cancel the setTimeout
300310
if (this.scheduledRefreshID) {
301311
clearTimeout(this.scheduledRefreshID);
312+
this.scheduledRefreshID = null
302313
}
303-
this.scheduledRefreshID = null;
304314
}
305315

306316
// Mark this instance as having an active connection. This is important to
@@ -315,6 +325,10 @@ export class CloudSQLInstance {
315325
close(): void {
316326
this.closed = true;
317327
this.cancelRefresh();
328+
if(this.checkDomainID){
329+
clearInterval(this.checkDomainID);
330+
this.checkDomainID = null
331+
}
318332
}
319333

320334
isClosed(): boolean {

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

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

219212
return {
220213
stream() {
221-
const cloudSqlInstance = instances.getInstance({
222-
ipType,
223-
instanceConnectionName,
224-
domainName,
225-
authType,
226-
});
214+
const cloudSqlInstance = instances.getInstance(opts);
227215
const {
228216
instanceInfo,
229217
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

@@ -301,6 +307,7 @@ t.test('CloudSQLInstance', async t => {
301307
limitRateInterval: 50,
302308
},
303309
});
310+
t.after(() =>instance.close())
304311

305312
let cancelRefreshCalled = false;
306313
let refreshCalled = false;
@@ -338,6 +345,7 @@ t.test('CloudSQLInstance', async t => {
338345
sqlAdminFetcher: fetcher,
339346
},
340347
});
348+
t.after(() =>instance.close())
341349

342350
const start = Date.now();
343351
// starts regular refresh cycle
@@ -377,6 +385,7 @@ t.test('CloudSQLInstance', async t => {
377385
sqlAdminFetcher: fetcher,
378386
},
379387
});
388+
t.after(()=> instance.close())
380389
const start = Date.now();
381390
// starts out refresh logic
382391
let refreshCount = 1;
@@ -424,6 +433,7 @@ t.test('CloudSQLInstance', async t => {
424433
limitRateInterval: 50,
425434
},
426435
});
436+
t.after(() =>instance.close())
427437

428438
// starts a new refresh cycle but do not await on it
429439
instance.refresh();
@@ -451,6 +461,7 @@ t.test('CloudSQLInstance', async t => {
451461
limitRateInterval: 50,
452462
},
453463
});
464+
t.after(() =>instance.close())
454465

455466
// simulates an ongoing instance, already has data
456467
await instance.refresh();
@@ -487,6 +498,7 @@ t.test('CloudSQLInstance', async t => {
487498
limitRateInterval: 50,
488499
},
489500
});
501+
t.after(() =>instance.close())
490502

491503
await instance.refresh();
492504
instance.setEstablishedConnection();
@@ -522,6 +534,7 @@ t.test('CloudSQLInstance', async t => {
522534
limitRateInterval: 50,
523535
},
524536
});
537+
t.after(() =>instance.close())
525538

526539
await instance.refresh();
527540
instance.setEstablishedConnection();
@@ -589,6 +602,7 @@ t.test('CloudSQLInstance', async t => {
589602
limitRateInterval: 0,
590603
},
591604
});
605+
t.after(() =>instance.close())
592606
await (() =>
593607
new Promise((res): void => {
594608
let refreshCount = 0;
@@ -612,6 +626,5 @@ t.test('CloudSQLInstance', async t => {
612626
instance.refresh();
613627
instance.setEstablishedConnection();
614628
}))();
615-
}
616-
);
629+
});
617630
});

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)