不要怂,就是干,撸起袖子干!

Added check to prevent multiple commit/rollback on a single transaction

when not using CLS #4491.
Added rollback to prepareEnvironment to abort half-initialized
transactions that run into error, fixes connection pool exhaust bug #4491.
1 parent 2bfa312f
......@@ -2,6 +2,7 @@
- [ADDED] beforeRestore/afterRestore hooks [#4371](https://github.com/sequelize/sequelize/issues/4371)
- [INTERNALS] `options` has been renamed to `$options` in instance.js [#4429](https://github.com/sequelize/sequelize/pull/4429)
- [FIXED] Reload doesn't synchronize a null include [#4353](https://github.com/sequelize/sequelize/issues/4353)
- [FIXED] commit/rollback multiple times on same transaction [#4491](https://github.com/sequelize/sequelize/issues/4491)
# 3.8.0
- [ADDED] `version` on `Sequelize` returning the current npm/package.json version [#4459](https://github.com/sequelize/sequelize/pull/4459)
......
......@@ -127,6 +127,10 @@ Transaction.LOCK = Transaction.prototype.LOCK = {
Transaction.prototype.commit = function() {
var self = this;
if (this.finished) {
throw new Error('Transaction cannot be committed because it has been finished with state: ' + self.finished);
}
this.$clearCls();
return this
......@@ -150,6 +154,10 @@ Transaction.prototype.commit = function() {
Transaction.prototype.rollback = function() {
var self = this;
if (this.finished) {
throw new Error('Transaction cannot be rolled back because it has been finished with state: ' + self.finished);
}
this.$clearCls();
return this
......@@ -180,6 +188,10 @@ Transaction.prototype.prepareEnvironment = function() {
return self.setIsolationLevel();
}).then(function () {
return self.setAutocommit();
}).catch(function (setupErr) {
return self.rollback().finally(function () {
throw setupErr;
});
}).tap(function () {
if (self.sequelize.constructor.cls) {
self.sequelize.constructor.cls.set('transaction', self);
......
......@@ -134,6 +134,42 @@ describe(Support.getTestDialectTeaser('Transaction'), function() {
).to.eventually.be.rejected;
});
it('does not allow commits after commit', function () {
var self = this;
return expect(self.sequelize.transaction().then(function (t) {
return t.commit().then(function () {
return t.commit();
});
})).to.be.rejectedWith('Error: Transaction cannot be committed because it has been finished with state: commit');
});
it('does not allow commits after rollback', function () {
var self = this;
return expect(self.sequelize.transaction().then(function (t) {
return t.rollback().then(function () {
return t.commit();
});
})).to.be.rejectedWith('Error: Transaction cannot be committed because it has been finished with state: rollback');
});
it('does not allow rollbacks after commit', function () {
var self = this;
return expect(self.sequelize.transaction().then(function (t) {
return t.commit().then(function () {
return t.rollback();
});
})).to.be.rejectedWith('Error: Transaction cannot be rolled back because it has been finished with state: commit');
});
it('does not allow rollbacks after rollback', function () {
var self = this;
return expect(self.sequelize.transaction().then(function (t) {
return t.rollback().then(function () {
return t.rollback();
});
})).to.be.rejectedWith('Error: Transaction cannot be rolled back because it has been finished with state: rollback');
});
if (dialect === 'sqlite'){
it('provides persistent transactions', function () {
var sequelize = new Support.Sequelize('database', 'username', 'password', {dialect: 'sqlite'})
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!