Skip to content

Commit b5df71e

Browse files
HCK-8816: queries optimization (#120)
* chore: refactored `connectionState` into a `ClientManager` class; resolved some sonarlint remarks * chore: refactored the main `reverseCollectionsToJSON` function in RE, reducing the in-place complexity, splitting it to smaller pieces * feat: restricted some queries to a predefined set of schemas and tables * fix: concatenated the query properly * fix: proper formatting, returned to tagged template literals for simple queries * feat: optimized queries for `getSpatialIndexes`, `getFullTextIndexes`, `getViewsIndexes` * fix: removed inapplicable WHERE clause for `getViewsIndexes` * feat: optimized queries for `getDatabaseMemoryOptimizedTables`, `getDatabaseCheckConstraints`, `getTableKeyConstraints`, `getDatabaseXmlSchemaCollection` * chore: improved ClientManager API, eliminated redundant `sshService` manipulations * fix: key constraints * fix: moved WHERE clause parts to LEFT JOIN * chore: reduced implicit complexity --------- Co-authored-by: Thomas Jakemeyn <thomas.jakemeyn@gmail.com>
1 parent 1468c91 commit b5df71e

14 files changed

+866
-641
lines changed

forward_engineering/api.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ module.exports = {
107107
await getExternalBrowserUrl(connectionInfo, logger, callback, app);
108108
} else {
109109
const client = await connect(connectionInfo, logger, () => {}, app);
110-
await logDatabaseVersion(client, logger);
110+
await logDatabaseVersion({ client, logger });
111111
}
112112
callback(null);
113113
} catch (error) {

reverse_engineering/api.js

+20-17
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
const crypto = require('crypto');
44
const randomstring = require('randomstring');
55
const base64url = require('base64url');
6-
const { getClient, setClient, clearClient } = require('./connectionState');
6+
const { clientManager } = require('./clientManager');
77
const { getObjectsFromDatabase, getDatabaseCollationOption } = require('./databaseService/databaseService');
88
const {
99
reverseCollectionsToJSON,
@@ -21,19 +21,21 @@ const { prepareError } = require('./databaseService/helpers/errorService');
2121

2222
module.exports = {
2323
async connect(connectionInfo, logger, callback, app) {
24-
const client = getClient();
25-
const sshService = app.require('@hackolade/ssh-service');
24+
const client = clientManager.getClient();
25+
2626
if (!client) {
27-
await setClient(connectionInfo, sshService, 0, logger);
28-
return getClient();
27+
return await clientManager.initClient({
28+
connectionInfo,
29+
logger,
30+
sshService: app.require('@hackolade/ssh-service'),
31+
});
2932
}
3033

3134
return client;
3235
},
3336

34-
disconnect(connectionInfo, logger, callback, app) {
35-
const sshService = app.require('@hackolade/ssh-service');
36-
clearClient(sshService);
37+
disconnect(connectionInfo, logger, callback) {
38+
clientManager.clearClient();
3739
callback();
3840
},
3941

@@ -44,9 +46,9 @@ module.exports = {
4446
await this.getExternalBrowserUrl(connectionInfo, logger, callback, app);
4547
} else {
4648
const client = await this.connect(connectionInfo, logger, () => {}, app);
47-
await logDatabaseVersion(client, logger);
49+
await logDatabaseVersion({ client, logger });
4850
}
49-
callback(null);
51+
callback();
5052
} catch (error) {
5153
const errorWithUpdatedInfo = prepareError({ error });
5254
logger.log(
@@ -88,16 +90,17 @@ module.exports = {
8890
async getDbCollectionsNames(connectionInfo, logger, callback, app) {
8991
try {
9092
logInfo('Retrieving databases and tables information', connectionInfo, logger);
93+
9194
const client = await this.connect(connectionInfo, logger, () => {}, app);
9295
if (!client.config.database) {
9396
throw new Error('No database specified');
9497
}
9598

96-
await logDatabaseVersion(client, logger);
99+
await logDatabaseVersion({ client, logger });
97100

98101
const objects = await getObjectsFromDatabase(client);
99102
const dbName = client.config.database;
100-
const collationData = (await getDatabaseCollationOption(client, dbName, logger)) || [];
103+
const collationData = (await getDatabaseCollationOption({ client, dbName, logger })) || [];
101104
logger.log('info', { collation: collationData[0] }, 'Database collation');
102105
callback(null, objects);
103106
} catch (error) {
@@ -120,16 +123,16 @@ module.exports = {
120123
logger.log('info', collectionsInfo, 'Retrieving schema', collectionsInfo.hiddenKeys);
121124
logger.progress({ message: 'Start reverse-engineering process', containerName: '', entityName: '' });
122125
const { collections } = collectionsInfo.collectionData;
123-
const client = getClient();
124-
const dbName = client.config.database;
125-
if (!dbName) {
126+
const client = clientManager.getClient();
127+
const dbName = client?.config.database;
128+
if (!client || !dbName) {
126129
throw new Error('No database specified');
127130
}
128131

129132
const reverseEngineeringOptions = getOptionsFromConnectionInfo(collectionsInfo);
130133
const [jsonSchemas, relationships] = await Promise.all([
131-
await reverseCollectionsToJSON(logger)(client, collections, reverseEngineeringOptions),
132-
await getCollectionsRelationships(logger)(client, collections),
134+
await reverseCollectionsToJSON({ client, tablesInfo: collections, reverseEngineeringOptions, logger }),
135+
await getCollectionsRelationships({ client, tablesInfo: collections, logger }),
133136
]);
134137

135138
const jsonSchemasWithDescriptionComments = await getJsonSchemasWithInjectedDescriptionComments({
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
11
const { getConnectionClient } = require('./databaseService/databaseService');
22

3-
const stateInstance = {
4-
_client: null,
5-
_isSshTunnel: false,
6-
getClient: () => this._client,
7-
setClient: async (connectionInfo, sshService, attempts = 0, logger) => {
8-
if (connectionInfo.ssh && !this._isSshTunnel) {
3+
class ClientManager {
4+
#client = null;
5+
#sshService = null;
6+
#isSshTunnel = false;
7+
8+
getClient() {
9+
return this.#client;
10+
}
11+
12+
async initClient({ connectionInfo, sshService, attempts = 0, logger }) {
13+
if (!this.#sshService) {
14+
this.#sshService = sshService;
15+
}
16+
17+
let connectionParams = { ...connectionInfo };
18+
19+
if (connectionInfo.ssh && !this.#isSshTunnel) {
920
const { options } = await sshService.openTunnel({
1021
sshAuthMethod: connectionInfo.ssh_method === 'privateKey' ? 'IDENTITY_FILE' : 'USER_PASSWORD',
1122
sshTunnelHostname: connectionInfo.ssh_host,
@@ -18,44 +29,51 @@ const stateInstance = {
1829
port: connectionInfo.port,
1930
});
2031

21-
this._isSshTunnel = true;
22-
connectionInfo = {
32+
this.#isSshTunnel = true;
33+
34+
connectionParams = {
2335
...connectionInfo,
2436
...options,
2537
};
2638
}
2739

2840
try {
29-
this._client = await getConnectionClient(connectionInfo, logger);
41+
this.#client = await getConnectionClient({ connectionInfo: connectionParams, logger });
42+
43+
return this.#client;
3044
} catch (error) {
3145
const encryptConnection =
32-
connectionInfo.encryptConnection === undefined || Boolean(connectionInfo.encryptConnection);
46+
connectionParams.encryptConnection === undefined || Boolean(connectionParams.encryptConnection);
47+
3348
const isEncryptedConnectionToLocalInstance =
3449
error.message.includes('self signed certificate') && encryptConnection;
3550

3651
if (isEncryptedConnectionToLocalInstance && attempts <= 0) {
37-
return stateInstance.setClient(
38-
{
39-
...connectionInfo,
52+
return this.initClient({
53+
connectionInfo: {
54+
...connectionParams,
4055
encryptConnection: false,
4156
},
4257
sshService,
43-
attempts + 1,
58+
attempts: attempts + 1,
4459
logger,
45-
);
60+
});
4661
}
4762

4863
throw error;
4964
}
50-
},
51-
clearClient: async sshService => {
52-
this._client = null;
65+
}
66+
67+
clearClient() {
68+
this.#client = null;
5369

54-
if (this._isSshTunnel) {
55-
await sshService.closeConsumer();
56-
this._isSshTunnel = false;
70+
if (this.#isSshTunnel && this.#sshService) {
71+
this.#sshService.closeConsumer();
72+
this.#isSshTunnel = false;
5773
}
58-
},
59-
};
74+
}
75+
}
6076

61-
module.exports = stateInstance;
77+
module.exports = {
78+
clientManager: new ClientManager(),
79+
};

0 commit comments

Comments
 (0)