Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions lib/postgresql.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,17 @@ PostgreSQL.prototype.executeSQL = function(sql, params, options, callback) {
}

var transaction = options.transaction;
if (transaction && transaction.connection &&
transaction.connector === this) {
if (transaction && transaction.connector === this) {
if (!transaction.connection) {
return process.nextTick(function() {
callback(new Error(g.f('Connection does not exist')));
});
}
if (transaction.txId !== transaction.connection.txId) {
return process.nextTick(function() {
callback(new Error(g.f('Transaction is not active')));
});
}
debug('Execute SQL within a transaction');
// Do not release the connection
executeWithConnection(transaction.connection, null);
Expand Down
9 changes: 8 additions & 1 deletion lib/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

'use strict';
var debug = require('debug')('loopback:connector:postgresql:transaction');
var uuid = require('uuid');
var Transaction = require('loopback-connector').Transaction;

module.exports = mixinTransaction;

Expand All @@ -18,14 +20,18 @@ function mixinTransaction(PostgreSQL) {
* @param cb
*/
PostgreSQL.prototype.beginTransaction = function(isolationLevel, cb) {
var connector = this;
debug('Begin a transaction with isolation level: %s', isolationLevel);
this.pg.connect(function(err, connection, done) {
if (err) return cb(err);
connection.autorelease = done;
connection.query('BEGIN TRANSACTION ISOLATION LEVEL ' + isolationLevel,
function(err) {
if (err) return cb(err);
cb(null, connection);
var tx = new Transaction(connector, connection);
tx.txId = uuid.v1();
connection.txId = tx.txId;
cb(null, tx);
Copy link
Contributor

@jannyHou jannyHou Mar 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think here we should callback connection instead of tx?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The goal here is to make the new connection and transaction and say "this connection is currently working on this transaction" allowing for the error if you pass in a transaction with a connection id that doesn't match.

If you callback with connection, loop back connector will make a transaction out of it and pas it up the chain. This means you how no tie between the id of the connection and the current transaction.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zbarbuto after I fixed if(err) return done(err), now we get the real error:

 1) transactions bulk should work with bulk transactions: Error: read ECONNRESET at exports._errnoException (util.js:907:11) at TCP.onread (net.js:558:26)

I suspect it's because here we callback the tx, then when we do tx.commit to release it, but we don't close/release the connection in L25...
The old code callback connection so it doesn't have this error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is the case, as if you look at loopback-conenctor the commit method passes the connection of the transaction to the connector:

https://github.com/strongloop/loopback-connector/blob/master/lib/transaction.js#L48

And moreover, if you debug the code at line 76, you can see that the connection that is passed in to releaseConnection is definitely the connection that has autorelease() attached to it.

Calling back with transaction instead of the connection should make essentially no difference, as loopback-connector will immediately turn it into a Transaction if it is not already an instance of Transaction:

https://github.com/strongloop/loopback-connector/blob/master/lib/transaction.js#L88

I simply skip this step so we can attach the id to both the connection and the transaction.

});
});
};
Expand Down Expand Up @@ -65,6 +71,7 @@ function mixinTransaction(PostgreSQL) {

PostgreSQL.prototype.releaseConnection = function(connection, err) {
if (typeof connection.autorelease === 'function') {
connection.txId = null;
connection.autorelease(err);
connection.autorelease = null;
} else {
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"debug": "^2.1.1",
"loopback-connector": "^4.0.0",
"pg": "^6.0.0",
"strong-globalize": "^2.6.2"
"strong-globalize": "^2.6.2",
"uuid": "^3.0.1"
},
"devDependencies": {
"eslint": "^2.13.1",
Expand Down
29 changes: 27 additions & 2 deletions test/postgresql.transaction.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ describe('transactions', function() {
Post.create(post, {transaction: tx},
function(err, p) {
if (err) {
done(err);
return done(err);
} else {
tx.commit(function(err) {
if (err) {
done(err);
return done(err);
}
completed++;
checkResults();
Expand Down Expand Up @@ -124,4 +124,29 @@ describe('transactions', function() {

it('should not see the rolledback insert', expectToFindPosts(post, 0));
});

describe('finished', function() {
var post = {title: 't2', content: 'c2'};
beforeEach(createPostInTx(post));

it('should throw an error when creating in a committed transaction', function(done) {
currentTx.commit(function(err) {
if (err) return done(err);
Post.create({title: 't4', content: 'c4'}, {transaction: currentTx}, function(err, post) {
if (!err) return done(new Error('should throw error'));
done();
});
});
});

it('should throw an error when creating in a rolled back transaction', function(done) {
currentTx.rollback(function(err) {
if (err) return done(err);
Post.create({title: 't4', content: 'c4'}, {transaction: currentTx}, function(err, post) {
if (!err) return done(new Error('should throw error'));
done();
});
});
});
});
});