msavin / userCache

The one simple trick that can save you millions of database requests
MIT License
35 stars 6 forks source link

Stale data is received if doing a findOne after an update #13

Open wildhart opened 4 years ago

wildhart commented 4 years ago

If quickly doing a findOne(field) after an update() then stale data is received. Contrived example:

const name = Meteor.user('profile').profile.name;
console.log('before', name);  // -> "bob"
Meteor.users.update(Meteor.userId(), {$set: {"profile.name": name+name.length}});
console.log('after ', Meteor.user('profile').profile.name); // -> "bob"  - should be "bob3"

This is a contrived example for demonstration purposes, but I have hit this bug in real production code.

My work-around involves a bit of a hack of Meteor.EnvironmentVariable to set an environment variable within the Fiber whenever the Meteor.users collection is updated:

const _origUpdate = Meteor.users.update;
const updated = new Meteor.EnvironmentVariable();
var Fiber = Npm.require('fibers');

// https://github.com/meteor/meteor/blob/096641b084b70f4ab52f3ef4468728d164ec66d1/packages/meteor/dynamics_nodejs.js#L47
Meteor.EnvironmentVariable.prototype.set = function(value) {
    Meteor._nodeCodeMustBeInFiber();
    if (!Fiber.current._meteor_dynamics) Fiber.current._meteor_dynamics = [];
    var currentValues = Fiber.current._meteor_dynamics;
    currentValues[this.slot] = value;
}

// Set an environment variable whenever the Meteor.users collection is updated
Meteor.users.update = function(...args) {
    updated.set(true);
    return _origUpdate.apply(this, args);
}

Meteor.user = function (input) {
    // don't hit the cache if the Meteor.users collection has been updated in this Fiber
    if (typeof input === "undefined" || updated.get()) {
        return _original();
    }
    ...
wildhart commented 4 years ago

FYI, I've written a meteor package wildhart:env-var-set which adds the EnviromentVariable.set()method so feel free to re-use that if you want.

I've used it within wildhart:mergebox-cache which is my own fork of userCache which works with all collections, not just Meteor.users.