Using Fibers as Server Session Caching

Context:

Some years ago I decided to write a microservice in node with the fibers package, just for the sake of getting to know fibers better since the main application was built in Meteor. This experiment went very well, never had a single issue with fibers.

Today I’m writing code related with roles and permissions. In this new project there is the concept of a user belonging to an organisation. So, as you can imagine, for every method call, publication, etc. I’ve to check if the current user belongs to an organisation which implies a database query. I also need to provide the organisationId for every call because the user might switch the organisation.

Using Meteor.user() is an expensive operation, it will query the database. So I had an idea that seems pretty possible to me but there must be something internally in Meteor that is blocking me.

With Meteor every websocket client is associated with a fiber. You can access the Fiber very easily:

const Fibers = require( 'fibers' );

Meteor.methods({
    'showFiber'(){
        console.log( Fibers.current );
    }
});

You’ll find _meteor_dynamics on Fiber.current with the userId. When you do Meteor.user() on the server, this is how Meteor knows which user is using the current connection to the server.

Dream Goal;
If I could extend Fibers.current with my own namespace, I could cache a roles/permission object and avoid several database queries. This would have some impact on the server memory but with a small object, i wouldn’t mind.

Even better, I could write my own: Meteor.myCurrentUser() helper and use collection-helpers to access my roles/permission on every single Method and Subscription,

If I track every login and logout on the server, I can add data to the right Fiber and remove it.

Problem:

 Accounts.onLogin( ( info ) => {
    _.extend( Fibers.current , {
            __myapp_internals : {
                userId : info.user._id
            }
        }
    );

    console.log( 'Fibers.current : ' , Fibers.current );

} );

I can see my context being assigned but when I call the method showFiber, my context is gone. I’m pretty sure this is the same Fiber since I calling the method in Chrome with a user logged in. Kadira is able to add it’s own namespace called __kadiraInfo but I’m not.

What am I missing here?
Took a look at Kadira and it seems that it hijacks and wraps the core functions, this is a behaviour that I would like to avoid if possible. Is there any naming convention not present in the docs?

3 Likes

Why aren’t you simply just caching into memory the user’s roles ? And do just one fetch first time ? Also, aren’t you concerned that his roles may change during his session ?

1 Like

This is very interesting as in our app we do have the concept of organisations we do have to every fucking time query the DB to get the organisation from the userId.

We do not use Meteor.users(), but Meteor.users.findOne(userId, { fields: { organizationId: true } }). But it is still not the optimal way.

I’m concerned about changes. I can track when you login, logout and also when some Roles/Permissions methods is called. The problem is you’ll be doing this all the time: Meteor.call( "blaBlaMethods'" , arg, arg2 , **organisationId** )

You will be using the organisation ID everywhere because the use can hit the server with any organisation. You can create a React Container or the application for calling a method that sets the current organisation to current websocket connected.

I got a feedback from a fellow developer, Igor Vaneev, and his solution is interesting.

You can create a root application container that get all the data needed for roles and permissions. This data comes in the form of a JWT Token payload. So you only care about encrypting this data to be sure the client doesn’t changes anything.

Then you create a client side helper that wraps Meteor.call and Meteor.subscribe to inject a last argument: payload. There you have it, you can decode the payload and check the permissions.

I would still rather use the Fiber.current, I don’t think it would break Meteor in any way but this is also a plausible solution.

What did you ended up doing?

I believe Kadira managed to get custom data via DDP because they replace the default publication and method function.

Have you thought about using Redis to keep track of organizations? The key/value style should be a good fit and it should be significantly faster.

I’m still baking it but the approach will be:
1 - On user login, an application root container will fetch all the global data for permissions, roles and etc. This info is encrypted with the JWT library
2 - Create a client side wrapper for Meteor.call and Meteor.subscribe to inject a last argument userInfo
3 - On the server I’ll decrypt this object and I’ll avoid hitting the network and other servers.
4- I’ll use the decrypt object to instantiate a class that has all the logic required. This ends up being quite flexible because you can save this object in any collection to get custom document based permissions. My use case is not a trivial one.

This solves the problem.
The initial idea is possible but it would require you to mess around with Meteor internal components, creating a wrapper like Kadira does but it may not survive another Meteor version. Kadira used to be maintained by Arunoda and now MDG is maintaining it. if something changes Kadira will work but my app might suffer in the long run since this is not officially documented.

I think MDG should address this problem since this could improve meteor app performance by a major factor. Authentication and roles permissions is a basic element of web apps.