2

My Meteor app runs slowly in the beginning for about ten seconds, and then becomes fast again. I am trying to improve the performance but having troubles to find the real cause.

I thought the problem was that I am publishing all the course information like following:

if (Meteor.isServer) {
  Meteor.publish("courses", function() {
    return Courses.find();
  });
}

I tried using Kadira to monitor exactly what's happening. However, looking at the result, I am starting to think maybe it's not the real problem.

enter image description here

If it only takes 292ms for pubsub response time, it shouldn't feel that laggy but I cannot think of any other reason why the app would be so slow in the beginning and become fast again. Can an expert point me to the redirection?

UPDATE:

I could improve the duration of lagginess in the beginning by making the following changes:

in /server/publications.js

if (Meteor.isServer) {
  Meteor.publish("courses", function() {
    // since we only need these two fields for the search bar's autocomplete feature
    return Courses.find({}, {fields: {'catalog':1, 'titleLong':1}});
  });

  Meteor.publish("courseCatalog", function(catalog) {
    // publish specific information only when needed
    return Courses.find({"catalog": catalog});
  });
}

and in router.js I made changes accordingly so I subscribe based on specific pages. But there's still some lag in the beginning and I wonder if I can make more optimizations, and what is the real cause of the slowness in the beginning.

UPDATE2:

I followed the suggestion and made changes like below:

Session.set('coursesReady', false); on startup.

and in router:

Router.route('/', function () {
  Meteor.subscribe("courses", function(err) {
    if (!err) {
      console.log("course data is ready")
      Session.set('coursesReady', true);
    }
  });
  ....

and in /lib/helpers.js which returns data for typeahead library

if (Meteor.isClient) {
  Template.registerHelper("course_data", function() {
    console.log("course_data helper is called");    
    if (Session.get('coursesReady')) {
      var courses = Courses.find().fetch(); 
      return [
        {
          name: 'course-info1',
          valueKey: 'titleLong',
          local: function() {
            return Courses.find().fetch();
          },
          template: 'Course'
        },

But now the problem is that when the helper function is called, the data is never ready. The console print:

enter image description here

Q: How do I ensure that the helper function is called only after the data is ready, OR called again when the data is ready? Since Session is reactive, shouldn't it be called again automatically?

Maximus S
  • 10,759
  • 19
  • 75
  • 154
  • Could you post some unminified code? I do not believe pub/sub latency is your issue, but it's very difficult to track it down without being able to see what's happening in your code. – sbking Apr 05 '15 at 17:46
  • @sbking Thank you. Here it is: https://github.com/woniesong92/coursescoop – Maximus S Apr 05 '15 at 17:56
  • @sbking I also made some update in the original question – Maximus S Apr 05 '15 at 18:01
  • In https://github.com/woniesong92/coursescoop/blob/master/client/views/global/helpers.js, I am calling `fetch()` three times. I believe this might be the problem. – Maximus S Apr 05 '15 at 18:13

1 Answers1

0

I can't check this right now, but I believe your issue might be that the course_data helper is being run multiple times before all 1000+ documents in the subscription are ready, causing the typeahead package to re-run some expensive calculations. Try something like this:

/client/views/global/helpers.js

Template.registerHelper("course_data", function() {
  if (!Session.get('coursesReady')) return [];      

  return [ //...

/client/subscriptions.js

Meteor.subscribe("courses", function(error) {
  if (!error) Session.set('coursesReady', true);
});

Update:

Really, Meteor's new features this.subscribe() and Template.instance().subscriptionsReady() are ideal for this. Session isn't really the right choice, but it should still be reactively updating (not sure why it isn't for you). Try instead making the following changes to /client/views/navwithsearch.js (and main, though ideally both templates should share a single search template):

Template.NavWithSearch.onCreated(function() {
  this.subscribe('courses');
});

Template.NavWithSearch.onRendered(function() {
  this.autorun(function() {
    if (Template.instance().subscriptionsReady()) {
      Meteor.typeahead.inject();
    }
  });
});

The idea is to tie the lifecycle of the subscription to the view that will actually be using that subscription. This should delay the typeahead injection until the subscription is completely ready.

sbking
  • 7,630
  • 24
  • 33
  • I also believe this is the issue. One thing that I don't understand is that by the time `if (!Session.get('coursesReady')) return [];` is called, what if Session value is still false? Shouldn't I setup some kind of callback? – Maximus S Apr 05 '15 at 19:04
  • I updated the question again with more details. Please look at update2 – Maximus S Apr 05 '15 at 19:11
  • @MaximusS Hmm, not sure why it isn't working. The typeahead package might not actually run your helper in an autorun, but that would mean my original guess at your performance issue could be wrong. Try using the new, template-instance-bound subscription features, as shown in my update. – sbking Apr 05 '15 at 19:53
  • 1
    I get `Error: Can't call View#autorun from a Tracker Computation; try calling it from the created or rendered callback {stack: (...), message: "Can't call View#autorun from a Tracker Computation… calling it from the created or rendered callback"}` – Maximus S Apr 05 '15 at 20:06
  • when you have time: http://stackoverflow.com/questions/29466051/how-to-debug-performance-issue-optimize-your-meteor-app – Maximus S Apr 06 '15 at 06:02