Improved speed of vote score updates (Mongo aggregator + bulkwrite)

This commit is contained in:
Discordius 2017-10-25 04:45:21 -07:00
parent 7ff103f806
commit 78308f6810
2 changed files with 117 additions and 11 deletions

View file

@ -1,5 +1,5 @@
import { getSetting, registerSetting, debug } from 'meteor/vulcan:core';
import { updateScore } from './scoring.js';
import { /*updateScore,*/ batchUpdateScore } from './scoring.js';
import { VoteableCollections } from '../modules/make_voteable.js';
registerSetting('voting.scoreUpdateInterval', 60, 'How often to update scores, in seconds');
@ -14,28 +14,32 @@ Meteor.startup(function () {
VoteableCollections.forEach(collection => {
// active items get updated every N seconds
Meteor.setInterval(function () {
Meteor.setInterval(async function () {
let updatedDocuments = 0;
// let updatedDocuments = 0;
// console.log('tick ('+scoreInterval+')');
collection.find({'inactive': {$ne : true}}).forEach(document => {
updatedDocuments += updateScore({collection, item: document});
});
// collection.find({'inactive': {$ne : true}}).forEach(document => {
// updatedDocuments += updateScore({collection, item: document});
// });
const updatedDocuments = await batchUpdateScore(collection, false, false);
debug(`[vulcan:voting] Updated scores for ${updatedDocuments} active documents in collection ${collection.options.collectionName}`)
}, scoreInterval * 1000);
// inactive items get updated every hour
Meteor.setInterval(function () {
Meteor.setInterval(async function () {
let updatedDocuments = 0;
// let updatedDocuments = 0;
//
// collection.find({'inactive': true}).forEach(document => {
// updatedDocuments += updateScore({collection, item: document});
// });
collection.find({'inactive': true}).forEach(document => {
updatedDocuments += updateScore({collection, item: document});
});
const updatedDocuments = await batchUpdateScore(collection, true, false);
debug(`[vulcan:voting] Updated scores for ${updatedDocuments} inactive documents in collection ${collection.options.collectionName}`)

View file

@ -57,3 +57,105 @@ export const updateScore = ({collection, item, forceUpdate}) => {
}
return 0;
};
export const batchUpdateScore = async (collection, inactive = false, forceUpdate = false) => {
// n = number of days after which a single vote will not have a big enough effect to trigger a score update
// and posts can become inactive
const n = 30;
// x = score increase amount of a single vote after n days (for n=100, x=0.000040295)
const x = 1/Math.pow(n*24+2,1.3);
// time decay factor
const f = 1.3
const itemsPromise = collection.rawCollection().aggregate([
{
$match: {
$and: [
{postedAt: {$exists: true}},
{postedAt: {$lte: new Date()}},
{inactive: inactive ? true : {$ne: true}}
]
}
},
{
$project: {
postedAt: 1,
baseScore: 1,
score: 1,
newScore: {
$divide: [
"$baseScore",
{
$pow: [
{
$add: [
{
$divide: [
{
$subtract: [new Date(), "$postedAt"] // Age in miliseconds
},
60 * 60 * 1000
]
}, // Age in hours
2
]
},
f
]
}
]
}
}
},
{
$project: {
postedAt: 1,
baseScore: 1,
score: 1,
newScore: 1,
scoreDiffSignificant: {
$gt: [
{$abs: {$subtract: ["$score", "$newScore"]}},
x
]
},
oldEnough: { // Only set a post as inactive if it's older than n days
$gt: [
{$divide: [
{
$subtract: [new Date(), "$postedAt"] // Difference in miliseconds
},
60 * 60 * 1000 //Difference in hours
]},
n*24]
}
}
},
])
const items = await itemsPromise;
const itemsArray = await items.toArray();
let updatedDocumentsCounter = 0;
const itemUpdates = _.compact(itemsArray.map(i => {
if (forceUpdate || i.scoreDiffSignificant) {
updatedDocumentsCounter++;
return {
updateOne: {
filter: {_id: i._id},
update: {$set: {score: i.newScore, inactive: false}},
upsert: false,
}
}
} else if (i.oldEnough) {
// only set a post as inactive if it's older than n days
return {
updateOne: {
filter: {_id: i._id},
update: {$set: {inactive: true}},
upsert: false,
}
}
}
}))
if (itemUpdates && itemUpdates.length) {await collection.rawCollection().bulkWrite(itemUpdates, {ordered: false});}
return updatedDocumentsCounter;
}