redis-oplog
redis-oplog copied to clipboard
Highly efficient counter
If you want to have a simple count for your query, we should be able to use redis-oplog to catch added, updated, removed events, and update an ObservableCounter.
If we are here why not take another leap and provide a function that allows people to reduce to the value they want. A sort of map reducer. To think
This would be great! Currently using this package, but the ~10 second polling interval can be a bit annoying: https://github.com/nate-strauser/meteor-publish-performant-counts
using "INCR" and "DECR" should do a great job, in the worst case we could appeal to some lua...
I think the problem would be how to count the results for a certain query, i.e. count all documents that belong to a user instead of simply all documents.
Perhaps the same way we have a "channel" parameter we could have a "counter" parameter which would be the id for the counter we are affecting when doing the given insert/update/remove !
Some users cases i can think of:
Let's say you have a query where you need a counter for all documents belonging to a user with status "done" and another counter for all documents belonging to a user with status "todo", so, later on, we could easily provide counters to "paginate" such tables.
Following / Followers: Every time a user follows another user a new document is created in a collection, once a user follow someone we should INCR the following count for a user and INCR the "followed" counter for the other user.
Not sure how performant this really is, also it depends on server-side autoruns --- but our current hack to get Counters working in our transition to redis oplog is:
package.js
Package.describe({
name: 'hive:counts',
version: '0.1.1',
summary: 'Publish counts',
});
Package.onUse(function(api) {
api.versionsFrom('[email protected]');
api.use(['random', 'peerlibrary:[email protected]'], 'server');
api.addFiles('lib/server.js', 'server');
api.addFiles('lib/client.js', 'client');
api.export(['Counter']);
});
Server (lib/server.js
):
const collection = new Meteor.Collection('counters-collection', { connection: null });
// Sets up an autorun for a counters-collection insert unique to this
// publication. Inserts a document on the first run, updates after that.
// Once the subscription is stopped, removes the item.
Counter = function(name, cursor, sub) {
var _id = Random.id();
var c = null;
Tracker.autorun(function(computation) {
c = computation;
var count = cursor.count();
if (computation.firstRun) {
collection.insert({ _id: _id, name: name, count: count });
} else {
collection.update({ _id: _id }, { $set: { count: count } });
}
});
sub.onStop(function() {
if (c && c.stop) {
c.stop();
collection.remove({ _id: _id });
}
});
return collection.find({ _id: _id });
};
Client (lib/client.js
):
Counter = {};
Counter.collection = new Meteor.Collection('counters-collection');
Counter.get = function(name) {
var doc = Counter.collection.findOne({ name: name });
if (doc) {
return doc.count;
} else {
return 0;
}
};
Example usage on Server:
Meteor.publish('mySubscription', function mySubcription() {
const subName = 'someUniqueCounterName';
const self = this;
const cursor = MyCollection.find({});
const counter = new Counter(subName, cursor, self);
return [counter, cursor];
});
Example usage on Client:
Meteor.subscribe('mySubscription', () => {
Counter.get('someUniqueCounterName');
});
Again, just a hack to get it working. Haven't benchmarked yet since we're not using redis oplog in production yet but are working towards it.