Skip to content

Commit e01bdc4

Browse files
HCK-13386: abort controller (#61)
* feat: added dedicated abort controller for all spawned processes * chore: improved check on static values * feat: made the Host field required * fix: reverted previous simple implementation * chore: remarks
1 parent 132cc75 commit e01bdc4

2 files changed

Lines changed: 76 additions & 15 deletions

File tree

reverse_engineering/connection_settings_modal/connectionSettingsModalConfig.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
"inputType": "text",
1616
"inputPlaceholder": "host",
1717
"inputTooltip": "Specify host IP address or DNS name of Teradata server",
18-
"regex": "([^\\s])"
18+
"validation": {
19+
"regex": "([^\\s])"
20+
}
1921
},
2022
{
2123
"inputLabel": "Port",

reverse_engineering/helpers/connectionHelper.js

Lines changed: 73 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ const SYSTEM_DATABASES = [
3939
'TD_SYSXML',
4040
'TDPUSER',
4141
];
42-
const SYSTEM_UDT = [
42+
43+
const SYSTEM_UDT = new Set([
4344
'ArrayVec',
4445
'InternalPeriodDateType',
4546
'InternalPeriodTimeStampType',
@@ -57,12 +58,15 @@ const SYSTEM_UDT = [
5758
'TD_JSON_BSON',
5859
'TD_JSON_UBJSON',
5960
'XML',
60-
];
61+
]);
62+
6163
const MISSING_JAVA_PATH_MESSAGE =
6264
'Path to JAVA binary file is incorrect. Please specify JAVA_HOME variable in your system or put specific path to JAVA binary file in connection settings.';
6365

6466
let connection;
6567
let useSshTunnel;
68+
let abortController;
69+
let activeQueries = new Set();
6670

6771
const isWindows = () => os.platform() === 'win32';
6872

@@ -172,16 +176,42 @@ const createConnection = async (connectionInfo, sshService, logger) => {
172176
const teradataClientCommandArguments = buildCommand(teradataClientPath, connectionSettings);
173177

174178
return {
175-
execute: query => {
179+
execute: (query, signal) => {
176180
return new Promise((resolve, reject) => {
181+
if (signal?.aborted) {
182+
return reject(new Error('Query execution was aborted'));
183+
}
184+
177185
const queryArgument = createArgument('query', query);
178186
const javaArgs = [...teradataClientCommandArguments, queryArgument];
179187

180188
const queryResult = spawn(`"${javaPath}"`, javaArgs, {
181189
shell: true,
182190
});
183191

192+
activeQueries.add(queryResult);
193+
194+
const abortHandler = () => {
195+
if (!queryResult?.killed) {
196+
queryResult.kill('SIGTERM');
197+
activeQueries.delete(queryResult);
198+
}
199+
reject(new Error('Query execution was aborted'));
200+
};
201+
202+
if (signal) {
203+
signal.addEventListener('abort', abortHandler);
204+
}
205+
206+
const cleanup = () => {
207+
activeQueries.delete(queryResult);
208+
if (signal) {
209+
signal.removeEventListener('abort', abortHandler);
210+
}
211+
};
212+
184213
queryResult.on('error', error => {
214+
cleanup();
185215
reject(new Error(error));
186216
});
187217

@@ -196,6 +226,12 @@ const createConnection = async (connectionInfo, sshService, logger) => {
196226
});
197227

198228
queryResult.on('close', code => {
229+
cleanup();
230+
231+
if (signal?.aborted) {
232+
return;
233+
}
234+
199235
if (code !== 0) {
200236
reject(new Error(Buffer.concat(errorData).toString()));
201237
return;
@@ -225,6 +261,7 @@ const createConnection = async (connectionInfo, sshService, logger) => {
225261
});
226262
});
227263
},
264+
getAbortController: () => abortController,
228265
};
229266
};
230267

@@ -233,6 +270,9 @@ const connect = async (connectionInfo, sshService, logger) => {
233270
return connection;
234271
}
235272

273+
abortController = new AbortController();
274+
activeQueries.clear();
275+
236276
useSshTunnel = connectionInfo.useSshTunnel;
237277
connection = await createConnection(connectionInfo, sshService, logger);
238278

@@ -246,12 +286,14 @@ const getConcatenatedQueryResult = (queryResult = []) => {
246286
};
247287

248288
const createInstance = (connection, _) => {
289+
const signal = connection.getAbortController()?.signal;
290+
249291
const getDatabasesWithTableNames = async tableType => {
250292
const query = buildQuery(queryType.GET_DATABASE_AND_TABLE_NAMES, {
251293
tableType,
252294
systemDatabases: SYSTEM_DATABASES,
253295
});
254-
const queryResult = await connection.execute(query);
296+
const queryResult = await connection.execute(query, signal);
255297

256298
return groupBy({
257299
items: queryResult,
@@ -261,24 +303,27 @@ const createInstance = (connection, _) => {
261303
};
262304

263305
const getCount = async (dbName, tableName) => {
264-
const count = await connection.execute(buildQuery(queryType.COUNT_COLUMNS, { dbName, tableName }));
306+
const count = await connection.execute(buildQuery(queryType.COUNT_COLUMNS, { dbName, tableName }), signal);
265307

266308
return Number(count[0]?.Quantity || 0);
267309
};
268310

269311
const getRecords = async (dbName, tableName, limit) => {
270-
return connection.execute(buildQuery(queryType.GET_RECORDS, { dbName, tableName, limit }));
312+
return connection.execute(buildQuery(queryType.GET_RECORDS, { dbName, tableName, limit }), signal);
271313
};
272314

273315
const getVersion = async () => {
274-
const result = await connection.execute('SELECT * FROM dbc.dbcinfo');
316+
const result = await connection.execute('SELECT * FROM dbc.dbcinfo', signal);
275317
const versionInfo = result.find(info => info.InfoKey === 'VERSION');
276318

277319
return versionInfo?.InfoData;
278320
};
279321

280322
const describeDatabase = async dbName => {
281-
const databaseInfoResult = await connection.execute(buildQuery(queryType.DESCRIBE_DATABASE, { dbName }));
323+
const databaseInfoResult = await connection.execute(
324+
buildQuery(queryType.DESCRIBE_DATABASE, { dbName }),
325+
signal,
326+
);
282327

283328
if (!databaseInfoResult.length) {
284329
return {};
@@ -303,14 +348,15 @@ const createInstance = (connection, _) => {
303348
const showCreateEntity = async (dbName, tableName, entityType) => {
304349
const result = await connection.execute(
305350
buildQuery(queryType.SHOW_CREATE_ENTITY_STATEMENT, { dbName, tableName, entityType }),
351+
signal,
306352
);
307353

308354
return getConcatenatedQueryResult(result);
309355
};
310356

311357
const getColumns = async (dbName, tableName) => {
312358
const query = buildQuery(queryType.GET_COLUMNS, { dbName, tableName });
313-
const result = await connection.execute(query);
359+
const result = await connection.execute(query, signal);
314360

315361
return result.map(raw => ({
316362
dbName: raw.DatabaseName,
@@ -322,7 +368,7 @@ const createInstance = (connection, _) => {
322368

323369
const getCreateIndexStatement = async index => {
324370
const query = `SHOW ${index.indexType} INDEX "${index.dbName}"."${index.indxName}";`;
325-
const createStatement = await connection.execute(query);
371+
const createStatement = await connection.execute(query, signal);
326372

327373
return {
328374
...index,
@@ -332,7 +378,7 @@ const createInstance = (connection, _) => {
332378

333379
const getIndexes = async dbName => {
334380
const query = buildQuery(queryType.GET_INDEXES, { dbName });
335-
const queryResult = await connection.execute(query);
381+
const queryResult = await connection.execute(query, signal);
336382

337383
const indexes = _.uniqBy(queryResult, 'IndexName').map(index => ({
338384
dbName: index.DatabaseName,
@@ -353,7 +399,7 @@ const createInstance = (connection, _) => {
353399
const getCreateUdtStatement = async type => {
354400
const name = type['Table/View/Macro Dictionary Name'];
355401
const query = `SHOW TYPE "${name}";`;
356-
const createStatement = await connection.execute(query);
402+
const createStatement = await connection.execute(query, signal);
357403

358404
return {
359405
name,
@@ -363,7 +409,7 @@ const createInstance = (connection, _) => {
363409

364410
const getUserDefinedTypes = async () => {
365411
const query = 'HELP DATABASE SYSUDTLIB;';
366-
const queryResult = await connection.execute(query);
412+
const queryResult = await connection.execute(query, signal);
367413

368414
return queryResult
369415
.filter(filterUdt)
@@ -390,6 +436,19 @@ const createInstance = (connection, _) => {
390436
};
391437

392438
const close = async sshService => {
439+
if (abortController) {
440+
abortController.abort();
441+
abortController = null;
442+
}
443+
444+
for (const activeQuery of activeQueries) {
445+
if (activeQuery && !activeQuery.killed) {
446+
activeQuery.kill('SIGTERM');
447+
}
448+
}
449+
450+
activeQueries.clear();
451+
393452
if (connection) {
394453
connection = null;
395454
}
@@ -444,7 +503,7 @@ const getIndexType = index => {
444503

445504
const filterUdt = object => object.Kind === 'U';
446505

447-
const excludeSystemUdt = type => !SYSTEM_UDT.includes(type['Table/View/Macro Dictionary Name']);
506+
const excludeSystemUdt = type => !SYSTEM_UDT.has(type['Table/View/Macro Dictionary Name']);
448507

449508
module.exports = {
450509
connect,

0 commit comments

Comments
 (0)