Introducing a new approach to Meteor.user() - This simple trick can save you millions of database requests!


#1

Did you know that every time you run Meteor.user(), the function has to call the database in order to retrieve the user document?

For those of us that store things like roles data on the user document, it means that nearly every time we run a method, we have to make a database request.

For those of that do not, well… once you see this package, you might want to start :slight_smile:


#2

This is an interesting idea! I know we could do it in our app code if we use this package, but I think providing a way to configure it so that it monkey patches Meteor.user() to internally check MergeBox first would be helpful so that we don’t have to change the possibly hundreds of places we use it, and would make for a cleaner API. Something like

import UserCache from 'meteor/msavin:usercache':

UserCache.configure({
  automatic: true
});

#3

Sweet! Rock on and keep up the great work! Very relevant.


#5

I’m not big on monkey patching, but maybe this can be a good time for doing that.

What do you folks think about this kind of API:

Meteor.user()                              // works as usual
Meteor.user(true)                          // gets data from mergebox
Meteor.user(['profile.name', 'email'])     // verifies if the fields are in cache. If not, retrieves them from database

This shouldn’t break anything, and would provide a clear integration path.


#6

And, done. The new version now works like the API in the post above :slight_smile:


#7

Please make it compatible with user-extra package. :slight_smile:


#8

Or maybe you could integrate it into that package. :slight_smile: Maybe a third argument Meteor.user(userId, fields, cache) with cache set to true by default?


#9
const admin = Meteor.user() && Meteor.user().profile && Meteor.user().profile.role && Meteor.user().profile.role.admin;

use your package

const admin = Meteor.user('profile.role.admin')

but,The result was not correct, All user data is returned, not the value of the child field:喜悦:


#10

It doesn’t return the field - it simply verifies that its there in MergeBox. If not, it will query the database.

Would everyone prefer if it returned the fields?


#11

It would probably be a nice addition (it feels natural I believe)


#12

This looks great! How does the cache get updated? For example, if I have a user logged in that has an Admin role, and the role is removed - when will the cache be updated such that they no longer have any associated Admin privileges?


#13

The cache is constantly being updated by the oplog.

Here’s how Meteor’s real-time data works:

Oplog Tailing > Merge Box > Pub/Sub

All the data that is delivered via Pub/Sub is first processed in the MergeBox.

Thus, any change to your user document is available on the server even before it makes it to the client.


#14

Oh, so would it be compatible with redis-oplog package out of the box or are they supposed to clash each other?


#15

It should be fine. As I understand, redis-oplog (currently) only replaces the parts around oplog tailing, but everything from mergebox to pub/sub is Meteor’s code.

cc @diaconutheodor


#16

I had been using node-cache for this and invalidating based on updates, but this looks really nice!


#17

I think it’s fine to use with redis-oplog but are you absolutely sure it queries the database everytime I always thought it reads from the local collection?

You subscribe to the current user, it pushes it to the client. And that’s it. Also I’ve never seen multiple data coming down the pipe in websockets when Meteor.user() was called.

So I am curious to find out how you reached this conclusion ?


#18

Thanks for chiming in.

On the client, this.users.findOne(userId) will query minimongo (local cache). On the server, this.users.findOne(userId) would query the MongoDB collection. This package targets the server-side usecase.


If you look at this snippet, posted a few posts ago, it would literally query the database four times.

const admin = Meteor.user() && Meteor.user().profile && Meteor.user().profile.role && Meteor.user().profile.role.admin;

It could be optimized like this:

const userDoc = Meteor.user()
const admin = userDoc && userDoc.profile && userDoc.profile.role && userDoc.profile.role.admin;

or by using userCache, which would even bypass the first query that would be made for userDoc, and update it in real-time.


#19

Oh, I now completely understand. I never used Meteor.user() anywhere on the server it’s just too magical. But for those who use it I think that’s fine. I don’t see there how do you invalidate the cache ? I’m curious to understand your approach. What happens if the user’s role gets demoted, or he is suspended ?