balderdashy / sails

Realtime MVC Framework for Node.js
https://sailsjs.com
MIT License
22.83k stars 1.95k forks source link

Web App template hangs after login using sequelize eager loading #5462

Open emrahu opened 5 years ago

emrahu commented 5 years ago

Node version: 11.4.0 Sails version (sails): 1.1.0 ORM hook version (sails-hook-orm): 2.1.1 Sockets hook version (sails-hook-sockets): 1.5.5 Organics hook version (sails-hook-organics): 0.15.0 Grunt hook version (sails-hook-grunt): 3.1.0 Uploads hook version (sails-hook-uploads): 0.4.3 DB adapter & version (mysql2): 1.6.5 Sequelize (sequelize):5.8.6


This doesn't seem like a sequelize or sequelize-hook issue as the same query I run on different page executed normally. The problem I face is this. I create a new sails app using Web App template. I install sequelize, then update my User model. In my /welcome page action/controller I run a sequelize query with eager loading. As soon as I hit the login button the app waits around 50 seconds then shows me the /welcome page. There are 5 rows in my table. If there are more records the wait time increases. The same query runs perfectly on the same controller if I use return res.json(queryResult) or use the same query in a different controller and use the result in a view.

Screen record of what I am experiencing https://streamable.com/3qdvm

Steps to reproduce 1. Create a new sails app choosing Wep App template 2. Install Sequelize dependencies

$ npm install sails-hook-sequelize
$ npm install sails-hook-sequelize-blueprints
$ npm install sequelize 
$ npm install mysql2 

3. Replace the content of .sailsrc with the following

{
  "hooks":{
    "blueprints": false,
    "orm":false,
    "pubsub":false
  }
}

4. Update datastores,js with your database credentials

default: {
user: '<your-user>',
    password: '<your-password>,
    database: '<database-name>',
    dialect: 'mysql',
    ssl: false,
    options: {
      query: {
        logging:console.log
      },
      dialect: 'mysql',
      host: 'localhost',
      port: 3306,
      logging:console.log
    }
}

5. Sequelize your User.js model

module.exports = {
  options: {
    tableName: 'user',
    classMethods: {},
    instanceMethods: {},
    hooks: {},
    scopes: {}
  },
  attributes: {
    emailAddress: {
      type: Sequelize.STRING,
      allowNull:false,
      unique:{args: true, msg:'Email address already in use.'},
      validate:{
        isEmail:true,
        notEmpty:true,
        min:4
      }

    },
    emailStatus: { type: Sequelize.STRING },
    emailChangeCandidate: { type: Sequelize.STRING },
    password: { type: Sequelize.STRING },
    firstName: { type: Sequelize.STRING },
    lastName: { type: Sequelize.STRING },
    isSuperAdmin: { type: Sequelize.BOOLEAN },
    passwordResetToken: { type: Sequelize.STRING },
    passwordResetTokenExpiresAt: { type: Sequelize.INTEGER },
    emailProofToken: { type: Sequelize.STRING },
    emailProofTokenExpiresAt: { type: Sequelize.INTEGER },
    stripeCustomerId: { type: Sequelize.STRING },
    hasBillingCard: { type: Sequelize.BOOLEAN },
    billingCardBrand: { type: Sequelize.STRING },
    billingCardLast4: { type: Sequelize.STRING },
    billingCardExpMonth: { type: Sequelize.STRING },
    billingCardExpYear: { type: Sequelize.STRING },
    tosAcceptedByIp: { type: Sequelize.STRING },
    lastSeenAt: { type: Sequelize.BIGINT },
  }
};

6. Update login.js userRecord

await User.findOne({where:{emailAddress: inputs.emailAddress.toLowerCase()}});

7. Create a model that has hasMany association with another model. Create another model that has belongsTo association with the previously created model. For example:

// Project.js model
module.exports = {
  options: {
    tableName: 'project',
    classMethods: {},
    instanceMethods: {},
    hooks: {}
  },
  associations: function () {
    Project.hasMany(Part, {
      as:'parts',
      foreignKey:{name:'project_id'}
    });
  },
  attributes: {
    name: { type: Sequelize.STRING(255), allowNull:false },
  },
};
// Part.js model
module.exports = {
  options: {
    tableName: 'part',
    classMethods: {},
    instanceMethods: {},
    hooks: {},

  },
  associations: function () {
    Part.belongsTo(Project,{
      foreignKey:{name:'project_id'}
    });
  },
  attributes: {
    number: { type: Sequelize.STRING, allowNull:false },
  },
};

In your /welcome action/controller using an eager loading will cause your app to hang right after the login.

var projects = Project.findAll({ include:[ {model:Part, as:'parts'} ] });

This is not the case if you use an Empty sails app.

I noticed in my Chrome console the /welcome page is Pending for TTFB. Socket.io also pending

sailsbot commented 5 years ago

@emrahu Thanks for posting! We'll take a look as soon as possible.

In the mean time, there are a few ways you can help speed things along:

Please remember: never post in a public forum if you believe you've found a genuine security vulnerability. Instead, disclose it responsibly.

For help with questions about Sails, click here.

emrahu commented 5 years ago

Not entirely sure if the labeling of this issue is correct. I could be wrong but this doesn't seem like a 3rd party package problem.

mikermcneil commented 5 years ago

Hi @emrahu, I've never tried those plugins (e.g. sails-hook-sequelize), but my guess is that the hook probably doesn't work the same way. It's been several years since I've played with Sequelize (I think I switched us off of it prior to Sails 0.8 circa early 2013), but I know a lot of its usage is different than Waterline.

In this case, I think I'd recommend using the built-in ORM

emrahu commented 5 years ago

Hi @mikermcneil, I don't think it's the plugins, the same query that gives me problem works fine on other pages. The login query works fine, I can see it in the sails console. The problem only exists on /welcome page. In the screen record you can see that my front end javascript files are also pending for few seconds after I login and see the Welcome page.

rachaelshaw commented 5 years ago

@emrahu would you try reproducing this issue in a new app using the default ORM (to verify that it's unrelated to sails-hook-sequelize), then creating a repo that we can use to reproduce the issue? (I'm not experiencing this with a freshly-generated Sails app, so in order to diagnose this, it would be helpful to see what else you have in your action that displays the /welcome page.)

emrahu commented 5 years ago

@rachaelshaw I didn't have any problem with the default ORM until I needed to pull data from my database using nested associations. As far as I know Waterline is not able to do deep populate/nested associations.

rachaelshaw commented 5 years ago

@emrahu I'm not using Sequelize, so unfortunately I can't really help with questions about this plugin, but I'll leave this issue open in case anyone else using Sequelize on their project has any wisdom they can offer.

For posterity: I just double-checked and this is not an issue when you're using the default ORM.