Exactly.
Here’s a naïve implementation of the caching, and at first sight it looks quite straightforward:
import cache from 'js-cache'; // https://github.com/dirkbonhomme/js-cache
const yourCache = new cache();
/**
* Set this to a duration long enough for all clients to call this method.
* Once expired, js-cache will automatically delete the item.
*/
const dataTTL = 15000; // 15 seconds
new ValidatedMethod({
name: 'setData',
validate: new SimpleSchema({
data: Object, // make it nicer
}).validator(),
run({ data }) {
this.unblock();
const version = Date.now();
yourCache.set(version, data, dataTTL);
MyData.update( {...}, { $set: { data } } ); // whatever
// The document the 1000 users will subscribe to
MyDataVersion.upsert( { _id: "versionSignal" }, { $set: { version } } );
}
});
new ValidatedMethod({
name: 'fetchData',
validate: new SimpleSchema({
version: Number
}).validator(),
run({ version }) {
this.unblock();
let data = yourCache.get(version);
if (data) return data;
data = MyData.find( {...} );
yourCache.set(version, data, dataTTL);
return data;
}
});
If you have multiple server instances, fetchData
's cache management still isn’t going to be optimal on the instances other than on which setData
was invoked.
The reason is that once the subscription delivers the version change to the 1000 users, they all will start hammering their respective Meteor instance to fetch the data. Collection#find
is entering a Fiber for each and every user, so they will practically hammer MongoDB all at the same time with the same request.
One could make a case that it doesn’t matter that much, because MongoDB is smart enough to do some caching as well, so it won’t actually perform hundreds of thousands of times the very same query. That’s probably true, but then why do we do any caching on the server in the first place?
So ultimately this was a demonstration of how to NOT do the caching server-side.
Redis is commonly used as a distributed cache; it’s lightning fast, and a TTL can also be set on each item. Here’s a Node.js redis client.
It also has functions to set
and get
, however they need to be promisified, and used accordingly – see the documentation.