Skip to content
Browse files

0.1.0_test

now on test
  • Loading branch information...
1 parent 93ce6c0 commit 4bc5262111c6f44e55e969b33afd1eac41953ece @HBYoon committed Sep 11, 2013
Showing with 497 additions and 795 deletions.
  1. +74 −99 README.md
  2. +15 −10 index.js
  3. +2 −3 lib/chain.js
  4. +0 −89 lib/dynamicConnection.js
  5. +0 −132 lib/query.js
  6. +86 −45 lib/queue.js
  7. +89 −61 lib/queueConnection.js
  8. +74 −0 test/chain_loop.js
  9. +5 −9 test/chain_test.js
  10. +0 −132 test/query_test.js
  11. +15 −12 test/set_loop.js
  12. +88 −60 test/set_test.js
  13. +49 −143 test/test.js
View
173 README.md
@@ -3,7 +3,7 @@ node-mysql-transaction
#### transaction wrapper for mysql driver
based on node-mysql: https://github.com/felixge/node-mysql
-node-mysql-transaction is run by single callback function queue with dynamic multi connection structure.
+node-mysql-transaction is run by single callback function queue with dynamic connection pool structure.
Install
---
@@ -28,18 +28,28 @@ var trCon = transaction({
...
}],
- // number of static parallel connection
- // you can chose it 0, if you want to use only dynamic connection.
- staticConnection:3,
+ // create temporary connection for increased volume of async work.
+ // if request queue became empty,
+ // start soft removing process of the connection.
+ // recommended for normal usage.
+ dynamicConnection: 32,
- // when queue length is more than 0,
- // make temporary connection for increased volume of async work.
- dynamicConnection:3,
+ // set dynamicConnection soft removing time.
+ idleConnectionCutoffTime: 600,
- // auto time out rollback in ms
+ // auto timeout rollback time in ms
// turn off is 0
- timeOut:600
+ timeout:600
});
+
+// listener of bubbled exception from connections.
+trCon.on('error', function(err){
+ // internal error handling was completed
+ // use for logging or end method caller
+ ...
+ trCon.end();
+});
+
```
@@ -121,7 +131,7 @@ on('result', function(result){
query('insert ...').
on('result',function(result){
chain.
- query('insert ...',[result.insertId]).
+ query('insert ...',[...]).
query('insert ...').
query('insert ...').
query('insert ...')
@@ -193,116 +203,79 @@ for(var i = 0; i < 10; i+=1) {
}
```
-###Terminating
-
-Call end method. Method sending error to all callback function in the queue and connection terminating after current transaction finished.
-
-```
-trCon.end()
-```
-
-###transaction query
-
-query is not recommended method. The query method can be removal in a next version.
-
-.query style transaction can usable. But, in some case, the method is slower than chain. And you will make a lot of indent.
-
+###transaction set
+Transaction chain is the application layer of the transaction set. You can use this set method for transaction, also. But connection set doesn't have any transaction helper, like transaction chain. So, you must to check error for every query request. And you must to select rollback or commit for each transaction capsule.
```
-// With old style error handling
-// if you choose this way, auto rollback and timeout rollback is turn off
-trCon.query('insert ...',[...],function(err,result){
- if (err) {
- return result.rollback();
- }
- trCon.query('insert ...',[...],function(err,otherResult){
+trCon.set(function(err, safeCon){
+ safeCon.on('commit', function(){
+ console.log('79 / 71 commit, after several event loop');
+ });
+ safeCon.on('rollback', function(err){
+ console.log(err);
+ });
+ safeCon.query('insert ......',[......],function(err,result){
if (err) {
- return otherResult.rollback();
+ safeCon.rollback();
}
- trCon.query('insert ...',[...],function(err,theOtherResult){
+ safeCon.query('insert ......',[......],function(err,result){
if (err) {
- return theOtherResult.rollback();
+ safeCon.rollback(err);
}
- theOtherResult.commit(function(err){
- if (!err) {
- console.log('complete')
- }
- });
+ // .set transaction can work after several event loop.
+ // if you forget commit or rollback,
+ // and no timeout setting, connection will be leak.
+ safeCon.commit();
});
});
-})
+});
```
-You can choose rollback event for an error handling. In this way, you can turn on auto rollback and timeout rollback.
+Event provide better way for transaction in some case.
```
-// now auto rollback and timeout rollback is working.
-// you don't need any error handling in the middle of transaction
-// If you call the query method after call end method, error occurred to callback function
+test.set(function(err, safeCon){
+ safeCon.on('commit', function(){
+ console.log('23731 commit!');
+ }).
+ on('rollback', function(err){
+ console.log('23731 rollback');
+ console.log(err);
+ });
-trCon.query('insert ...',[...],function(err,result){
+ reqQuery1 = safeCon.query('insert ......', [...]);
+ reqQuery2 = safeCon.query('insert ......', [...]);
- trCon.query('insert ...',['err'],function(err,otherResult){
-
- trCon.query('insert ...',[...],function(err,theOtherResult){
-
- });
+ reqQuery1.
+ on('result', function(result){
+ console.log('23731: ' + result.insertId);
+ }).
+ on('error', function(err){
+ safeCon.rollback(err);
+ });
+
+ reqQuery2.
+ on('result', function(result){
+ safeCon.commit()
+ }).
+ on('error', function(err){
+ safeCon.rollback(err);
});
-}).
-on('rollback', function(err){
- // normal error linked to here after rollback occur
- console.log('trCon.query auto rollback');
});
```
-auto commit can turn off.
+###Terminating
+
+Call end method. Method sending error to all callback function in the queue and connection terminating after current transaction finished.
```
-// result.rollback === otherResult.rollback === theOtherResult.rollback;
-
-trCon.query('insert ...',[...],function(err,result){
- trCon.query('insert ...',[...],function(err,otherResult){
- trCon.query('insert ...',[...],function(err,theOtherResult){
- // auto commit off
- theOtherResult.autoCommit(false);
-
- setTimeout(function(){
- theOtherResult.commit();
-
- },0);
- });
- });
-}).
-on('commit', function(){
- console.log('manual commit');
-}).
-on('rollback',function(err){
- console.log(err);
-});
+trCon.end()
```
-Unlike chain method, .query method cannot linked to other event loop. even auto commit is off.
+###transaction query
+
+removed
+
-```
-trCon.query('insert ...',[...],function(err,result){
- trCon.query('insert ...',[...],function(err,theOtherResult){
- // auto commit off
- theOtherResult.autoCommit(false);
-
- setTimeout(function(){
- trCon.query('insert ...',[...],function(err,otherTransactionResult){
- // This is new transaction!!
- // Now, earlier 2 insert query just waiting timeout rollback.
- ...
- });
- },0);
- });
-}).
-on('commit', function(){
- console.log('manual commit');
-}).
-on('rollback',function(err){
- console.log(err);
-});
```
@@ -318,4 +291,6 @@ Update
0.0.3: default chain method setMaxListeners is 0. code and internal API update.
-0.0.31: query method update.
+0.0.31: query method update.
+
+0.1.0: redesigned internal connection pool and queue. pool and queue performance improved. more solid error handling. removed query method. bug fix. internal API changed, but minimum userland change.
View
25 index.js
@@ -1,23 +1,28 @@
+var events = require("events");
+
// MySQL/MariaDB transactions link
var queue = require('./lib/queue');
-var dynamicConnection = require('./lib/dynamicConnection');
var chain = require('./lib/chain');
-var query = require('./lib/query');
module.exports = function interfaceFun (opt) {
opt.connection = opt.connection;
opt.staticConnection = opt.connectionNumber || opt.staticConnection || 0;
- opt.dynamicConnection = opt.dynamicConnection || 4;
- opt.dynamicConnectionLoopTime = opt.dynamicConnectionLoopTime || 200;
- opt.timeOut = opt.timeOut || 0;
+ opt.dynamicConnection = opt.dynamicConnection || 8;
+ opt.idleConnectionCutoffTime = opt.dynamicConnectionLoopTime || opt.idleConnectionCutoffTime || 600;
+ opt.timeout = opt.timeOut || opt.timeout || 0;
return setup(opt);
};
function setup (opt) {
- var newQueue = queue(opt);
- newQueue.dynamicConnection = dynamicConnection(newQueue);
- newQueue.chain = chain(newQueue);
- newQueue.query = query(newQueue);
- return newQueue;
+ var transactionObj = new events.EventEmitter();
+
+ transactionObj.config = opt;
+ transactionObj.queue = queue(transactionObj);
+ transactionObj.chain = chain(transactionObj.queue);
+
+ transactionObj.set = transactionObj.queue.set.bind(transactionObj.queue);
+ transactionObj.end = transactionObj.queue.end.bind(transactionObj.queue);
+
+ return transactionObj;
};
View
5 lib/chain.js
@@ -58,7 +58,7 @@ var chainObj = {
newObject: function(firstRun){
var obj = new events.EventEmitter();
- obj.setMaxListeners(0);
+ obj.setMaxListeners(0);
obj._autoCommit = true;
obj.autoCommit = function(select){
obj._autoCommit = select;
@@ -71,7 +71,6 @@ var chainObj = {
}
};
-
function commitLoop (safeCon) {
if (!safeCon.usable()){
return;
@@ -82,7 +81,7 @@ function commitLoop (safeCon) {
return setImmediate(commitLoop.bind(null,safeCon));
};
-// rollback dose't need any safty timer
+// rollback dosen't need any safety timer
function rollbackFacroty (safeCon) {
return function(err){
safeCon._count = 0;
View
89 lib/dynamicConnection.js
@@ -1,89 +0,0 @@
-module.exports = dynamicConnection;
-
-function dynamicConnection(queueControl) {
- var dCon = Object.create(dynamicObj);
-
- dCon.dConNum = queueControl.config.dynamicConnection;
- dCon.loopTime = queueControl.config.dynamicConnectionLoopTime;
- dCon.on = false;
- dCon.dynamicCount = 0;
- dCon.lastQueueLength = 0;
-
- dCon.tool = {};
- dCon.tool.queueLength = queueControl.queueLength.bind(queueControl);
- dCon.tool.usableConnnection = queueControl.usableConnnection.bind(queueControl);
- dCon.tool.increaseConnection = queueControl.increaseConnection.bind(queueControl);
- dCon.tool.decreaseConnection = queueControl.decreaseConnection.bind(queueControl);
-
- return dCon;
-};
-
-var dynamicObj = {
- increaseDynamicConnection: function(){
- if (this.dynamicCount >= this.dConNum) {
- return;
- }
- this.dynamicCount += 1;
- try {
- this.tool.increaseConnection();
- } catch(e) {
- console.error(e);
- }
- this.tool.usableConnnection().nextQuery();
- },
-
- decreaseDynamicConnection: function decreaseDynamicConnection(){
- if (0 >= this.dynamicCount) {
- return;
- }
- this.dynamicCount -= 1;
- try {
- this.tool.decreaseConnection();
- } catch(e) {
- console.error(e);
- }
- },
-
- loopOn: function(){
- if (this.on) {
- return;
- }
- this.on = true;
-
- var dynamicLoop;
- (dynamicLoop = function(){
- var length = this.tool.queueLength();
- var delta = length - this.lastQueueLength;
-
- if (length) {
- this.increaseDynamicConnection();
- } else {
- this.decreaseDynamicConnection();
- }
-
- if (length === 0 && delta === 0) {
- return this.loopOff();
- }
-
- this.lastQueueLength = length;
- if (this.on) {
- return setTimeout(dynamicLoop,this.loopTime);
- }
- }.bind(this))();
- },
-
- loopOff: function (){
- this.on = false;
-
- var endLoop;
- (endLoop = function () {
- if (!(this.dynamicCount > 0)) {
- return;
- }
- this.decreaseDynamicConnection();
- if (!this.on) {
- return setTimeout(endLoop,this.loopTime);
- }
- }.bind(this))();
- }
-};
View
132 lib/query.js
@@ -1,132 +0,0 @@
-var events = require("events");
-
-module.exports = quaryFactory
-function quaryFactory (queueControl) {
- var query = Object.create(queryObj);
-
- query.chainOn = false;
- query.stateQuery = query.stateUp();
-
- query.tool = {};
- query.tool.set = queueControl.set.bind(queueControl);
-
- return function(){
- return query.stateQuery.apply(query, arguments);
- };
-};
-
-var queryObj = {
- callbackQuery: function(safeCon, arg, position, eventObj) {
- var callback = arg[position];
-
- arg[position] = function (err, result, raw) {
- if (err) {
- if (eventObj.listeners('rollback').length) {
- return safeCon.rollback(err);
- }
- }
- // if is not a array result
- if (!Array.isArray(result)) {
- result = resultObjectSet(result, safeCon)
- }
- // state query function change
- // ready to link to the next transaction query
- this.stateQuery = this.stateUp(safeCon, eventObj);
-
- this.chainOn = false;
-
- callback(err, result, raw);
-
- // state query function rollback
- // ready to take new transaction queue
- this.stateQuery = this.stateUp();
-
- // auto commit
- if (!this.chainOn && result._autoCommit) {
- return safeCon.commit();
- }
- this.chainOn = false;
- }.bind(this);
- safeCon.query.apply(null, arg);
- return eventObj;
- },
-
- newCallbackQuery: function(arg, position) {
- var eventObj = new events.EventEmitter();
-
- // set point
- this.tool.set(function(err, safeCon){
- if (err) {
- if (eventObj.listeners('rollback').length) {
- return eventObj.emit('rollback', err);
- }
- return arg[position](err);
- }
-
- if (!eventObj.listeners('rollback').length) {
- safeCon.timeOut = 0;
- }
-
- safeCon.on('commit',function(){
- eventObj.emit('commit');
- });
- safeCon.on('rollback',function(err){
- eventObj.emit('rollback', err);
- });
-
- this.callbackQuery(safeCon, arg, position, eventObj);
- }.bind(this));
- return eventObj;
- },
-
- stateUp: function(currentConnection, eventObj){
- var readyState = !!(currentConnection);
- return function(){
- this.chainOn = readyState;
- for (var position in arguments) {
- if (typeof arguments[position] === 'function') {
- if (readyState) {
- return this.callbackQuery(currentConnection, arguments, position, eventObj);
- }
- return this.newCallbackQuery(arguments, position);
- }
- };
- if (readyState) {
- return skipCallbackQuery(currentConnection, arguments, eventObj)
- }
- return null;
- }
- },
-};
-
-function resultObjectSet (result, safeCon) {
- result = result || {};
- result.rollback = safeCon.rollback.bind(safeCon);
- result.commit = safeCon.commit.bind(safeCon);
- result._autoCommit = true;
- result.autoCommit = function(select){
- this._autoCommit = select;
- };
- return result;
-};
-
-function skipCallbackQuery (safeCon, arg, eventObj) {
- if (!(eventObj.listeners('rollback').length) || !(eventObj.listeners('commit').length)) {
- throw new Error('skip callback query must need rollback and commit event listener');
- }
-
- var query;
- try {
- query = safeCon.query.apply(null, arg);
- } catch(e) {
- return safeCon.rollback(e);
- }
-
- query.
- on('error', function(err){
- safeCon.rollback(err);
- }).
- on('end', function(result){
- safeCon.commit();
- });
-};
View
131 lib/queue.js
@@ -2,55 +2,96 @@ var queueConnection = require("./queueConnection");
module.exports = queue
-function queue (opt){
+function queue (mainObj){
var queue = Object.create(queueObj);
- queue.queue = [];
- queue.connections = [];
- queue.config = opt;
-
- for (var i = 0; i < opt.staticConnection; i += 1) {
- queue.increaseConnection();
- };
+ queue.mainObj = mainObj;
+ queue.config = mainObj.config;
+ queue.connections = [];
+ queue.queue = [];
+ queue.idleConnections = [];
+ queue.cutoffTimer = null;
+
return queue;
};
var queueObj = {
set: function (setPoint){
- this.queue.unshift(setPoint);
+ this.queue.push(setPoint);
var usableCon = this.usableConnnection();
if (usableCon) {
- usableCon.nextQuery();
+ return usableCon.nextQuery();
}
- if (this.config.dynamicConnection) {
- this.dynamicConnection.loopOn();
+ if (this.connections.length >= (this.config.staticConnection + this.config.dynamicConnection)) {
+ return
}
+ this.createConnection()
},
usableConnnection: function(){
- var length = this.connections.length;
- while(length){
- length -= 1;
- if (!this.connections[length].lock){
- return this.connections[length];
+ if (this.idleConnections.length) {
+ if (this.idleConnections.length === 1) {
+ clearTimeout(this.cutoffTimer);
+ this.cutoffTimer = null;
}
- };
- return;
+ return this.idleConnections.pop();
+ }
+ return null;
},
-
- decreaseConnection: function(){
- var last = this.connections.pop();
- if (last) {
- return last.connectionCut();
+
+ idleIn : function (connection) {
+ this.idleConnections.push(connection);
+ this.cutoffSet();
+ },
+
+ cutoffSet: function(){
+ if (this.cutoffTimer) {
+ return;
+ }
+ if (this.idleConnections.length > this.config.staticConnection) {
+ this.cutoffTimer = setTimeout(this.cutoffRun.bind(this), this.config.idleConnectionCutoffTime)
+ }
+ },
+
+ cutoffRun: function(){
+ this.cutoffTimer = null;
+ if (!this.idleConnections[0]) {
+ return;
+ }
+ var cutNow = this.idleConnections.shift();
+ this.removeConnection(cutNow);
+ this.cutoffSet();
+ },
+
+ removeConnection: function(connection){
+ connection.connectionCut();
+ var len = this.connections.length
+ while (len > 0) {
+ len -= 1;
+ if (this.connections[len] === connection) {
+ this.connections.splice(len, 1);
+ break;
+ }
}
},
- increaseConnection: function(){
- this.connections.push(
- queueConnection(
- connectionFactory(this.config),
- this.config,
- this.queue));
+ createConnection: function(){
+ var newCon = queueConnection(this);
+ newCon.nextQuery();
+ this.connections.push(newCon);
+ },
+
+ connectionError: function(connection, err){
+ this.mainObj.emit('error', err);
+ this.removeConnection(connection);
+ if (this.queue.length >0) {
+ console.log('connection recreate');
+ this.createConnection();
+ }
+ },
+
+ connectionFactory: function(){
+ return this.config.connection[0](this.config.connection[1]);
},
queueLength: function(){
@@ -59,33 +100,33 @@ var queueObj = {
end: function(){
this.set = dummySet;
- this.queueCleaner;
- if (this.dynamicConnection) {
- this.dynamicConnection.loopOff();
- }
- for (var i = 0; i < this.config.staticConnection; i += 1) {
- this.decreaseConnection();
+ this.queueCleaner(this.queue);
+ this.queue = [];
+ var len = this.connections.length;
+ while (len > 0) {
+ len -= 1;
+ this.removeConnection(this.connections[len]);
};
},
- queueCleaner: function(){
- var err = new Error('transaction queue cleaner work')
- var inLoop;
- (inLoop = function () {
- var setPoint = this.queue.pop();
+ queueCleaner: function(queue){
+ queue = queue || this.queue;
+
+ (function inLoop() {
+ var setPoint = queue.pop();
if (!setPoint) {
return;
}
- setPoint(err);
+ setPoint(new Error('transaction queue cleaner work'));
setImmediate(inLoop);
}.bind(this));
},
};
-function connectionFactory (opt) {
+// function connectionFactory (queueObj) {
// return mysql.createConnection(options)
- return opt.connection[0](opt.connection[1]);
-};
+ // return queueObj.config.connection[0](queueObj.config.connection[1]);
+// };
function dummySet () {
var err = new Error('transaction connection closed');
View
150 lib/queueConnection.js
@@ -1,28 +1,32 @@
var events = require("events");
module.exports = queueConnection;
-function queueConnection (connection, config, queue){
- var con = Object.create(queueObj);
+function queueConnection (queueObj) {
+ var con = Object.create(queueConnectionObj);
- con.lock = false;
- con.workCut = false;
- con.timeOut = config.timeOut;
- con.connection = connection;
-
- con._queue = queue;
+ con.queueObj = queueObj;
+ con.lock = false;
+ con.workCut = false;
+ con.timeout = queueObj.config.timeout;
+ con.connection = queueObj.connectionFactory();
+ con.errorBubble = queueObj.connectionError.bind(queueObj, con);
+ con.queryMethod = Object.getPrototypeOf(con.connection).query;
+
+ con._queue = queueObj.queue;
con._currentTransaction = null;
- con.tool = {};
- con.tool.query = Object.getPrototypeOf(con.connection).query;
- con.tool.connectionQuery = Object.getPrototypeOf(con.connection).query.bind(con.connection);
+
+
+ con.connection.on('error', con.errorBubble);
+
return con;
};
-var queueObj = {
+var queueConnectionObj = {
safeConnectionFactory: function(target){
var safeConnection = new events.EventEmitter();
- safeConnection.timeOut = this.timeOut;
+ safeConnection.timeout = this.timeout;
safeConnection.timer = null;
safeConnection.target = target;
@@ -32,56 +36,48 @@ var queueObj = {
safeConnection.rollback = safeConMethod.rollback.bind(safeConnection);
safeConnection.tool = {};
- safeConnection.tool.query = this.tool.query;
+ safeConnection.tool.next = this._next.bind(this);
+ safeConnection.tool.selectError = this.selectError.bind(this);
+ safeConnection.tool.query = this.queryMethod
safeConnection.tool.connection = this.connection;
- safeConnection.tool.commitReq = this.commitReq.bind(this);
- safeConnection.tool.rollbackReq = this.rollbackReq.bind(this);
safeConnection.tool.nullCurrentState = safeConMethod.nullCurrentState.bind(null, this);
return safeConnection;
},
_next: function(){
- if (!this._queue.length || this.workCut) {
+ if (this._queue.length === 0 || this.workCut) {
this.lock = false;
+ if (!this.workCut) {
+ this.queueObj.idleIn(this);
+ }
return;
}
+
this.lock = true;
- this._currentTransaction = this._queue.pop();
+ this._currentTransaction = this._queue.shift();
- var safeConnection = this.safeConnectionFactory(this._currentTransaction);
- this.tool.connectionQuery('START TRANSACTION', function(err){
- if (!this._currentTransaction) {
- return;
- }
- if (err) {
- this.nextQuery();
- return this._currentTransaction(err);
- }
- this._currentTransaction(undefined, safeConnection);
- }.bind(this));
- },
+ if (typeof this._currentTransaction !== 'function') {
+ return this._next();
+ }
- // internal commit and rollback request query sender
- commitReq: function (callback){
- this.tool.connectionQuery('COMMIT', function(err, result){
- if(callback){
- callback(err);
- }
- });
- this._next();
+ this.connection.query('START TRANSACTION', this._nextCallback.bind(this));
},
-
- rollbackReq: function(reason, callback){
- this.tool.connectionQuery('ROLLBACK', function(err, result){
- if (callback) {
- if (err) {
- callback(err);
- }
- callback(reason);
- }
- });
- this._next();
+
+ _nextCallback: function(err){
+ var safeConnection = this.safeConnectionFactory(this._currentTransaction);
+
+ if (err) {
+ this._next();
+ return this._currentTransaction(err);
+ }
+ this._currentTransaction(undefined, safeConnection);
+ },
+
+ selectError: function(err, safeCon){
+ this.lock = false;
+ this.errorBubble(err);
+ safeCon.emit('rollback', err);
},
nextQuery: function(){
@@ -92,6 +88,9 @@ var queueObj = {
},
connectionCut: function(){
+ if (this.workCut) {
+ return;
+ }
this.workCut = true;
var endLoop
(endLoop = function(){
@@ -110,7 +109,7 @@ var safeConMethod = {
// if current transaction on, new timer set and, query start.
var err;
if (!this.usable()) {
- err = new Error('out of transaction');
+ err = new Error('Query request error, Transaction was closed');
// if request is not a current transaction, query try send error to callback and throw error
for (var i in arguments) {
if (typeof arguments[i] === 'function') {
@@ -120,40 +119,69 @@ var safeConMethod = {
throw err;
}
- if (this.timeOut) {
+ if (this.timeout) {
if (this.timer) {
clearTimeout(this.timer);
}
this.timer = setTimeout(function(){
this.rollback(new Error('transaction request time over'));
- }.bind(this),this.timeOut);
+ }.bind(this),this.timeout);
}
return this.tool.query.apply(this.tool.connection, arguments);
},
commit: function(callback){
if (!this.usable()) {
+ if (callback) {
+ callback(new Error('Commit request error. Transaction was closed.'));
+ }
return;
}
- this.emit('commit');
- this.removeAllListeners();
+
this.tool.nullCurrentState();
- return this.tool.commitReq(callback);
+
+ var self = this;
+ this.tool.connection.query('COMMIT', function(err){
+ if (callback) {
+ callback(err);
+ }
+
+ if (err) {
+ return self.tool.selectError(err, self);
+ }
+ self.emit('commit');
+ self.tool.next();
+
+ });
},
rollback: function(reason, callback){
- if (!this.usable()) {
- return;
- }
if (typeof reason === 'function') {
callback = reason;
reason = undefined;
}
- this.emit('rollback', reason);
- this.removeAllListeners();
+ if (!this.usable()) {
+ if (callback) {
+ callback(new Error('Rollback request error. Transaction was closed.'));
+ }
+ return;
+ }
+
this.tool.nullCurrentState();
- return this.tool.rollbackReq(reason, callback);
+
+ var self = this;
+ this.tool.connection.query('ROLLBACK', function(err){
+ if (callback) {
+ callback(err || reason);
+ }
+
+ if (err) {
+ return self.tool.selectError(err, self);
+ }
+ self.emit('rollback', reason);
+ self.tool.next()
+ });
},
usable: function(obj){
View
74 test/chain_loop.js
@@ -0,0 +1,74 @@
+// transaction test
+// tested with mysql 2.0 alpha
+
+// test table query -> CREATE TABLE `transaction_test` (`num` INT(10) NULL AUTO_INCREMENT, `test` INT(10) NULL DEFAULT '0',PRIMARY KEY (`num`))COLLATE='latin1_swedish_ci'ENGINE=InnoDB;
+
+var mysql = require('mysql');
+
+var transaction = require('../index.js');
+var test = transaction({
+ connection: [mysql.createConnection,{
+ user: 'test',
+ password: 'test',
+ database: 'test'
+ }],
+ staticConnection:0,
+
+ dynamicConnection:64,
+ idleConnectionCutoffTime: 100,
+ // auto time out rollback in ms
+ timeOut:600
+});
+
+// don't use this test for compare of set_loop test.
+// this test has 3 times insert query in each round.
+
+// chain transaction API test
+console.time('test');
+var number = 0;
+var to = 10000;
+
+var numberTo = 0;
+
+for (var i = 0; i < to; i+=1) {
+ (function(){
+ var num = number+=1;
+
+ var chain = test.chain();
+
+ chain.
+ on('commit', function(){
+ if (num === to) {
+ console.timeEnd('test');
+ }
+ }).
+ on('rollback', function(err){
+ if (num === to) {
+ console.timeEnd('test');
+ }
+ });
+
+
+ chain.
+ // query chaining is important and most heavy process in a chain method.
+ query('insert transaction_test set test=?',[num]).
+ query('insert transaction_test set test=?',[num]).
+ query('insert transaction_test set test=?',[num]).
+ on('result', function(result){
+ if (num%2) {
+ // I don't want any odd number!
+ chain.rollback();
+ }
+ });
+ })();
+}
+
+// 0.1.0
+// mariaDB 5.5.x intel i5 cpu
+// time per dynamicConnection
+// 64 -> 72xx ~ 73xx ms
+// 32 -> 74xx ~ 76xx ms
+// 16 -> 88xx ~ 89xx ms
+// 8 -> 119xx ~ 120xx ms
+
+// */
View
14 test/chain_test.js
@@ -27,8 +27,6 @@ var test = transaction({
// /* //<<<<<<<<<<<<<block
// chain transaction API test
-
-
var chain = test.chain();
chain.
@@ -63,8 +61,6 @@ on('result', function(result){
chain.commit();
}).
autoCommit(false);
-
- // finally auto commit run
// each chain set it's own auto commit function if you did not set auto commit off
// so must need auto commit off for make chain stream to after event loop
@@ -108,7 +104,7 @@ query('insert transaction_test set test=?',[16]).
query('insert transaction_test set test=?',[17]).
query('insert transaction_test set test=?',[18]).
query('insert transaction_test set test=?',[19]).
-query('insert transaction_test set test=?',[20])
+query('insert transaction_test set test=?',[20]);
// transaction chain with loop!!!
var chain4 = test.chain();
@@ -121,23 +117,23 @@ on('rollback', function(err){
}).
setMaxListeners(0);
-for(var i = 0; i < 10; i+=1) {
+for(var i = 0; i < 5; i+=1) {
// working good :)
chain4.query('insert transaction_test set test=?',[i*100]);
}
var chain5 = test.chain();
chain5.
on('commit', function(){
- console.log('chain4 commit');
+ console.log('chain5 commit');
}).
on('rollback', function(err){
- console.log('chain4 rollback');
+ console.log('chain5 rollback');
console.log(err);
}).
setMaxListeners(0);
-for(var i = 0; i < 10; i+=1) {
+for(var i = 0; i < 30; i+=1) {
if (i===8) { i='error maker' }
chain5.query('insert transaction_test set test=?',[i*10000]);
}
View
132 test/query_test.js
@@ -1,132 +0,0 @@
-// transaction test
-// tested with mysql 2.0 alpha
-
-// test table query -> CREATE TABLE `transaction_test` (`num` INT(10) NULL AUTO_INCREMENT, `test` INT(10) NULL DEFAULT '0',PRIMARY KEY (`num`))COLLATE='latin1_swedish_ci'ENGINE=InnoDB;
-
-var mysql = require('mysql');
-
-var transaction = require('../index.js');
-var test = transaction({
- connection: [mysql.createConnection,{
- user: 'test',
- password: 'test',
- database: 'test'
- }],
- // parallel connection queue number
- staticConnection:0,
-
- // when queue length increase or queue length is longer than connectionNumber * 32,
- // make temporary connection for increased volume of async work.
- dynamicConnection:6,
-
- // auto time out rollback in ms
- timeOut:600
-});
-
-
-// /* //<<<<<<<<<<<<<block
-
-// connection.query function test
-// it also transaction, but range of chance to make transaction link is
-// <<< only limited in a query's callback function in a same event loop >>>
-// conclusion -> use connection.chain transaction
-
-
-test.query('insert transaction_test set test=?',[199],function(err,result){
- result.rollback();
-});
-
-test.query('insert transaction_test set test=?',[133],function(err,result){
- //auto commit
-});
-
-// you can make connect.query transaction chain in callback function
-test.query('insert transaction_test set test=?',[1000],function(err,result){
- // formal error handling way -> this is bad idea, because you cannot take any information about timeout rollback event
- if (err) {
- result.rollback();// result.rollback === otherResult.rollback;
- }
- test.query('insert transaction_test set test=?',['err'],function(err,otherResult){
- // again...
- if (err) {
- // oops! there is no return...
- otherResult.rollback();
- }
- test.query('insert transaction_test set test=?',[998],function(err,theOtherResult){
- // and again...
- if (err) {
- console.log(err) // occur out of transaction error
- theOtherResult.rollback();// result.rollback === otherResult.rollback === theOtherResult.rollback;
- }
- });
- });
-});
-
-test.query('insert transaction_test set test=?',[2000],function(err,result){
-
- test.query('insert transaction_test set test=?',['err'],function(err,otherResult){
-
- console.log('because of error, you cannot see this message on console');
-
- test.query('insert transaction_test set test=?',[1998],function(err,theOtherResult){
- // now auto rollback is working
- // if you setup 'rollback' listener, auto rollback also ready to working
- // you don't need any error handling in the middle of transaction
- });
- });
-}).
-on('rollback', function(err){
- // error to here
- console.log('test.query auto rollback');
-});
-
-test.query('insert transaction_test set test=?',[3000],function(err,result){
- test.query('insert transaction_test set test=?',[2999],function(err,otherResult){
- test.query('insert transaction_test set test=?',[2998],function(err,theOtherResult){
- // auto commit off
- theOtherResult.autoCommit(false);
-
- // time out rollback test
- setTimeout(function(){
- theOtherResult.commit();// result.rollback === otherResult.rollback;
- },1000);
- });
- });
-}).
-on('commit', function(){
- console.log('time out test commit??');
-}).
-on('rollback',function(err){
- console.log(err);
-});
-
-test.query('insert transaction_test set test=?',[4000],function(err,result){
- test.query('insert transaction_test set test=?',[3999],function(err,otherResult){
- // you can skip the final callback function if you setup commit and rollback event listeners
- // if you are not make event listeners, function throw error
- test.query('insert transaction_test set test=?',[3998]);
- });
-}).
-on('commit', function(){
- console.log('nice auto commit');
-}).
-on('rollback',function(err){
- console.log(err);
-});
-
-
-var testQuery = test.query('insert transaction_test set test=?',[5000],function(err,result){
- test.query('insert transaction_test set test=?',[4999],function(err,otherResult){
- test.query('insert transaction_test set test=?',[4998]);
- });
-})
-
-testQuery.
-on('commit', function(){
- console.log('nice auto commit 2');
-}).
-on('rollback',function(err){
- console.log(err);
-});
-
-// */
View
27 test/set_loop.js
@@ -12,14 +12,12 @@ var test = transaction({
password: 'test',
database: 'test'
}],
- // parallel connection queue number
- connectionNumber:4,
+ connectionNumber:0,
- // when queue length increase or queue length is longer than connectionNumber * 32,
- // make temporary connection for increased volume of async work.
- dynamicConnection:2,
-
- // auto time out rollback in ms
+ dynamicConnection:32,
+
+ idleConnectionCutoffTime: 100,
+
timeOut:600
});
@@ -38,11 +36,6 @@ for (var i = 0; i < to; i+=1) {
// I don't want any odd number!
safeCon.commit();
-
- // query can not work after transaction end(commit or rollback)
- // query('insert transaction_test set test=?',[num],function(err,result){
- // console.log(err);//[Error: out of transaction]
- // });
}
// when after commit...oops!
@@ -54,13 +47,23 @@ for (var i = 0; i < to; i+=1) {
// time per connectionNumber in 10000 loop in my pc with mariaDB 5.5
// better speed == more cpu usage
+ // 0.0.31
// 32 -> 112xx ~ 114xx ms
// 16 -> 113xx ~ 114xx ms -> full single cpu usage
// 8 -> 127xx ~ 128xx ms
// 6 -> 137xx ~ 140xx ms
// 4 -> 164xx ~ 165xx ms
// 2 -> 272xx ~ 276xx ms
// 1 -> 395xx ~ 403xx ms
+
+ // time per dynamicConnection
+ // improved in 0.1.0 ;-)
+ // 0.1.0
+ // 64 -> 31xx ~ 32xx ms
+ // 32 -> 39xx ~ 38xx ms -> full single cpu usage
+ // 16 -> 53xx ~ 54xx ms
+ // 6 -> 101xx ~ 109xx ms
+ // 4 -> 148xx ~ 153xx ms
}
});
});
View
148 test/set_test.js
@@ -21,101 +21,125 @@ var test = transaction({
// auto time out rollback in ms
timeOut:600
+}).
+on('error', function(err){
+ console.log('on connection error');
+ console.log(err);
});
-
// /* //<<<<<<<<<<<<<block
// connection.set
// connection.set is the base API for queue reservation
-// connection.chain/.query is wrapper of .set
-// simply connection.set doesn't have any sugar
+// connection.chain is wrapper of .set
+// simply connection.set doesn't have any transaction helper
+// in real world, you must to check error for every query request.
+// and you must to select rollback or commit for each transaction capsule.
// commit after event loop
test.set(function(err, safeCon){
- safeCon.on('commit', function(){
- console.log('79 / 71 commit, after several event loop');
- });
- safeCon.on('rollback', function(err){
- console.log(err);
- });
- safeCon.query('insert transaction_test set test=?',[79],function(err,result){
- if (err) {
- safeCon.rollback(err);
- }
- safeCon.query('insert transaction_test set test=?',[71],function(err,result){
- if (err) {
- safeCon.rollback(err);
- }
- setTimeout(function(){
- setTimeout(function(){
- // .set transaction can work after several event loop.
- safeCon.commit();
- },0);
- },0);
- });
- });
+ safeCon.on('commit', function(){
+ console.log('79 / 71 commit, after several event loop');
+ });
+ safeCon.on('rollback', function(err){
+ console.log(err);
+ });
+ safeCon.query('insert transaction_test set test=?',[79],function(err,result){
+ if (err) {
+ safeCon.rollback();
+ }
+ safeCon.query('insert transaction_test set test=?',[71],function(err,result){
+ if (err) {
+ safeCon.rollback(err);
+ }
+ setTimeout(function(){
+ setTimeout(function(){
+ // .set transaction can work after several event loop.
+ safeCon.commit();
+ // if you forget commit or rollback, connection will be leak.
+ },0);
+ },0);
+ });
+ });
});
-// Streaming query
+// /*
+
+// event query
test.set(function(err, safeCon){
- safeCon.on('commit', function(){
+ safeCon.
+ on('commit', function(){
console.log('commit!');
- });
- safeCon.on('rollback', function(err){
+ }).
+ on('rollback', function(err){
console.log(err);
});
reqQuery1 = safeCon.query('insert transaction_test set test=23');
reqQuery2 = safeCon.query('insert transaction_test set test=11');
- reqQuery1.on('result', function(result){
+ reqQuery1.
+ on('result', function(result){
// console.log(result);
});
- reqQuery2.on('result', function(result){
+
+ reqQuery2.
+ on('result', function(result){
safeCon.rollback('23 / 11 rollback yeap!')
});
});
test.set(function(err, safeCon){
- safeCon.on('commit', function(){
+ safeCon.
+ on('commit', function(){
console.log('23371 commit!');
- });
- safeCon.on('rollback', function(err){
+ }).
+ on('rollback', function(err){
console.log(err);
});
+
reqQuery1 = safeCon.query('insert transaction_test set test=23371');
reqQuery2 = safeCon.query('insert transaction_test set test=23371');
- reqQuery1.on('result', function(result){
+ reqQuery1.
+ on('result', function(result){
console.log('23371: ' + result.insertId);
});
- reqQuery2.on('result', function(result){
+ reqQuery2.
+ on('result', function(result){
safeCon.commit()
});
});
+// error handling with event query
test.set(function(err, safeCon){
- safeCon.on('commit', function(){
- console.log('23731 commit!');
- });
- safeCon.on('rollback', function(err){
- console.log('23731 rollback');
- console.log(err);
- });
-
- // input type error value
- reqQuery1 = safeCon.query('insert transaction_test set test=23731');
- reqQuery2 = safeCon.query('insert transaction_test set test=?',['errrrrr']);
-
- reqQuery1.on('result', function(result){
- console.log('23731: ' + result.insertId);
- });
- reqQuery2.on('result', function(result){
- safeCon.commit()
- }).on('error', function(err){
- safeCon.rollback(err);
- });
+ safeCon.on('commit', function(){
+ console.log('23731 commit!');
+ }).
+ on('rollback', function(err){
+ console.log('23731 rollback');
+ console.log(err);
+ });
+
+ // input type error value
+ reqQuery1 = safeCon.query('insert transaction_test set test=23731');
+ reqQuery2 = safeCon.query('insert transaction_test set test=?',['errrrrr']);
+
+ reqQuery1.
+ on('result', function(result){
+ console.log('23731: ' + result.insertId);
+ }).
+ on('error', function(err){
+ safeCon.rollback(err);
+ });
+
+ reqQuery2.
+ on('result', function(result){
+ safeCon.commit()
+ }).
+ on('error', function(err){
+ safeCon.rollback(err);
+ });
});
test.set(function(err, safeCon){
@@ -129,14 +153,18 @@ test.set(function(err, safeCon){
reqQuery1 = safeCon.query('insert transaction_test set test=?',[37301]);
reqQuery2 = safeCon.query('insert transaction_test set test=?',[37301]);
- reqQuery1.on('result', function(result){
+ reqQuery1.
+ on('result', function(result){
console.log('37301: ' + result.insertId);
});
- // time out rollback
- reqQuery2.on('result', function(result){
+
+ // time out rollback
+ reqQuery2.
+ on('result', function(result){
setTimeout(function(){
safeCon.commit()
},1000);
});
});
-//*/
+
+// */
View
192 test/test.js
@@ -13,17 +13,23 @@ var test = transaction({
database: 'test'
}],
// static parallel connection queue number
- staticConnection:2,
+ staticConnection:10,
// when queue length increase or queue length is longer than connectionNumber * 32,
// make temporary connection for increased volume of async work.
- dynamicConnection:6,
+ dynamicConnection:1,
// auto time out rollback in ms
timeOut:600
});
+setTimeout(function(){
+ console.log('end to end');
+ test.end();
+},10000)
+
/* //<<<<<<<<<<<<<block
+
// simple loop test
setTimeout(function(){
var number = 0;
@@ -38,11 +44,6 @@ setTimeout(function(){
// I don't want any odd number!
safeCon.commit();
-
- // query can not work after transaction end(commit or rollback)
- // query('insert transaction_test set test=?',[num],function(err,result){
- // console.log(err);//[Error: out of transaction]
- // });
}
// when after commit...oops!
@@ -57,7 +58,7 @@ setTimeout(function(){
},1000);
//*/
-// /* //<<<<<<<<<<<<<block
+/* //<<<<<<<<<<<<<block
// chain transaction API test
var chain = test.chain();
@@ -94,8 +95,6 @@ on('result', function(result){
chain.commit();
}).
autoCommit(false);
-
- // finally auto commit run
// each chain set it's own auto commit function if you did not set auto commit off
// so must need auto commit off for make chain stream to after event loop
@@ -173,127 +172,17 @@ for(var i = 0; i < 30; i+=1) {
chain5.query('insert transaction_test set test=?',[i*10000]);
}
-
-// */
-
-// /* //<<<<<<<<<<<<<block
-
-// connection.query function test
-// it also transaction, but range of chance to make transaction link is
-// <<< only limited in a query's callback function in a same sync >>>
-// conclusion -> use connection.chain transaction
-
-
-test.query('insert transaction_test set test=?',[199],function(err,result){
- result.rollback();
-});
-
-test.query('insert transaction_test set test=?',[133],function(err,result){
- //auto commit
-});
-
-// you can make connect.query transaction chain in callback function
-test.query('insert transaction_test set test=?',[1000],function(err,result){
- // formal error handling way -> this is bad idea, because you cannot take any information about timeout rollback event
- if (err) {
- result.rollback();// result.rollback === otherResult.rollback;
- }
- test.query('insert transaction_test set test=?',['err'],function(err,otherResult){
- // again...
- if (err) {
- // oops! there is no return...
- otherResult.rollback();
- }
- test.query('insert transaction_test set test=?',[998],function(err,theOtherResult){
- // and again...
- if (err) {
- console.log(err) // occur out of transaction error
- theOtherResult.rollback();// result.rollback === otherResult.rollback === theOtherResult.rollback;
- }
- });
- });
-});
-
-test.query('insert transaction_test set test=?',[2000],function(err,result){
-
- test.query('insert transaction_test set test=?',['err'],function(err,otherResult){
-
- console.log('because of error, you cannot see this message on console');
-
- test.query('insert transaction_test set test=?',[1998],function(err,theOtherResult){
- // now auto rollback is working
- // if you setup 'rollback' listener, auto rollback also ready to working
- // you don't need any error handling in the middle of transaction
- });
- });
-}).
-on('rollback', function(err){
- // error to here
- console.log('test.query auto rollback');
-});
-
-test.query('insert transaction_test set test=?',[3000],function(err,result){
- test.query('insert transaction_test set test=?',[2999],function(err,otherResult){
- test.query('insert transaction_test set test=?',[2998],function(err,theOtherResult){
- // auto commit off
- theOtherResult.autoCommit(false);
-
- // time out rollback test
- setTimeout(function(){
- theOtherResult.commit(function(err){
- if(!err){
- console.log('time out rollback off');
- }
- });// result.rollback === otherResult.rollback;
- },1000);
- });
- });
-})//.
-// on('commit', function(){
- // console.log('time out test commit??');
-// }).
-// on('rollback',function(err){
- // console.log(err);
-// });
-
-test.query('insert transaction_test set test=?',[4000],function(err,result){
- test.query('insert transaction_test set test=?',[3999],function(err,otherResult){
- // you can skip the final callback function if you setup commit and rollback event listeners
- // if you are not make event listeners, function throw error
- test.query('insert transaction_test set test=?',[3998]);
- });
-}).
-on('commit', function(){
- console.log('nice auto commit');
-}).
-on('rollback',function(err){
- console.log(err);
-});
-
-
-var testQuery = test.query('insert transaction_test set test=?',[5000],function(err,result){
- test.query('insert transaction_test set test=?',[4999],function(err,otherResult){
- test.query('insert transaction_test set test=?',[4998]);
- });
-})
-
-testQuery.
-on('commit', function(){
- console.log('nice auto commit 2');
-}).
-on('rollback',function(err){
- console.log(err);
-});
-
// */
// /* //<<<<<<<<<<<<<block
// connection.set
// connection.set is the base API for queue reservation
-// connection.chain/.query is wrapper of .set
-// simply connection.set doesn't have any sugar
+// connection.chain is wrapper of .set
+// simply connection.set doesn't have any transaction helper
+// in real world, you must to check error for every query request.
+// and you must to select rollback or commit for each transaction capsule.
// commit after event loop
test.set(function(err, safeCon){
@@ -315,55 +204,65 @@ test.set(function(err, safeCon){
setTimeout(function(){
// .set transaction can work after several event loop.
safeCon.commit();
+ // if you forget commit or rollback work with only set, connection will be leak.
},0);
},0);
});
});
});
-// Streaming query
+// event query
test.set(function(err, safeCon){
- safeCon.on('commit', function(){
+ safeCon.
+ on('commit', function(){
console.log('commit!');
- });
- safeCon.on('rollback', function(err){
+ }).
+ on('rollback', function(err){
console.log(err);
});
reqQuery1 = safeCon.query('insert transaction_test set test=23');
reqQuery2 = safeCon.query('insert transaction_test set test=11');
- reqQuery1.on('result', function(result){
+ reqQuery1.
+ on('result', function(result){
// console.log(result);
});
- reqQuery2.on('result', function(result){
+
+ reqQuery2.
+ on('result', function(result){
safeCon.rollback('23 / 11 rollback yeap!')
});
});
test.set(function(err, safeCon){
- safeCon.on('commit', function(){
+ safeCon.
+ on('commit', function(){
console.log('23371 commit!');
- });
- safeCon.on('rollback', function(err){
+ }).
+ on('rollback', function(err){
console.log(err);
});
+
reqQuery1 = safeCon.query('insert transaction_test set test=23371');
reqQuery2 = safeCon.query('insert transaction_test set test=23371');
- reqQuery1.on('result', function(result){
+ reqQuery1.
+ on('result', function(result){
console.log('23371: ' + result.insertId);
});
- reqQuery2.on('result', function(result){
+ reqQuery2.
+ on('result', function(result){
safeCon.commit()
});
});
+// error handling with event query
test.set(function(err, safeCon){
safeCon.on('commit', function(){
console.log('23731 commit!');
- });
- safeCon.on('rollback', function(err){
+ }).
+ on('rollback', function(err){
console.log('23731 rollback');
console.log(err);
});
@@ -372,12 +271,16 @@ test.set(function(err, safeCon){
reqQuery1 = safeCon.query('insert transaction_test set test=23731');
reqQuery2 = safeCon.query('insert transaction_test set test=?',['errrrrr']);
- reqQuery1.on('result', function(result){
+ reqQuery1.
+ on('result', function(result){
console.log('23731: ' + result.insertId);
});
- reqQuery2.on('result', function(result){
+
+ reqQuery2.
+ on('result', function(result){
safeCon.commit()
- }).on('error', function(err){
+ }).
+ on('error', function(err){
safeCon.rollback(err);
});
});
@@ -393,11 +296,14 @@ test.set(function(err, safeCon){
reqQuery1 = safeCon.query('insert transaction_test set test=?',[37301]);
reqQuery2 = safeCon.query('insert transaction_test set test=?',[37301]);
- reqQuery1.on('result', function(result){
+ reqQuery1.
+ on('result', function(result){
console.log('37301: ' + result.insertId);
});
- // time out rollback
- reqQuery2.on('result', function(result){
+
+ // time out rollback
+ reqQuery2.
+ on('result', function(result){
setTimeout(function(){
safeCon.commit()
},1000);

0 comments on commit 4bc5262

Please sign in to comment.
Something went wrong with that request. Please try again.