-
Notifications
You must be signed in to change notification settings - Fork 463
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
node-mssql is not truly concurrent on queries #217
Comments
There is no queue for requests outside transaction. You can try it yourself: var sql = require('mssql');
var config = {
user: "...",
password: "...",
server: "localhost",
port: 1433,
database: "...",
pool: {
max: 3,
min: 1
}
}
var connection = new sql.Connection(config, function(err) {
console.log("connected");
var request1 = new sql.Request(connection);
request1.stream = true
request1.query('waitfor delay \'00:00:03\'');
request1.on('done', function() {
console.log("done 1");
});
var request2 = new sql.Request(connection);
request2.stream = true
request2.query('waitfor delay \'00:00:02\'');
request2.on('done', function() {
console.log("done 2");
});
var request3 = new sql.Request(connection);
request3.stream = true
request3.query('waitfor delay \'00:00:01\'');
request3.on('done', function() {
console.log("done 3");
});
}); Result:
Result after changing
|
@patriksimek Here is your example extended to correspond with my original code. What I want to happen is to get /*jshint esnext: true */
/*jslint node: true */
/* global -Promise */
'use strict';
// check execution environment
let dev = process.env.NODE_ENV === 'development' ? true : false;
let fs = require('fs');
let sql = require('mssql');
let Promise = require('bluebird');
// config
let config;
try {
config = JSON.parse(fs.readFileSync('../../config/server.json', 'utf-8'));
} catch (err) {
throw Error('unable to read config file.');
}
sql.Promise = Promise;
let successConfig = config.database.success;
let dbConfig = {
user: successConfig.user,
password: successConfig.pw,
server: successConfig.host,
database: successConfig.name,
requestTimeout: successConfig.requestTimeout,
connectionTimeout: successConfig.connectionTimeout,
options: {
tdsVersion: '7_3_B'
},
pool: {
max: 15,
min: 5,
idleTimeoutMillis: successConfig.pool.idleTimeoutMillis
}
};
// instantiate new connection pool and run queries
let connectionPool = new sql.Connection(dbConfig, (err) => {
if(err){
throw Error(err);
}
console.log('connected to database');
var request1 = new sql.Request(connectionPool);
request1.stream = true;
request1.query('SELECT COUNT(*) FROM KUNDEN');
request1.on('row', (row) => {
var innerRequest1 = new sql.Request(connectionPool);
innerRequest1.stream = true;
innerRequest1.query('SELECT * FROM KUNDEN');
innerRequest1.on('row', (row) => console.log('got row on inner request 1'));
innerRequest1.on('done', () => console.log('inner request 1 done'));
});
request1.on('done', function() {
console.log("done 1");
});
var request2 = new sql.Request(connectionPool);
request2.stream = true;
request2.query('SELECT COUNT(*) FROM TA_LUMO_Karbon_Vertrag_Ziel');
request2.on('row', (row) => {
var innerRequest2 = new sql.Request(connectionPool);
innerRequest2.stream = true;
innerRequest2.query('SELECT * FROM TA_LUMO_Karbon_Vertrag_Ziel OPTION(RECOMPILE)');
innerRequest2.on('row', (row) => console.log('got row on inner request 2'));
innerRequest2.on('done', () => console.log('inner request 2 done'));
});
request2.on('done', function() {
console.log("done 2");
});
var request3 = new sql.Request(connectionPool);
request3.stream = true;
request3.query('SELECT COUNT(*) FROM TA_LUMO_Karbon_Kundengeraete OPTION(RECOMPILE)');
request3.on('row', (row) => {
var innerRequest3 = new sql.Request(connectionPool);
innerRequest3.stream = true;
innerRequest3.query('SELECT * FROM TA_LUMO_Karbon_Kundengeraete OPTION(RECOMPILE)');
innerRequest3.on('row', (row) => console.log('got row on inner request 3'));
innerRequest3.on('done', () => console.log('inner request 3 done'));
});
request3.on('done', function() {
console.log("done 3");
});
}); output 1:
If I use your example with the let connectionPool = new sql.Connection(dbConfig, (err) => {
if(err){
throw Error(err);
}
console.log('connected to database');
var reqTime1 = new Date();
var request1 = new sql.Request(connectionPool);
request1.stream = true;
request1.query('waitfor delay \'00:00:03\'');
request1.on('row', (row) => {
});
request1.on('done', function() {
console.log(`done 1 ${new Date() - reqTime1}ms`);
var inReqTime1 = new Date();
var innerRequest1 = new sql.Request(connectionPool);
innerRequest1.stream = true;
innerRequest1.query('waitfor delay \'00:00:06\'');
innerRequest1.on('row', (row) => {});
innerRequest1.on('done', () => console.log(`inner request 1 done ${new Date() - inReqTime1}ms`));
});
var reqTime2 = new Date();
var request2 = new sql.Request(connectionPool);
request2.stream = true;
request2.query('waitfor delay \'00:00:02\'');
request2.on('row', (row) => {
});
request2.on('done', function() {
console.log(`done 2 ${new Date() - reqTime2}ms`);
var inReqTime2 = new Date();
var innerRequest2 = new sql.Request(connectionPool);
innerRequest2.stream = true;
innerRequest2.query('waitfor delay \'00:00:02\'');
innerRequest2.on('row', (row) => {});
innerRequest2.on('done', () => console.log(`inner request 2 done ${new Date() - inReqTime2}ms`));
});
var reqTime3 = new Date();
var request3 = new sql.Request(connectionPool);
request3.stream = true;
request3.query('waitfor delay \'00:00:01\'');
request3.on('row', (row) => {
});
request3.on('done', function() {
console.log(`done 3 ${new Date() - reqTime3}ms`);
var inReqTime3 = new Date();
var innerRequest3 = new sql.Request(connectionPool);
innerRequest3.stream = true;
innerRequest3.query('waitfor delay \'00:00:01\'');
innerRequest3.on('row', (row) => {});
innerRequest3.on('done', () => console.log(`inner request 3 done ${new Date() - inReqTime3}ms`));
});
}); output 2:
Now without any inner requests but directly querying 3 different tables with stream enabled:
Gives the same result as using 3 pools for each query also runs the queries sequentially:
Does the query parser somehow block subsequent requests if there are rows returned? Yes, it does work with |
Discussion moved to tediousjs/tedious#319 |
This is unfortunate because it is the main driver node-mssql relies upon, I am not sure if the other drives fair any better. Maybe the windows native msnodesql driver doesn't have this issue. |
Hi Patrik.
I have some code which should have simultaneous queries running. I am using a global connection pool by instantiating a new
let sqlConn = new sql.Connection(dbconfig)
with a pool of max 15 connections.Using that single connection pool called
sqlConn
, I am not able to run multiplesqlConn.request()
queries concurrently. It always queues requests up and runs them sequentially, instead of executing all calledquery()
functions. Is there a way to stop the queuing?What is the reason requests are queued if the purpose of NodeJS is to run code concurrently? Problem is that this design stalls every other query in the program. From my understanding, atleast queries which have
stream = true
should allow other streamed queries to run concurrently, so that node-mssql is between those two functions concurrently instead of using all power to fininsh one and then executing the other. Sequentially running queries should explicitly be written in code by nesting callbacks or using thenable Promises and not by the library doing it automatically for me.Here is an example:
The connection pool:
Now using that connection pool, we run 3 Promises concurrently, in this example, we load data from the sql server into a cache, all at the same time:
Each of this
preloadCache
functions does the same, but when the program is executed, only one of them is returning query results back at a time. The 3preloadCache
functions are called immediately, they don't wait for the one before to be finished. (The wordentities
is replaced with the actual entity which is cached in the original code)What really happens is, that in the console you might or might no see two
mainLogger.info('preloading ${amountOfEntities} entities');
messages and then you can see how only one of the streamed queries is returning data at a time because thereport(onePercent, done, 'entity');
function never gets called from the other queries as only one is ran at a time.Is this a bug? If I have 15 connections in a pool, in my opinion, I should be able to run 15 streamed queries simlultaneously. Here we see, that only one is executed.
Changing
sqlConn.request()
tonew sql.Request(sqlConn)
has no effect.The documentation does not comply with what is actually happening:
"Internally, each Connection instance is a separate pool of TDS connections. Once you create a new Request/Transaction/Prepared Statement, a new TDS connection is acquired from the pool and reserved for desired action (So one executing query handled by one connection from the pool?). Once the action is complete, connection is released back to the pool. Connection health check is built-in so once the dead connection is discovered, it is immediately replaced with a new one."
The text was updated successfully, but these errors were encountered: