mirror of
https://github.com/Fabio286/antares.git
synced 2025-06-05 21:59:22 +02:00
perf: use fork() for the export process
This commit is contained in:
@ -1,10 +1,11 @@
|
|||||||
import { ipcMain, dialog, Notification } from 'electron';
|
|
||||||
import path from 'path';
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { fork } from 'child_process';
|
||||||
|
import { ipcMain, dialog, Notification } from 'electron';
|
||||||
|
|
||||||
// @TODO: need some factories
|
// @TODO: need some factories
|
||||||
import MysqlExporter from '../libs/exporters/sql/MysqlExporter';
|
|
||||||
import MysqlImporter from '../libs/importers/sql/MysqlImporter';
|
import MysqlImporter from '../libs/importers/sql/MysqlImporter';
|
||||||
|
const isDevelopment = process.env.NODE_ENV !== 'production';
|
||||||
|
|
||||||
export default connections => {
|
export default connections => {
|
||||||
let exporter = null;
|
let exporter = null;
|
||||||
@ -172,80 +173,72 @@ export default connections => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('export', async (event, { uid, type, tables, ...rest }) => {
|
ipcMain.handle('export', (event, { uid, type, tables, ...rest }) => {
|
||||||
if (exporter !== null) return;
|
if (exporter !== null) return;
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case 'mysql':
|
|
||||||
case 'maria':
|
|
||||||
exporter = new MysqlExporter(connections[uid], tables, rest);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return {
|
|
||||||
status: 'error',
|
|
||||||
response: `${type} exporter not aviable`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const outputFileName = path.basename(rest.outputFile);
|
|
||||||
|
|
||||||
if (fs.existsSync(rest.outputFile)) {
|
|
||||||
const result = await dialog.showMessageBox({
|
|
||||||
type: 'warning',
|
|
||||||
message: `File ${outputFileName} already exists. Do you want to replace it?`,
|
|
||||||
detail:
|
|
||||||
'A file with the same name already exists in the target folder. Replacing it will overwrite its current contents.',
|
|
||||||
buttons: ['Cancel', 'Replace'],
|
|
||||||
defaultId: 0,
|
|
||||||
cancelId: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result.response !== 1) {
|
|
||||||
exporter = null;
|
|
||||||
return { status: 'error', response: 'Operation aborted' };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
exporter.once('error', err => {
|
(async () => {
|
||||||
reject(err);
|
if (fs.existsSync(rest.outputFile)) { // If file exists ask for replace
|
||||||
});
|
const result = await dialog.showMessageBox({
|
||||||
|
type: 'warning',
|
||||||
|
message: `File ${rest.outputFile} already exists. Do you want to replace it?`,
|
||||||
|
detail: 'A file with the same name already exists in the target folder. Replacing it will overwrite its current contents.',
|
||||||
|
buttons: ['Cancel', 'Replace'],
|
||||||
|
defaultId: 0,
|
||||||
|
cancelId: 0
|
||||||
|
});
|
||||||
|
|
||||||
exporter.once('end', () => {
|
if (result.response !== 1) {
|
||||||
resolve({ cancelled: exporter.isCancelled });
|
resolve({
|
||||||
});
|
type: 'error',
|
||||||
|
response: 'Operation aborted'
|
||||||
exporter.once('cancel', () => {
|
});
|
||||||
fs.unlinkSync(exporter.outputFile);
|
return;
|
||||||
});
|
}
|
||||||
|
|
||||||
exporter.on('progress', state => {
|
|
||||||
event.sender.send('export-progress', state);
|
|
||||||
});
|
|
||||||
|
|
||||||
exporter.run();
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
if (!response.cancelled) {
|
|
||||||
new Notification({
|
|
||||||
title: 'Export finished',
|
|
||||||
body: `Finished exporting to ${outputFileName}`
|
|
||||||
}).show();
|
|
||||||
}
|
}
|
||||||
return { status: 'success', response };
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
new Notification({
|
|
||||||
title: 'Export error',
|
|
||||||
body: err.toString()
|
|
||||||
}).show();
|
|
||||||
|
|
||||||
return { status: 'error', response: err.toString() };
|
// Init exporter process
|
||||||
})
|
exporter = fork(isDevelopment ? './dist/exporter.js' : path.resolve(__dirname, './exporter.js'));
|
||||||
.finally(() => {
|
exporter.send({
|
||||||
exporter.removeAllListeners();
|
type: 'init',
|
||||||
exporter = null;
|
client: {
|
||||||
});
|
name: type,
|
||||||
|
config: await connections[uid].getDbConfig()
|
||||||
|
},
|
||||||
|
tables,
|
||||||
|
options: rest
|
||||||
|
});
|
||||||
|
|
||||||
|
// Exporter message listener
|
||||||
|
exporter.on('message', ({ type, payload }) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'export-progress':
|
||||||
|
event.sender.send('export-progress', payload);
|
||||||
|
break;
|
||||||
|
case 'end':
|
||||||
|
exporter.kill();
|
||||||
|
exporter = null;
|
||||||
|
resolve({ status: 'success', response: payload });
|
||||||
|
break;
|
||||||
|
case 'cancel':
|
||||||
|
exporter.kill();
|
||||||
|
exporter = null;
|
||||||
|
resolve({ status: 'error', response: 'Operation cancelled' });
|
||||||
|
break;
|
||||||
|
case 'error':
|
||||||
|
exporter.kill();
|
||||||
|
exporter = null;
|
||||||
|
resolve({ status: 'error', response: payload });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exporter.on('exit', code => {
|
||||||
|
exporter = null;
|
||||||
|
resolve({ status: 'error', response: `Operation ended with code: ${code}` });
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('abort-export', async event => {
|
ipcMain.handle('abort-export', async event => {
|
||||||
@ -262,7 +255,7 @@ export default connections => {
|
|||||||
|
|
||||||
if (result.response === 1) {
|
if (result.response === 1) {
|
||||||
willAbort = true;
|
willAbort = true;
|
||||||
exporter.cancel();
|
exporter.send({ type: 'cancel' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,9 +53,7 @@ ${footer}
|
|||||||
let rowCount = 0;
|
let rowCount = 0;
|
||||||
let sqlStr = '';
|
let sqlStr = '';
|
||||||
|
|
||||||
const countResults = await this._client.raw(
|
const countResults = await this._client.raw(`SELECT COUNT(1) as count FROM \`${this.schemaName}\`.\`${tableName}\``);
|
||||||
`SELECT COUNT(1) as count FROM \`${this.schemaName}\`.\`${tableName}\``
|
|
||||||
);
|
|
||||||
if (countResults.rows.length === 1) rowCount = countResults.rows[0].count;
|
if (countResults.rows.length === 1) rowCount = countResults.rows[0].count;
|
||||||
|
|
||||||
if (rowCount > 0) {
|
if (rowCount > 0) {
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
import { Worker, isMainThread, workerData, parentPort } from 'worker_threads';
|
|
||||||
import
|
|
||||||
|
|
||||||
if (isMainThread) {
|
|
||||||
module.exports = function run (workerData) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const worker = new Worker(__filename, { workerData });
|
|
||||||
worker.on('message', resolve);
|
|
||||||
worker.on('error', reject);
|
|
||||||
worker.on('exit', (code) => {
|
|
||||||
if (code !== 0)
|
|
||||||
reject(new Error(`Worker stopped with exit code ${code}`));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
}
|
|
61
src/main/workers/exporter.js
Normal file
61
src/main/workers/exporter.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { ClientsFactory } from '../libs/ClientsFactory';
|
||||||
|
// TODO: exporter factory class
|
||||||
|
import MysqlExporter from '../libs/exporters/sql/MysqlExporter.js';
|
||||||
|
import fs from 'fs';
|
||||||
|
let exporter;
|
||||||
|
|
||||||
|
process.on('message', async ({ type, client, tables, options }) => {
|
||||||
|
if (type === 'init') {
|
||||||
|
const connection = await ClientsFactory.getConnection({
|
||||||
|
client: client.name,
|
||||||
|
params: client.config,
|
||||||
|
poolSize: 5
|
||||||
|
});
|
||||||
|
await connection.connect();
|
||||||
|
|
||||||
|
switch (client.name) {
|
||||||
|
case 'mysql':
|
||||||
|
case 'maria':
|
||||||
|
exporter = new MysqlExporter(connection, tables, options);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
process.send({
|
||||||
|
type: 'error',
|
||||||
|
payload: `"${client.name}" exporter not aviable`
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
exporter.once('error', err => {
|
||||||
|
console.error(err);
|
||||||
|
process.send({
|
||||||
|
type: 'error',
|
||||||
|
payload: err.toString()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
exporter.once('end', () => {
|
||||||
|
process.send({
|
||||||
|
type: 'end',
|
||||||
|
payload: { cancelled: exporter.isCancelled }
|
||||||
|
});
|
||||||
|
connection.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
exporter.once('cancel', () => {
|
||||||
|
fs.unlinkSync(exporter.outputFile);
|
||||||
|
process.send({ type: 'cancel' });
|
||||||
|
});
|
||||||
|
|
||||||
|
exporter.on('progress', state => {
|
||||||
|
process.send({
|
||||||
|
type: 'export-progress',
|
||||||
|
payload: state
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
exporter.run();
|
||||||
|
}
|
||||||
|
else if (type === 'cancel')
|
||||||
|
exporter.cancel();
|
||||||
|
});
|
@ -1,75 +0,0 @@
|
|||||||
// import MysqlExporter from '../libs/exporters/sql/MysqlExporter.js';
|
|
||||||
// import path from 'path';
|
|
||||||
// import fs from 'fs';
|
|
||||||
// let exporter;
|
|
||||||
|
|
||||||
// switch (type) {
|
|
||||||
// case 'mysql':
|
|
||||||
// case 'maria':
|
|
||||||
// exporter = new MysqlExporter(connections[uid], tables, rest);
|
|
||||||
// break;
|
|
||||||
// default:
|
|
||||||
// // return {
|
|
||||||
// // status: 'error',
|
|
||||||
// // response: `${type} exporter not aviable`
|
|
||||||
// // };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const outputFileName = path.basename(rest.outputFile);
|
|
||||||
|
|
||||||
// if (fs.existsSync(rest.outputFile)) {
|
|
||||||
// const result = await dialog.showMessageBox({
|
|
||||||
// type: 'warning',
|
|
||||||
// message: `File ${outputFileName} already exists. Do you want to replace it?`,
|
|
||||||
// detail:
|
|
||||||
// 'A file with the same name already exists in the target folder. Replacing it will overwrite its current contents.',
|
|
||||||
// buttons: ['Cancel', 'Replace'],
|
|
||||||
// defaultId: 0,
|
|
||||||
// cancelId: 0
|
|
||||||
// });
|
|
||||||
|
|
||||||
// if (result.response !== 1)
|
|
||||||
// exporter = null;
|
|
||||||
// // return { status: 'error', response: 'Operation aborted' };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // return new Promise((resolve, reject) => {
|
|
||||||
// exporter.once('error', err => {
|
|
||||||
// reject(err);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// exporter.once('end', () => {
|
|
||||||
// resolve({ cancelled: exporter.isCancelled });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// exporter.once('cancel', () => {
|
|
||||||
// fs.unlinkSync(exporter.outputFile);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// exporter.on('progress', state => {
|
|
||||||
// event.sender.send('export-progress', state);
|
|
||||||
// });
|
|
||||||
|
|
||||||
// exporter.run();
|
|
||||||
// // })
|
|
||||||
// // .then(response => {
|
|
||||||
// // if (!response.cancelled) {
|
|
||||||
// // new Notification({
|
|
||||||
// // title: 'Export finished',
|
|
||||||
// // body: `Finished exporting to ${outputFileName}`
|
|
||||||
// // }).show();
|
|
||||||
// // }
|
|
||||||
// // return { status: 'success', response };
|
|
||||||
// // })
|
|
||||||
// // .catch(err => {
|
|
||||||
// // new Notification({
|
|
||||||
// // title: 'Export error',
|
|
||||||
// // body: err.toString()
|
|
||||||
// // }).show();
|
|
||||||
|
|
||||||
// // return { status: 'error', response: err.toString() };
|
|
||||||
// // })
|
|
||||||
// // .finally(() => {
|
|
||||||
// // exporter.removeAllListeners();
|
|
||||||
// // exporter = null;
|
|
||||||
// // });
|
|
@ -110,5 +110,4 @@ export default {
|
|||||||
.modal.modal-sm .modal-container {
|
.modal.modal-sm .modal-container {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -292,6 +292,7 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
|
addNotification: 'notifications/addNotification',
|
||||||
refreshSchema: 'workspaces/refreshSchema'
|
refreshSchema: 'workspaces/refreshSchema'
|
||||||
}),
|
}),
|
||||||
async startExport () {
|
async startExport () {
|
||||||
@ -306,13 +307,17 @@ export default {
|
|||||||
...this.options
|
...this.options
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = await Schema.export(params);
|
try {
|
||||||
if (result) {
|
const { status, response } = await Schema.export(params);
|
||||||
if (result.status === 'success')
|
if (status === 'success')
|
||||||
this.progressStatus = result.response.cancelled ? this.$t('word.aborted') : this.$t('word.completed');
|
this.progressStatus = response.cancelled ? this.$t('word.aborted') : this.$t('word.completed');
|
||||||
|
else {
|
||||||
else
|
this.progressStatus = response;
|
||||||
this.progressStatus = result.response;
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isExporting = false;
|
this.isExporting = false;
|
||||||
|
@ -165,6 +165,15 @@ option:checked {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal-overlay{
|
||||||
|
background: rgba( 255, 255, 255, 0.1 );
|
||||||
|
box-shadow: 0 8px 32px 0 rgba( 31, 38, 135, 0.37 );
|
||||||
|
backdrop-filter: blur( 4px );
|
||||||
|
-webkit-backdrop-filter: blur( 4px );
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 1px solid rgba( 255, 255, 255, 0.18 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab {
|
.tab {
|
||||||
|
@ -13,7 +13,7 @@ const config = {
|
|||||||
mode: process.env.NODE_ENV,
|
mode: process.env.NODE_ENV,
|
||||||
devtool: isDevMode ? 'eval-source-map' : false,
|
devtool: isDevMode ? 'eval-source-map' : false,
|
||||||
entry: {
|
entry: {
|
||||||
exporterProcess: path.join(__dirname, './src/main/workers/exporterProcess.js')
|
exporter: path.join(__dirname, './src/main/workers/exporter.js')
|
||||||
},
|
},
|
||||||
target: 'node',
|
target: 'node',
|
||||||
output: {
|
output: {
|
||||||
|
Reference in New Issue
Block a user