Skip to content

Commit 871729d

Browse files
committed
refactor: cluster topology monitor
1 parent 5ebcaf3 commit 871729d

File tree

3 files changed

+111
-110
lines changed

3 files changed

+111
-110
lines changed

common/lib/host_list_provider/monitoring/cluster_topology_monitor.ts

+86-96
Original file line numberDiff line numberDiff line change
@@ -17,50 +17,45 @@
1717
import { HostInfo } from "../../host_info";
1818
import { CacheMap } from "../../utils/cache_map";
1919
import { PluginService } from "../../plugin_service";
20-
import { HostListProviderService } from "../../host_list_provider_service";
2120
import { HostAvailability } from "../../host_availability/host_availability";
2221
import { logTopology, sleep } from "../../utils/utils";
2322
import { logger } from "../../../logutils";
2423
import { HostRole } from "../../host_role";
2524
import { ClientWrapper } from "../../client_wrapper";
26-
import { Messages } from "../../utils/messages";
27-
import { TopologyAwareDatabaseDialect } from "../../topology_aware_database_dialect";
28-
import { HostListProvider } from "../host_list_provider";
2925
import { AwsWrapperError } from "../../utils/errors";
26+
import { MonitoringRdsHostListProvider } from "./monitoring_host_list_provider";
3027

3128
export interface ClusterToplogyMonitor {
3229
forceRefresh(client: any, timeoutMs: number): Promise<HostInfo[]>;
3330

34-
setClusterId(clusterId: string): void;
35-
3631
close(): void;
3732

3833
forceMonitoringRefresh(shouldVerifyWriter: boolean, timeoutMs: number): Promise<HostInfo[]>;
3934
}
4035

4136
export class ClusterToplogyMonitorImpl implements ClusterToplogyMonitor {
42-
public clusterId: string;
43-
public topologyMap: CacheMap<string, HostInfo[]>;
44-
private topologyCacheExpirationNanos: number = 10000; // TODO: investigate values and set in constructor.
45-
protected initialHostInfo: HostInfo;
46-
public properties: Map<string, any>;
47-
public pluginService: PluginService;
48-
protected hostListProviderService: HostListProviderService;
49-
private hostListProvider: HostListProvider;
50-
protected refreshRate: number = 300; // TODO: investigate issues with setting lower values.
51-
private highRefreshRate: number = 300;
37+
static readonly TOPOLOGY_CACHE_EXPIRATION_MS: number = 300000;
38+
39+
private readonly clusterId: string;
40+
private readonly initialHostInfo: HostInfo;
41+
private readonly _properties: Map<string, any>;
42+
private readonly _pluginService: PluginService;
43+
private readonly _hostListProvider: MonitoringRdsHostListProvider;
44+
private readonly refreshRateMs: number;
45+
private readonly highRefreshRateMs: number;
5246

47+
private topologyMap: CacheMap<string, HostInfo[]>;
5348
private writerHostInfo: HostInfo = null;
5449
private isVerifiedWriterConnection: boolean = false;
5550
private monitoringClient: ClientWrapper = null;
56-
private highRefreshRateEndTime: any = 0;
57-
private highRefreshPeriodAfterPanic: number = 30000; // 30 seconds.
58-
private ignoreTopologyRequest: number = 1000; // 10 seconds.
51+
private highRefreshRateEndTime: number = 0;
52+
private highRefreshPeriodAfterPanicMs: number = 30000; // 30 seconds.
5953
private ignoreNewTopologyRequestsEndTime: number = 0;
54+
private ignoreTopologyRequestMs: number = 10000; // 10 seconds.
6055

6156
// Controls for stopping the ClusterTopologyMonitor run.
6257
private stopMonitoring: boolean = false;
63-
private runPromise: Promise<void>;
58+
private readonly runPromise: Promise<void>;
6459

6560
// Tracking of the host monitors.
6661
private hostMonitors: Map<string, HostMonitor> = new Map();
@@ -87,20 +82,29 @@ export class ClusterToplogyMonitorImpl implements ClusterToplogyMonitor {
8782
initialHostSpec: HostInfo,
8883
props,
8984
pluginService: PluginService,
90-
hostListProviderService: HostListProviderService,
91-
hostListProvider: HostListProvider,
92-
refreshRateNano
85+
hostListProvider: MonitoringRdsHostListProvider,
86+
refreshRateMs: number,
87+
highRefreshRateMs: number
9388
) {
9489
this.clusterId = clusterId;
9590
this.topologyMap = topologyMap;
9691
this.initialHostInfo = initialHostSpec;
97-
this.pluginService = pluginService;
98-
this.hostListProviderService = hostListProviderService;
99-
this.hostListProvider = hostListProvider;
100-
this.properties = props;
101-
//this.refreshRateNano = refreshRateNano; // TODO: coordinate timeouts for bigint or number.
102-
const runMonitor = this.run();
103-
this.runPromise = runMonitor;
92+
this._pluginService = pluginService;
93+
this._hostListProvider = hostListProvider;
94+
this._properties = props;
95+
this.refreshRateMs = refreshRateMs;
96+
this.highRefreshRateMs = highRefreshRateMs;
97+
this.runPromise = this.run();
98+
}
99+
100+
get hostListProvider(): MonitoringRdsHostListProvider {
101+
return this._hostListProvider;
102+
}
103+
get pluginService(): PluginService {
104+
return this._pluginService;
105+
}
106+
get properties(): Map<string, any> {
107+
return this._properties;
104108
}
105109

106110
async close(): Promise<void> {
@@ -111,10 +115,6 @@ export class ClusterToplogyMonitorImpl implements ClusterToplogyMonitor {
111115
this.hostMonitors.clear();
112116
}
113117

114-
setClusterId(clusterId: string): void {
115-
this.clusterId = clusterId;
116-
}
117-
118118
async forceMonitoringRefresh(shouldVerifyWriter: boolean, timeoutMs: number): Promise<HostInfo[]> {
119119
const currentHosts = this.topologyMap.get(this.clusterId);
120120
if (currentHosts) {
@@ -127,7 +127,7 @@ export class ClusterToplogyMonitorImpl implements ClusterToplogyMonitor {
127127
await this.closeConnection(client);
128128
}
129129

130-
return this.waitTillTopologyGetsUpdated(timeoutMs);
130+
return await this.waitTillTopologyGetsUpdated(timeoutMs);
131131
}
132132

133133
async forceRefresh(client: any, timeoutMs: number): Promise<HostInfo[]> {
@@ -180,7 +180,7 @@ export class ClusterToplogyMonitorImpl implements ClusterToplogyMonitor {
180180
}
181181

182182
try {
183-
const hosts: HostInfo[] = await this.queryForTopology(client);
183+
const hosts: HostInfo[] = await this._hostListProvider.sqlQueryForTopology(client);
184184
if (hosts) {
185185
this.updateTopologyCache(hosts);
186186
}
@@ -191,58 +191,46 @@ export class ClusterToplogyMonitorImpl implements ClusterToplogyMonitor {
191191
return null;
192192
}
193193

194-
private openAnyClientAndUpdateTopology() {
195-
// TODO: implement method.
196-
return [];
197-
}
198-
199-
async queryForTopology(targetClient: ClientWrapper): Promise<HostInfo[]> {
200-
const dialect = this.hostListProviderService.getDialect();
201-
if (!this.isTopologyAwareDatabaseDialect(dialect)) {
202-
throw new TypeError(Messages.get("RdsHostListProvider.incorrectDialect"));
203-
}
204-
return await dialect.queryForTopology(targetClient, this.hostListProvider).then((res: any) => this.processQueryResults(res));
205-
}
206-
207-
protected isTopologyAwareDatabaseDialect(arg: any): arg is TopologyAwareDatabaseDialect {
208-
return arg;
209-
}
210-
211-
private async processQueryResults(result: HostInfo[]): Promise<HostInfo[]> {
212-
const hostMap: Map<string, HostInfo> = new Map<string, HostInfo>();
213-
214-
let hosts: HostInfo[] = [];
215-
const writers: HostInfo[] = [];
216-
result.forEach((host) => {
217-
hostMap.set(host.host, host);
218-
});
194+
private async openAnyClientAndUpdateTopology() {
195+
if (!this.monitoringClient) {
196+
let client;
197+
try {
198+
client = await this._pluginService.forceConnect(this.initialHostInfo, this._properties);
199+
if (!this.monitoringClient) {
200+
this.monitoringClient = client;
201+
client = await this._pluginService.forceConnect(this.initialHostInfo, this._properties);
202+
}
203+
} catch {
204+
logger.debug(`Could not connect to: ${this.initialHostInfo.host}`);
205+
return null;
206+
}
219207

220-
hostMap.forEach((host) => {
221-
if (host.role !== HostRole.WRITER) {
222-
hosts.push(host);
208+
if (client && !this.monitoringClient) {
209+
this.monitoringClient = client;
210+
logger.debug(`Opened monitoring connection to: ${this.initialHostInfo.host}`);
211+
if (this.getWriterHostId(this.monitoringClient) !== null) {
212+
this.isVerifiedWriterConnection = true;
213+
this.writerHostInfo = this.initialHostInfo;
214+
}
223215
} else {
224-
writers.push(host);
216+
// Monitoring connection already set by another task, close the new connection.
217+
this.untrackedPromises.push(this.closeConnection(client));
225218
}
226-
});
219+
}
227220

228-
const writerCount: number = writers.length;
229-
if (writerCount === 0) {
230-
hosts = [];
231-
} else if (writerCount === 1) {
232-
hosts.push(writers[0]);
233-
} else {
234-
const sortedWriters: HostInfo[] = writers.sort((a, b) => {
235-
return b.lastUpdateTime - a.lastUpdateTime;
236-
});
221+
const hosts: HostInfo[] = await this.fetchTopologyAndUpdateCache(this.monitoringClient);
222+
if (!hosts) {
223+
const clientToClose = this.monitoringClient;
224+
this.monitoringClient = null;
225+
this.isVerifiedWriterConnection = false;
237226

238-
hosts.push(sortedWriters[0]);
227+
this.untrackedPromises.push(this.closeConnection(clientToClose));
239228
}
240-
241229
return hosts;
242230
}
243231

244232
updateTopologyCache(hosts: HostInfo[]) {
245-
this.topologyMap.put(this.clusterId, hosts, this.topologyCacheExpirationNanos);
233+
this.topologyMap.put(this.clusterId, hosts, ClusterToplogyMonitorImpl.TOPOLOGY_CACHE_EXPIRATION_MS);
246234
this.releaseTopologyUpdate();
247235
this.topologyUpdated = new Promise<void>((done) => {
248236
this.releaseTopologyUpdate = () => {
@@ -252,14 +240,14 @@ export class ClusterToplogyMonitorImpl implements ClusterToplogyMonitor {
252240
}
253241

254242
getWriterHostId(client: ClientWrapper) {
255-
return client.hostInfo.role === HostRole.WRITER ? client.id : null;
243+
return client && client.hostInfo.role === HostRole.WRITER ? client.id : null;
256244
}
257245

258-
async closeConnection(client: any) {
246+
async closeConnection(client: ClientWrapper) {
259247
if (!client) {
260248
return;
261249
}
262-
await this.pluginService.abortTargetClient(client);
250+
await this._pluginService.abortTargetClient(client);
263251
}
264252

265253
private isInPanicMode() {
@@ -284,7 +272,7 @@ export class ClusterToplogyMonitorImpl implements ClusterToplogyMonitor {
284272
// Use any client to gather topology information.
285273
let hosts: HostInfo[] = this.topologyMap.get(this.clusterId);
286274
if (!hosts) {
287-
hosts = this.openAnyClientAndUpdateTopology();
275+
hosts = await this.openAnyClientAndUpdateTopology();
288276
}
289277

290278
// Set up host monitors.
@@ -307,15 +295,13 @@ export class ClusterToplogyMonitorImpl implements ClusterToplogyMonitor {
307295
// Writer detected, update monitoringClient.
308296
const client = this.monitoringClient;
309297
this.monitoringClient = writerClient;
310-
await this.closeConnection(client);
298+
this.untrackedPromises.push(this.closeConnection(client));
311299
this.isVerifiedWriterConnection = true;
312-
this.highRefreshRateEndTime = Date.now() + this.highRefreshPeriodAfterPanic;
313-
this.ignoreNewTopologyRequestsEndTime = Date.now() - this.ignoreTopologyRequest;
300+
this.highRefreshRateEndTime = Date.now() + this.highRefreshPeriodAfterPanicMs;
301+
this.ignoreNewTopologyRequestsEndTime = Date.now() + this.ignoreTopologyRequestMs;
314302

315303
// Stop monitoring of each host, writer detected.
316304
this.hostMonitorsStop = true;
317-
await Promise.all(this.untrackedPromises);
318-
this.untrackedPromises = [];
319305
this.hostMonitors.clear();
320306
continue;
321307
} else {
@@ -340,25 +326,23 @@ export class ClusterToplogyMonitorImpl implements ClusterToplogyMonitor {
340326
if (this.hostMonitors.size !== 0) {
341327
// Stop host monitors.
342328
this.hostMonitorsStop = true;
343-
await Promise.all(this.untrackedPromises);
344-
this.untrackedPromises = [];
345329
this.hostMonitors.clear();
346330
}
347-
const hosts = this.fetchTopologyAndUpdateCache(this.monitoringClient);
331+
const hosts = await this.fetchTopologyAndUpdateCache(this.monitoringClient);
348332
if (!hosts) {
349333
// Unable to gather topology, switch to panic mode.
350334
const client = this.monitoringClient;
351335
this.monitoringClient = null;
352336
this.isVerifiedWriterConnection = false;
353-
await this.closeConnection(client);
337+
this.untrackedPromises.push(this.closeConnection(client));
354338
continue;
355339
}
356340
if (this.highRefreshRateEndTime > 0 && Date.now() > this.highRefreshRateEndTime) {
357341
this.highRefreshRateEndTime = 0;
358342
}
359343
if (this.highRefreshRateEndTime == 0) {
360344
// Log topology when not in high refresh rate.
361-
logger.debug(logTopology(this.topologyMap.get(this.clusterId), ""));
345+
this.logTopology("");
362346
}
363347
// Set an easily interruptible delay between topology refreshes.
364348
await this.delay(false);
@@ -379,12 +363,19 @@ export class ClusterToplogyMonitorImpl implements ClusterToplogyMonitor {
379363
}
380364

381365
private async delay(useHighRefreshRate: boolean) {
382-
const endTime = Date.now() + (useHighRefreshRate ? this.highRefreshRate : this.refreshRate);
366+
const endTime = Date.now() + (useHighRefreshRate ? this.highRefreshRateMs : this.refreshRateMs);
383367
while (Date.now() < endTime && !this.requestToUpdateTopology) {
384368
await sleep(50);
385369
}
386370
this.requestToUpdateTopology = false;
387371
}
372+
373+
logTopology(msgPrefix: string) {
374+
const hosts: HostInfo[] = this.topologyMap.get(this.clusterId);
375+
if (hosts && hosts.length !== 0) {
376+
logger.debug(logTopology(hosts, msgPrefix));
377+
}
378+
}
388379
}
389380

390381
export class HostMonitor {
@@ -433,9 +424,8 @@ export class HostMonitor {
433424
this.monitor.hostMonitorsStop = true;
434425

435426
await this.monitor.fetchTopologyAndUpdateCache(client);
436-
logger.debug(logTopology(this.monitor.topologyMap.get(this.monitor.clusterId), ""));
427+
this.monitor.logTopology("");
437428
}
438-
439429
client = null;
440430
return;
441431
} else if (!client) {
@@ -467,7 +457,7 @@ export class HostMonitor {
467457

468458
let hosts: HostInfo[];
469459
try {
470-
hosts = await this.monitor.queryForTopology(client);
460+
hosts = await this.monitor.hostListProvider.sqlQueryForTopology(client);
471461
} catch (error) {
472462
return;
473463
}

0 commit comments

Comments
 (0)