Skip to content

Commit

Permalink
Retry strategy, designed for Sql Azure
Browse files Browse the repository at this point in the history
  • Loading branch information
Mathieu Cartoixa committed Mar 3, 2016
1 parent 83ef93e commit af6e04e
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 131 deletions.
306 changes: 176 additions & 130 deletions lib/connect-tedious.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

var tedious=require('tedious');
var tediousPool=require('tedious-connection-pool');
var retry=require('retry');
var ncsBuilder=require('node-connection-string-builder');
var debug={
connection: require('debug')('connect-tedious:connection'),
Expand Down Expand Up @@ -129,6 +130,12 @@ module.exports = function(connect){
this.sidColumnName=options.sidColumnName || '[Sid]';
this.sessColumnName=options.sessColumnName || '[Sess]';
this.expiresColumnName=options.expiresColumnName || '[Expires]';

this.retryOptions={
retries: 3,
minTimeout: 200,
maxTimeout: 1000
};

this.pool=new tediousPool({
name: 'connect-tedious',
Expand All @@ -143,7 +150,9 @@ module.exports = function(connect){
});

dbCleanup(this, function (err) {
if (err) console.log('ERROR ' + err);
if (err) {
console.log('ERROR ' + err);
}
});
}

Expand All @@ -163,42 +172,48 @@ module.exports = function(connect){

TediousStore.prototype.get = function(sid, fn) {
var self=this;
self.pool.acquire(function(err, db) {
if (err)
return fn(err);

var r = new tedious.Request(
'SELECT s.' + self.expiresColumnName + ', s.' + self.sessColumnName + ' FROM ' + self.tableName + ' s WHERE s.' + self.sidColumnName + '=@sid AND s.' + self.expiresColumnName + '>=SYSUTCDATETIME()',
function(err, rowCount) {
debug.sql('Executed SELECT');
db.release();
if (err)
return fn(err);
if (!rowCount || rowCount!==1)
return fn();
}
);
r.on('row', function(columns) {
if (!columns || columns.length!==2)
return fn();

var expires = columns[0].value;
var sess = columns[1].value;

if (!expires || !sess)
return fn();

var dExpires = new Date(expires).toISOString();
var oSess = JSON.parse(sess);
oSess.cookie.expires = dExpires;

debug.sql('Returning ' + oSess);
return fn(null, oSess);
var operation=retry.operation(self.retryOptions);
operation.attempt(function() {
self.pool.acquire(function(err, db) {
if (operation.retry(err) || err) {
return fn(err);
}

var r = new tedious.Request(
'SELECT s.' + self.expiresColumnName + ', s.' + self.sessColumnName + ' FROM ' + self.tableName + ' s WHERE s.' + self.sidColumnName + '=@sid AND s.' + self.expiresColumnName + '>=SYSUTCDATETIME()',
function(err, rowCount) {
debug.sql('Executed SELECT');
db.release();
if (operation.retry(err) || err) {
return fn(err);
}
if (!rowCount || rowCount!==1)
return fn();
}
);
r.on('row', function(columns) {
if (!columns || columns.length!==2)
return fn();

var expires = columns[0].value;
var sess = columns[1].value;

if (!expires || !sess)
return fn();

var dExpires = new Date(expires).toISOString();
var oSess = JSON.parse(sess);
oSess.cookie.expires = dExpires;

debug.sql('Returning ' + oSess);
return fn(null, oSess);
});
r.addParameter('sid', tedious.TYPES.VarChar, sid);

debugSql(r);
db.execSql(r);
});
r.addParameter('sid', tedious.TYPES.VarChar, sid);

debugSql(r);
db.execSql(r);
});
};

Expand All @@ -213,28 +228,36 @@ module.exports = function(connect){

TediousStore.prototype.set = function(sid, sess, fn){
var self=this;
self.pool.acquire(function(err, db) {
if (err)
return fn(err);

var duration = sess.cookie.maxAge || oneDay;
var r = new tedious.Request(
'MERGE INTO ' + self.tableName + ' WITH (HOLDLOCK) s' +
' USING (VALUES(@sid, @sess)) ns(' + self.sidColumnName + ', ' + self.sessColumnName + ') ON (s.' + self.sidColumnName + '=ns.' + self.sidColumnName + ')' +
' WHEN MATCHED THEN UPDATE SET s.' + self.sessColumnName + '=@sess, s.' + self.expiresColumnName + '=DATEADD(ms, @duration, SYSUTCDATETIME())' +
' WHEN NOT MATCHED THEN INSERT (' + self.sidColumnName + ', ' + self.sessColumnName + ', ' + self.expiresColumnName + ') VALUES (@sid, @sess, DATEADD(ms, @duration, SYSUTCDATETIME()));',
function(err) {
debug.sql('Executed MERGE');
db.release();
fn.apply(self, arguments);
}
);
r.addParameter('sid', tedious.TYPES.VarChar, sid);
r.addParameter('sess', tedious.TYPES.NVarChar, JSON.stringify(sess));
r.addParameter('duration', tedious.TYPES.Int, duration);

debugSql(r);
db.execSql(r);
var operation=retry.operation(self.retryOptions);
operation.attempt(function() {
self.pool.acquire(function(err, db) {
if (operation.retry(err) || err) {
return fn(err);
}

var duration = sess.cookie.maxAge || oneDay;
var r = new tedious.Request(
'MERGE INTO ' + self.tableName + ' WITH (HOLDLOCK) s' +
' USING (VALUES(@sid, @sess)) ns(' + self.sidColumnName + ', ' + self.sessColumnName + ') ON (s.' + self.sidColumnName + '=ns.' + self.sidColumnName + ')' +
' WHEN MATCHED THEN UPDATE SET s.' + self.sessColumnName + '=@sess, s.' + self.expiresColumnName + '=DATEADD(ms, @duration, SYSUTCDATETIME())' +
' WHEN NOT MATCHED THEN INSERT (' + self.sidColumnName + ', ' + self.sessColumnName + ', ' + self.expiresColumnName + ') VALUES (@sid, @sess, DATEADD(ms, @duration, SYSUTCDATETIME()));',
function(err) {
debug.sql('Executed MERGE');
db.release();
if (operation.retry(err) || err) {
return fn(err);
}
fn.apply(self, arguments);
}
);
r.addParameter('sid', tedious.TYPES.VarChar, sid);
r.addParameter('sess', tedious.TYPES.NVarChar, JSON.stringify(sess));
r.addParameter('duration', tedious.TYPES.Int, duration);

debugSql(r);
db.execSql(r);
});
});
};

Expand All @@ -247,24 +270,30 @@ module.exports = function(connect){

TediousStore.prototype.destroy = function(sid, fn){
var self=this;
self.pool.acquire(function(err, db) {
if (err)
return fn(err);

var r = new tedious.Request(
'DELETE s FROM ' + self.tableName + ' s WHERE s.' + self.sidColumnName + '=@sid',
function(err) {
debug.sql('Executed DELETE');
db.release();
if (err)
return fn(err);
return fn(err, true);
}
);
r.addParameter('sid', tedious.TYPES.VarChar, sid);

debugSql(r);
db.execSql(r);
var operation=retry.operation(self.retryOptions);
operation.attempt(function() {
self.pool.acquire(function(err, db) {
if (operation.retry(err) || err) {
return fn(err);
}

var r = new tedious.Request(
'DELETE s FROM ' + self.tableName + ' s WHERE s.' + self.sidColumnName + '=@sid',
function(err) {
debug.sql('Executed DELETE');
db.release();
if (operation.retry(err) || err) {
return fn(err);
}
return fn(null, true);
}
);
r.addParameter('sid', tedious.TYPES.VarChar, sid);

debugSql(r);
db.execSql(r);
});
});
};

Expand All @@ -277,30 +306,35 @@ module.exports = function(connect){

TediousStore.prototype.length = function(fn){
var self=this;
self.pool.acquire(function(err, db) {
if (err)
return fn(err);

var r = new tedious.Request(
'SELECT @count=COUNT(*) FROM ' + self.tableName,
function(err, rowCount) {
debug.sql('Executed SELECT');
db.release();
var operation=retry.operation(self.retryOptions);
operation.attempt(function() {
self.pool.acquire(function(err, db) {
if (err)
return fn(err);
if (!rowCount || rowCount!==1)
return fn();
}
);
r.on('returnValue', function(parameterName, value, metadata) {
if (!value)
return fn();
return fn(null, value);
return fn(err);

var r = new tedious.Request(
'SELECT @count=COUNT(*) FROM ' + self.tableName,
function(err, rowCount) {
debug.sql('Executed SELECT');
db.release();
if (operation.retry(err) || err) {
return fn(err);
}
if (!rowCount || rowCount!==1)
return fn();
}
);
r.on('returnValue', function(parameterName, value, metadata) {
if (!value)
return fn();
return fn(null, value);
});
r.addOutputParameter('count', tedious.TYPES.Int);

debugSql(r);
db.execSql(r);
});
r.addOutputParameter('count', tedious.TYPES.Int);

debugSql(r);
db.execSql(r);
});
};

Expand All @@ -314,23 +348,29 @@ module.exports = function(connect){

TediousStore.prototype.clear = function(fn){
var self=this;
self.pool.acquire(function(err, db) {
if (err)
return fn(err);

var r = new tedious.Request(
'TRUNCATE TABLE ' + self.tableName,
function(err) {
debug.sql('Executed TRUNCATE');
db.release();
if (err)
return fn(err);
fn(null, true);
}
);

debugSql(r);
db.execSql(r);
var operation=retry.operation(self.retryOptions);
operation.attempt(function() {
self.pool.acquire(function(err, db) {
if (operation.retry(err) || err) {
return fn(err);
}

var r = new tedious.Request(
'TRUNCATE TABLE ' + self.tableName,
function(err) {
debug.sql('Executed TRUNCATE');
db.release();
if (operation.retry(err) || err) {
return fn(err);
}
fn(null, true);
}
);

debugSql(r);
db.execSql(r);
});
});
};

Expand All @@ -345,27 +385,33 @@ module.exports = function(connect){
*/
TediousStore.prototype.touch = function (sid, sess, fn) {
var self = this;
self.pool.acquire(function (err, db) {
if (err)
return fn(err);

var duration = sess.cookie.maxAge || oneDay;

var r = new tedious.Request(
'UPDATE ' + self.tableName + ' SET ' + self.expiresColumnName + '=DATEADD(ms, @duration, SYSUTCDATETIME()) WHERE ' + self.sidColumnName + '=@sid',
function (err) {
debug.sql('Executed UPDATE');
db.release();
if (err)
return fn(err);
fn(null, true);
}
);
r.addParameter('duration', tedious.TYPES.Int, duration);
r.addParameter('sid', tedious.TYPES.VarChar, sid);

debugSql(r);
db.execSql(r);
var operation=retry.operation(self.retryOptions);
operation.attempt(function() {
self.pool.acquire(function (err, db) {
if (operation.retry(err) || err) {
return fn(err);
}

var duration = sess.cookie.maxAge || oneDay;

var r = new tedious.Request(
'UPDATE ' + self.tableName + ' SET ' + self.expiresColumnName + '=DATEADD(ms, @duration, SYSUTCDATETIME()) WHERE ' + self.sidColumnName + '=@sid',
function (err) {
debug.sql('Executed UPDATE');
db.release();
if (operation.retry(err) || err) {
return fn(err);
}
fn(null, true);
}
);
r.addParameter('duration', tedious.TYPES.Int, duration);
r.addParameter('sid', tedious.TYPES.VarChar, sid);

debugSql(r);
db.execSql(r);
});
});
};

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
{
"name": "connect-tedious",
"description": "Connect session store for SQL Server, using Tedious.",
"version": "1.0.0",
"version": "1.1.0",
"author": "Mathieu Cartoixa",
"main": "lib/connect-tedious",
"keywords": ["sql-server", "tds", "connection", "session", "store"],
"dependencies": {
"node-connection-string-builder": "^0.0.1",
"retry": "^0.9.0",
"tedious": "^1.13.2",
"tedious-connection-pool": "^0.3.9",
"debug": "^2.2.0"
Expand Down

0 comments on commit af6e04e

Please sign in to comment.