Skip to content

Conversation

@zbarbuto
Copy link
Contributor

@zbarbuto zbarbuto commented Jun 26, 2017

Description

I was unable to reproduce #258 exactly (when I hear back I will look into this further) but I did find issues when transaction operations where called when Transaction is called from the model.

When called directly on the model, commit and rollback do not go through transaction.js in loopback-connector so what the Postgresql connector is the Transaction rather than the connection. This PR checks for that case.

Related issues

connect to #258

Checklist

  • New tests added or existing tests modified to cover all changes
  • Code conforms with the style
    guide

@slnode
Copy link

slnode commented Jun 26, 2017

Can one of the admins verify this patch?

2 similar comments
@slnode
Copy link

slnode commented Jun 26, 2017

Can one of the admins verify this patch?

@slnode
Copy link

slnode commented Jun 26, 2017

Can one of the admins verify this patch?

@zbarbuto
Copy link
Contributor Author

@raymondfeng @jannyHou @kjdelisle @dhmlau

If anyone can add a test reproducing the original issue in #258 I'll investigate further.

@dhmlau
Copy link
Member

dhmlau commented Jun 27, 2017

@zbarbuto, thanks for your fix.
I can reproduce the problem by creating the following js in boot folder:
(Customer model with name property)

'use strict';

module.exports = function(app) {
  var customer = app.models.Customer;
  customer.beginTransaction('READ COMMITTED', (err, tx)=>{
    customer.updateAll({name: 'aaa'}, {name: 'bbb'}, {transaction: tx}, (err)=>{
      console.log('err = ', err);
    });
  });
};

I can try out your fix in a few hours.

@dhmlau
Copy link
Member

dhmlau commented Jun 27, 2017

@zbarbuto while I believe your changes might fix some other problem, I don't think it's the cause of #258. Error was thrown before commit function. Please see my comments in #258.

@jannyHou
Copy link
Contributor

jannyHou commented Jun 27, 2017

Thank you @zbarbuto I will review it asap, and some investigation @dhmlau did yesterday shows the problem is:

In postgresql connector's beginTransaction function, the transaction instance is created from the one in:
loopback-connector-postgresql/node_modules/loopback-connector,
see var tx = new Transaction(connector, connection);

While when the postgresql's beginTransaction function calls back, it goes to begin function in:
loopback-datasource-juggler/node_modules/loopback-connector,

if (!(connection instanceof Transaction)) {
      tx = new Transaction(connector, connection);
}

the code above is get executed since the connection is an instanceof the first lb-connector, NOT instanceof the second lb-connector, and when instantiate Transaction again, the txId is removed, and therefore the connector considers the transaction is not active.
see code

@zbarbuto
Copy link
Contributor Author

@dhmlau Thanks for the steps to reproduce. Can see it happen myself now and am investigating.

@zbarbuto
Copy link
Contributor Author

zbarbuto commented Jun 27, 2017

@jannyHou You're correct. This is caused when the version of loopback-connector required by the app is different to that required by loopback-connector-postgresql resulting in the connector's instanceof returning false. Frustrating. The real fix would probably be to ensure that all components are using the same version (e.g. for the postgres connector require loopback-connector as a peer dep or avoid requiring loopback-connector at the app level).

However, I've fixed it to avoid using Transaction to track transaction state and instead use an object on the connector. It's a bit of a dirty fix but it means that the issue of #230 is still handled but allows differing versions of loopback-connector.

Not sure the best way to add a test to avoid the regression in the future. Perhaps someone else could advise?

@dhmlau
Copy link
Member

dhmlau commented Jun 28, 2017

@zbarbuto @jannyHou , the cause of the problem is because there are different definition of Transaction which causes this line (https://github.com/strongloop/loopback-connector/blob/master/lib/transaction.js#L88) to fail. Would it make sense to change the if-statement from
if (!(connection instanceof Transaction))
to
if (connection.txId === undefined)

cc @raymondfeng

@dhmlau dhmlau added the apex label Jun 28, 2017
@zbarbuto
Copy link
Contributor Author

@dhmlau connection.txId is only added by this connector (was part of #230). Changing would probably break other sql connectors (such as MySQL)

The definition of Transaction is the file required in can be different because the version is different (loopback-connector-postgresql requires the one in its own ./node_modules folder if the version is different from the one in the parent package's ../node_modules which is why instanceof isn't working).

@dhmlau dhmlau requested a review from jannyHou June 28, 2017 13:25
@dhmlau
Copy link
Member

dhmlau commented Jun 28, 2017

@jannyHou @zbarbuto , I've verified the code with and without transaction.
@jannyHou , PTAL.


before(function(done) {
Post.create(p1Content, function(err, p1) {
Post.create(p1Content, function(err, p2) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think here it should be p2Content?

@raymondfeng
Copy link
Contributor

To avoid the failure of instanceof Transaction, we probably should use duck-typing by testing the properties such as if (typeof transaction.txId === 'string').

I'm not sure we need to use activeTransactions to keep track of the valid transactions. If we have to do so, we can set connector to Transaction object and test if tx.connector === this.

@zbarbuto
Copy link
Contributor Author

@raymondfeng But loopback-connector is what checks instanceof Transaction, not loopback-connector-postgresql so it does not know about txId - its unique to this connector (see #268 (comment)). The only reason txId was added to this connector was to avoid executing CRUD operations on a connection that originally had a transaction but has since been committed (#230).

Moving to loopback-connector as a peer-dependency is probably the 'cleaner' solution as it doesn't require any code changes but is potentially more breaking.

@dhmlau
Copy link
Member

dhmlau commented Jun 30, 2017

It looks like only Transaction has connector. How about we check:
if (connection.connector === undefined)
in this line https://github.com/strongloop/loopback-connector/blob/master/lib/transaction.js#L88

@raymondfeng
Copy link
Contributor

@dhmlau We can probably fix the code at https://github.com/strongloop/loopback-connector/blob/master/lib/transaction.js#L88 as follows:

    var tx = connection;
    if (tx.connector !== this) {
      // The connector returns a connection instead of transaction
      tx = new Transaction(connector, connection);
    } else {
      tx = new Transaction(tx.connector, tx.connection);
    }

@dhmlau Can you verify?

@dhmlau
Copy link
Member

dhmlau commented Jun 30, 2017

Verified. Works with and without transaction.
@zbarbuto , are you ok with making the change suggested by @raymondfeng?

@kjdelisle kjdelisle removed the apex label Jul 5, 2017
@dhmlau
Copy link
Member

dhmlau commented Jul 5, 2017

@zbarbuto, do you have a chance to review the suggested changes? Thanks!

@dhmlau
Copy link
Member

dhmlau commented Jul 5, 2017

Apologize that there has been some environment issue, therefore, the changes were not got reflected when I was doing the testing. I will have to do the verification again.
If only changes in loopback-connector is needed, I'll create a separate PR. Thanks!

@zbarbuto
Copy link
Contributor Author

zbarbuto commented Jul 5, 2017

@dhmlau I'll PR the suggested change to loopback-connector shortly.

@zbarbuto
Copy link
Contributor Author

zbarbuto commented Jul 5, 2017

@dhmlau Update: I just tested with @raymondfeng 's suggested change (with the above changes removed) and tests start failing after the bulk transactions (database deadlocks).

https://github.com/strongloop/loopback-connector-postgresql/blob/master/test/postgresql.transaction.test.js#L62

@dhmlau
Copy link
Member

dhmlau commented Jul 6, 2017

right. there's some problem with the suggested change. @jannyHou proposed something else, let me try it out and let you know.

@dhmlau
Copy link
Member

dhmlau commented Jul 6, 2017

@zbarbuto , I put @jannyHou 's proposed fix in loopbackio/loopback-connector#107. However, currently there's some issue with the downstream loopback-connector-dashdb CI. We're working on that.

@dhmlau
Copy link
Member

dhmlau commented Jul 14, 2017

@zbarbuto, we've implemented a fix in loopback-connector which seems to be a better fix than patching it on the postgreql repo, and also include the test cases in there. loopbackio/loopback-connector#109

I think the tests that you've added in postgresql.transaction.test.js would be useful. If you agree, could you please remove the changes in lib*.js and incorporate the review comments in the test.js? Thanks a lot!

@zbarbuto zbarbuto force-pushed the hotfix/258_transactions branch from a620839 to b4dd9b2 Compare July 16, 2017 22:40
@zbarbuto
Copy link
Contributor Author

@dhmlau Updated tests, removed active transaction tracking and rebased off latest master.

PostgreSQL.prototype.commit = function(connection, cb) {
debug('Commit a transaction');
var self = this;
if (connection instanceof Transaction) {
Copy link
Member

Choose a reason for hiding this comment

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

I've tested our changes in loopback-connector and it works without any changes in this repo.
Any reason we need to make this change?

Copy link
Member

Choose a reason for hiding this comment

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

Actually i think CI failed because of this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Missed this in the revert. Have removed now.

PostgreSQL.prototype.rollback = function(connection, cb) {
debug('Rollback a transaction');
var self = this;
if (connection instanceof Transaction) {
Copy link
Member

Choose a reason for hiding this comment

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

same as above.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

as above

PostgreSQL.prototype.commit = function(connection, cb) {
debug('Commit a transaction');
var self = this;
if (connection instanceof Transaction) {
Copy link
Member

Choose a reason for hiding this comment

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

Actually i think CI failed because of this.

@zbarbuto zbarbuto force-pushed the hotfix/258_transactions branch from 0881c5a to f81e206 Compare July 17, 2017 22:46
@zbarbuto
Copy link
Contributor Author

Rebased again.

@dhmlau
Copy link
Member

dhmlau commented Jul 18, 2017

@slnode test please

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

Choose a reason for hiding this comment

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

the last one.. i think this is needed.

@zbarbuto
Copy link
Contributor Author

Done

@dhmlau dhmlau merged commit 07129cd into loopbackio:master Jul 19, 2017
@dhmlau
Copy link
Member

dhmlau commented Jul 19, 2017

Thanks for going through all this with us! Your PR has landed!

@dhmlau dhmlau added this to the Sprint 40 - Apex milestone Jul 19, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants