Performance: Meteor.user() is not free


#1

This is just FYI, so others don’t make the same mistake.

We use this or similar all over in our code:

		var user = Meteor.user();
		confirmUser(user);

Or

		var user = Meteor.user();
		if (!user) return;

Sometimes we actually care about the fields of the user, sometimes we don’t. Many times we end up calling Meteor.user() multiple times in a single call stack. (never more than 1 per frame)

When looking at Kadira/APM perf logs we learned that these calls are not only not free… but often times quite expensive. The .fetch() is anywhere from a few ms to 100’s of ms.

The suggestion is to use Meteor.userId() instead… and to pass the user doc if that makes sense.

This is a really “extreme” sample:

find on users 0ms
coll : users
func : find
selector : “RvxLpCgrjXsLhzGS2"
fetch on users 423ms
coll : users
selector : {”_id":“RvxLpCgrjXsLhzGS2”}
func : fetch
cursor : true
limit : 1
docsFetched : 1
docSize : 1193
find on decks 0ms
fetch on decks 608ms
find on cards 0ms
count on cards 204ms
find on users 0ms
fetch on users 1184ms
coll : users
selector : {"_id":“RvxLpCgrjXsLhzGS2”}
func : fetch
cursor : true
limit : 1
docsFetched : 1
docSize : 1193
find on deckLogs 0ms SHOW MORE
fetch on deckLogs 14ms SHOW MORE
find on users 0ms
coll : users
func : find
selector : “RvxLpCgrjXsLhzGS2"
fetch on users 57ms
coll : users
selector : {”_id":“RvxLpCgrjXsLhzGS2”}
func : fetch
cursor : true
limit : 1
docsFetched : 1
docSize : 1193


#3

I should have been more clear. We are tuning server performance. Thanks.


#4

@oemig I got burnt pretty badly by this behaviour - for a long while I thought Meteor.user() was doing something smart, eg caching the user for subsequent calls, until profiling proved otherwise.

From looking at the code, it’s just a thin wrapper around Meteor.users.findOne(this.userId) without any field filtering going on - ie almost never the right tool for the job in production environments. It would have really helped to see this indicated in the docs somewhere.


#5

Make a pull request in Meteor guide"? :smiley:


#6

Hmm, even though I don’t use Meteor.user() “accidentally”, I still need to retrieve the user document on every method call to check permissions, so it’d definitely be good to have a caching solution for it.


#7

Tracker.autorun(() => {
Session.set(‘user’, Meteor.user())
})

Please no :frowning:

And yes, he was talking about the server.

We end up doing a lot of this:

const user = Meteor.user();

user.xxx
user.yyy

Or Meteor.users.findOne(this.userId, {fields: {name: 1}}) to only get the needed fields for even more optimisation.


#8

I don’t know what so terrible about my suggestion, if it really was the case that Meteor.user() was slow on the client. Of course it’s pointless now that I realized he was talking about serverside, and yeah Meteor.user() doesn’t have much of a cost on the client since Minimongo is fast.


#9

Because it’s ugly, hacky code using Session which also isn’t recommended.


#10

It’s the easiest to understand for an example. Would you have been happier if I used a ReactiveVar or ReactiveDict instead? You know Session is just a ReactiveDict right?