Skip to content
This repository has been archived by the owner on Mar 15, 2021. It is now read-only.

Commit

Permalink
Improve cancel to work immediately
Browse files Browse the repository at this point in the history
Depedend on the client the cancel does not work immediately.
At least the kill command works but the executing query takes some time
to receive the response from the server that the query has been
canceled.
So, once the kill command has work successfully. We cancel the promise
immediately.
  • Loading branch information
maxcnunes committed Nov 5, 2016
1 parent 6bef835 commit 2e28fff
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 2 deletions.
15 changes: 14 additions & 1 deletion src/db/clients/mysql.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import mysql from 'mysql';
import { identify } from 'sql-query-identifier';

import createDebug from '../../debug';
import { createCancelablePromise } from '../../utils';
import errors from '../../errors';

const debug = createDebug('db:clients:mysql');

Expand Down Expand Up @@ -178,9 +180,14 @@ export async function getTableKeys(conn, database, table) {
}));
}


export function query(conn, queryText) {
let pid = null;
let canceling = false;
const cancelable = createCancelablePromise({
...errors.CANCELED_BY_USER,
sqlectronError: 'CANCELED_BY_USER',
});

return {
execute() {
Expand All @@ -194,7 +201,10 @@ export function query(conn, queryText) {
pid = dataPid[0].pid;

try {
const data = await executeQuery(connClient, queryText);
const data = await Promise.race([
cancelable.wait(),
executeQuery(connClient, queryText),
]);

pid = null;

Expand All @@ -206,6 +216,8 @@ export function query(conn, queryText) {
}

throw err;
} finally {
cancelable.discard();
}
});
},
Expand All @@ -220,6 +232,7 @@ export function query(conn, queryText) {
await driverExecuteQuery(conn, {
query: `kill ${pid};`,
});
cancelable.cancel();
} catch (err) {
canceling = false;
throw err;
Expand Down
15 changes: 14 additions & 1 deletion src/db/clients/postgresql.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import pg from 'pg';
import { identify } from 'sql-query-identifier';

import createDebug from '../../debug';
import { createCancelablePromise } from '../../utils';
import errors from '../../errors';

const debug = createDebug('db:clients:postgresql');

Expand Down Expand Up @@ -225,6 +227,10 @@ export async function getTableKeys(conn, database, table, schema) {
export function query(conn, queryText) {
let pid = null;
let canceling = false;
const cancelable = createCancelablePromise({
...errors.CANCELED_BY_USER,
sqlectronError: 'CANCELED_BY_USER',
});

return {
execute() {
Expand All @@ -238,7 +244,10 @@ export function query(conn, queryText) {
pid = dataPid.rows[0].pid;

try {
const data = await executeQuery(connClient, queryText);
const data = await Promise.race([
cancelable.wait(),
executeQuery(connClient, queryText),
]);

pid = null;

Expand All @@ -250,6 +259,8 @@ export function query(conn, queryText) {
}

throw err;
} finally {
cancelable.discard();
}
});
},
Expand All @@ -268,6 +279,8 @@ export function query(conn, queryText) {
if (!data.rows[0].pg_cancel_backend) {
throw new Error(`Failed canceling query with pid ${pid}.`);
}

cancelable.cancel();
} catch (err) {
canceling = false;
throw err;
Expand Down
7 changes: 7 additions & 0 deletions src/errors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default {
CANCELED_BY_USER: {
code: 'CANCELED_BY_USER',
name: 'Query canceled by user',
message: 'Query canceled by user. The query process may still in the process list. But has already received the command to kill it successfully.',
},
};
30 changes: 30 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,33 @@ export function getPort() {
});
});
}

export function createCancelablePromise(error, timeIdle = 100) {
let canceled = false;
let discarded = false;

const wait = (time) => new Promise((resolve) => setTimeout(resolve, time));

return {
async wait() {
while (!canceled && !discarded) {
await wait(timeIdle);
}

if (canceled) {
const err = new Error(error.message || 'Promise canceled.');

Object.getOwnPropertyNames(error)
.forEach((key) => err[key] = error[key]); // eslint-disable-line no-return-assign

throw err;
}
},
cancel() {
canceled = true;
},
discard() {
discarded = true;
},
};
}

0 comments on commit 2e28fff

Please sign in to comment.