Open tysjiang opened 9 years ago
I will do a test using the SELECT 1
query on a timer. If it ends up working, I'll post back here. I still haven't found any procedure that forces this bug to appear, so it's hard for me to test properly.
We are also experiencing this issue as well. This app is a typical NodeJS/Express API that queries data out of a large AzureSQL Database using this Tedious driver. Our workaround fix for now is catching the error as mentioned above, but it would be great if this was built in to the library. I'll see if I can find time to dig into this repo and put a patch in.
Hey everybody, apologies for the huge delay since my last post. I tested the SELECT 1
query pinger as a way of avoiding this issue, and I can confirm that it does not work for me. I've tried catching the error before without much success, but based on the post by @GISDev01 maybe I will try again with some different code.
My error catching code didn't seem to work either. Am I catching them incorrectly? Here's my code, based on the last few suggestions:
//Ping the database constantly
setInterval(function () {
var updateRequest = database.request();
var statement = "SELECT 1";
updateRequest.query(statement, function (err, recordset) {
if (err) {
console.log("Database failure.");
}
});
}, 9000);
var initTime = process.hrtime()[0];
//Catch connection reset errors and reconnect to the database
sql.on('error', function (err) {
if (err.message == "Connection lost - read ECONNRESET") {
console.log("Connection reset bug appeared.");
console.log("It took " + process.hrtime()[0] - initTime);
}
database.close();
database = new sql.Connection(databaseConfig, null);
});
@Reubend you can try to console.log(err)
and then see the exact string you get for err.message
.
Also note that you don't need to manually reconnect (tedious should do it for you) only catching the error and doing nothing (i.e console logging) prevents tedious
from breaking your app.
Again... it is not a solution but a hack...
Something that I haven't tested is whether the SQL query gets retried when the app reconnects :fearful:
Thanks @francolaiuppa for the tip! I'm going to remove the reconnection code and see if that helps.
@Reubend did you end up solving this?
Anyone has a working solution for this ?
Wondering if there is any solution to this besides the previously mentioned logging hacks :)
Fix for transient failure (#574) submitted by @tvrprasad is already in master. Changes should be out in next npm release.
When is this new npm release coming out? I am struggling with this issue and it looks like it's been a problem for years.
When using "SELECT 1" ping workaround, I get intermittent "ConnectionError: Failed to connect to xyz.database.windows.net:1433 in 10000ms" errors in the ping. ECONNRESETs seem to have stopped, though.
@jpalo Can you test with tedious@2.1.1
? It has the fix for intermittent failure :)
Fixed by #574. Closing this issue. Feel free to open it if you face this issue with version >= tedious@2.1.1
.
we're still seeing random connection resets on our end unfortunately.
the tedious version we're using is v2.2.1
. so i dont believe the update has resolved the issue (at least for us)
We're using stored procedures to handle a majority of the SQL queries. some of these queries and INSERT statements and therefore dont return anything. in cases such as these , do we have to manually close the connection between node and sql? something like connection.close()
?
Is there any other suggestion that you can provide?
@Kannaj Even if your TSQL doesn't return anything, Request
will emit either done
, doneInProc
or doneProc
event, based on SQL query. You can use those events to close the connection. And, yes, your application has to decide when to close the connection and call connection.close()
manually.
Regarding the connection reset issue, can you share more details? How is connection reset related to closing the connection? Just to confirm, does this involve Azure SQL Server?
@Kannaj Hey there! π
I'm really sad to hear you're facing trouble with tedious. As @v-suhame mentioned, your issue description is unfortunately not enough for us to debug this. It'd be great if you could help us help you! π
Which version of SQLServer do you connect to?
How many requests does your application execute, and how often do you see connection resets?
Do you have multiple processes that connect to the SQL Server? Is there only a single connection open per process? Do different processes (or different connections in a single process) see the reset at the same time?
Is there a stack trace or anything that helps to pinpoint a bit where the connection reset happens?
Does the connection reset happen during query execution or during times where no query is run?
How much time passes between the llast query execution and the connection reset?
Can you check the SQL Server logs and see if there's anything in there that might be helpful in there?
If anyone else in this thread can answer these questions, feel free to do so as well! π
I used to get this error using Azure SQL Server, but solved it by implementing automatic retries for any requests that failed with a transient error (I keep retrying until my TTL of 1 min expires using the async library). Note: each time you call query or execute on a request a new connection is acquired from the pool so this should solve any problems with individual connections that are acting up. Here is the code I use to test for transient errors.
function isErrorTransient(err) {
var arrayTransientErrors = ['ENOCONN', 'ENOTOPEN', 'ESOCKET', 'ENOTFOUND', 'ENETDOWN', 'EADDRNOTAVAIL', 'ETIMEOUT', 'ETIMEDOUT'];
return (err && 'code' in err && arrayTransientErrors.indexOf(err.code) > -1);
}
It would be very nice if the library provided an option on Requests to automatically retry with a new connection when a transient error occurred (with a TTL parameter).
@jtmilne Thanks for sharing the workaround π
@jtmilne @Kannaj Can you verify if the error occur only during idle connection (in the newer version of driver)? That would be of great help, we can rule out or consider idle connection resiliency based on it.
an idle connection is the one that is active (opened, but not in the pool) but itβs not executing a command or waiting for data.
From MSDN doc for other Driver,
Connection resiliency is the principle that a broken idle connection can be reestablished, within certain constraints. If a connection to Microsoft SQL Server fails, connection resiliency allows the client to automatically attempt to reestablish the connection.
@v-suhame Sorry but I wrote this fix 2 years ago so I don't remember the details of the errors.
Thanks for your prompt response guys
@v-suhame - my thinking is that the connection is being reset by Sql because the connection still persists after the procedure is executed. heres' the structure of my SQL queries
exports.do = function(type, params, callback){
var conn = new Connection(connectionConf);
conn.on('connect', function(err) {
// if a connection error occured, then fail immediately
if (err) {
logger("warn", "db", "cannot connect to sql db", null, {"err":err});
callback(err);
}
else
{
var request = new Request(type, function(err, rowCount){
if (err) {
callback(err);
}
else if (rowCount == 0) {
callback();
}
});
// set the parameters based on the request that was supposed to be executed
switch(type) {
case "procName":
request.addParameter('email', TYPES.VarChar, params[0]);
conn.callProcedure(request);
break;
}
request.on('row', function(columns) {
var returnObject = {};
// iterate and map the object to each column - the column name is in the metadata object
columns.forEach(function(column){
returnObject[column.metadata.colName] = column.value;
});
// return with an error if the error occured and was caught by the procedure
if (columns[0].metadata.colName == 'error') {
callback(returnObject);
}
// return with the result set given by the procedure
else {
callback(null,returnObject);
}
})
}
});
conn.on('error', function(err){
logger("warn", "db", "exception in tedious", null, {"err":err});
})
};
Am i missing something out?
@arthurschreiber
Connection resets are quite random and hard to measure unfortunately. but we see one happen every few hours. regarding number of queries sent - we have around 10-12 requests happen per session
We have only one process connected to the SQL server
Is there a stack trace or anything that helps to pinpoint a bit where the connection reset happens?
ConnectionError: Connection lost - read ECONNRESET
at ConnectionError (D:\home\site\wwwroot\node_modules\tedious\lib\errors.js:12:12)
at Connection.socketError (D:\home\site\wwwroot\node_modules\tedious\lib\connection.js:873:28)
at emitOne (events.js:121:20)
at Socket.emit (events.js:211:7)
at emitErrorNT (internal/streams/destroy.js:64:8)
at _combinedTickCallback (internal/process/next_tick.js:138:11)
at process._tickDomainCallback (internal/process/next_tick.js:218:9)
2018-01-08T07:50:34.791Z - error: uncaughtException: Connection lost - read ECONNRESET date=Mon Jan 08 2018 07:50:34 GMT+0000 (Coordinated Universal Time), pid=7068, uid=null, gid=null, cwd=D:\home\site\wwwroot, execPath=D:\Program Files (x86)\nodejs\8.9.3\node.exe, version=v8.9.3, argv=[D:\Program Files (x86)\nodejs\8.9.3\node.exe, D:\home\site\wwwroot\server.js], rss=37429248, heapTotal=38248448, heapUsed=34221824, external=33298857, loadavg=[0, 0, 0], uptime=undefined, trace=[column=12, file=D:\home\site\wwwroot\node_modules\tedious\lib\errors.js, function=ConnectionError, line=12, method=null, native=false, column=28, file=D:\home\site\wwwroot\node_modules\tedious\lib\connection.js, function=Connection.socketError, line=873, method=socketError, native=false, column=20, file=events.js, function=emitOne, line=121, method=null, native=false, column=7, file=events.js, function=Socket.emit, line=211, method=emit, native=false, column=8, file=internal/streams/destroy.js, function=emitErrorNT, line=64, method=null, native=false, column=11, file=internal/process/next_tick.js, function=_combinedTickCallback, line=138, method=null, native=false, column=9, file=internal/process/next_tick.js, function=process._tickDomainCallback, line=218, method=_tickDomainCallback, native=false], stack=[ConnectionError: Connection lost - read ECONNRESET, at ConnectionError (D:\home\site\wwwroot\node_modules\tedious\lib\errors.js:12:12), at Connection.socketError (D:\home\site\wwwroot\node_modules\tedious\lib\connection.js:873:28), at emitOne (events.js:121:20), at Socket.emit (events.js:211:7), at emitErrorNT (internal/streams/destroy.js:64:8), at _combinedTickCallback (internal/process/next_tick.js:138:11), at process._tickDomainCallback (internal/process/next_tick.js:218:9)]
Does the connection reset happen during query execution or during times where no query is run? This again is quite varied , but usually theres a reset happening for queries that were carried out several minutes ago
How much time passes between the llast query execution and the connection reset? Not sure on this unfortunately as its quite varied
Can you check the SQL Server logs and see if there's anything in there that might be helpful in there? Will get back to you on this.
@v-suhame , @arthurschreiber - any idea about this? We currently out of solutions. :(
Any help or advice would be great
@Kannaj Sorry for the delayed response. Is there any reason for not closing the connection after Request
completes? If you're not reusing the connection, it is best to close it after use to release resources both in Server and Client side.
If you want the connection to stay open, maybe you can try https://github.com/tediousjs/tedious/issues/300#issuecomment-355672780 as temporary solution, till we get to the root of it.
Is that an on-premise SQL Server or Azure SQL Server? Do share the SQL Server logs when you get them.
I am getting this issue also using the latest tedious, v2 of the function app runtime on Azure and MS SQL server. I'm thinking the only solution might be to write a sync loop to try up to 5 times to connect before crashing. Anyone else found a solution?
I also got this error just after starting bulk load. And the cause was that I tried to insert text which had more characters than column allowed. When I altered column it started to work.
I go this error when using the AzureDB example code with Tedious and Node to make requests etc
Got same error when using newest tedious version with typeorm. It works half years ago, not sure why. I didn't do any bulk load, only some simple query that test if a table already existed.
Actually it's not intermittent. A very stable behavior.
Hi @wy193777, Can you provide a reproduce script with the typeorm? I want to try it on my side, see if I got the same behavior. Thanks!
So... how do you close a connection? :D and why do connections 'crash' and go to sleep? no clear fix found.
There can be two reasons for this issue to happen, both most likely linked to cloud based SQL.
The most likely reason is to implement code for a persistent connection in your database connection code. Probably something similar to:
if (connPoolPromise) return connPoolPromise;
You persist the promise but the database, if the connection (by any reason) has been rejected, cannot connect it will proceed to try to make the request using a non existent connection and throw an error Uncaught exception: ConnectionError: Connection lost - read ECONNRESET. This error would be thrown on your REQUEST and not on your CONNECTION. I'm assuming most of the cases will be due to this.
You could have transient errors on connection but this would be very unlikely... Still, a retry approach would be wise regardless. You can try this locally by trying to connect to the database by turning your wifi off and on... Ideally when you can't connect you should retry a couple of times with a few seconds interval. Something similar to this should so the trick:
async function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
let connPooPromise = null;
let retries = 0;
while(retries<10){
connPoolPromise = await CreatePool();
if (connPoolPromise!==null) break;
retries++;
await sleep(2000);
}
return connPoolPromise;
I've been getting this error intermittently with tedious:
Fri Aug 14 2015 05:29:16 GMT+0000 (Coordinated Universal Time): Unaught exception: ConnectionError: Connection lost - read ECONNRESET at Connection.socketError (D:\home\site\wwwroot\node_modules\tedious\lib\connection.js:797:26) at Socket. (D:\home\site\wwwroot\node_modules\tedious\lib\connection.js:33:15)
at Socket.emit (events.js:117:20)
at net.js:441:14
at process._tickCallback (node.js:442:13)
The error stalls the entire node server and renders it unresponsive for around 10-15 minutes. Afterwards, the server recovers by itself automatically.
I'm on the latest tedious 1.12.2 and using it with the tedious-connection-pool module 0.3.8 (although the issue is present prior to tedious 1.12.2 and has been happening in all the versions for the past few months)
What is interesting is that it seems like the issue happens much more frequently these days than before.
I'm not sure if the above error message is helpful or not but if it's not, please inform me what other debug or log information I can get you good folks. I've been testing out tedious and besides this particular showstopper issue for me, everything looks great.
As for servers, I'm using node 32bit v0.10.36 (although I've tested this with the latest node 12 and the issue is present there too) connecting to Azure SQL (v12 DB)