Skip to content

Instantly share code, notes, and snippets.

@renanlecaro
Last active Mar 14, 2021
Embed
What would you like to do?
Rate limiting for Meteor in 2021
/*
Ensures that a function "fn" is only run every "delay" milliseconds.
Let's say you have two meteor users, and they want to call a function repeatedly to send 300 emails each.
Your mailing API blocks you if you go over 2 req per second.
Your SERVER code would look like this
// the expensive function
function rawSendMail(a,b,c){
// (this==null here)
return callSomeExpensiveFunctionThatReturnsResult()
}
// the rate limited version, will not run more than twice a second
const sendMail = rateLimit(rawSendMail, 550) ;
// just call it synchronously in meteor
Meteor.methods({
mailBatch(listOfEmails){
return listOfEmails.map(sendMail)
}
})
*/
export function rateLimit(fn, delay) {
var queue = [],
lastRun = 0,
running = false;
function delayTillNext() {
return Math.max(0, delay - (Date.now() - lastRun));
}
function processQueue() {
lastRun = Date.now();
var item = queue.shift();
if (item) {
try {
item.resolve(fn.apply(null, item.arguments));
} catch (e) {
item.reject(e);
}
}
if (queue.length === 0) {
running = false;
} else {
running = true;
Meteor.setTimeout(processQueue, delayTillNext());
}
}
return function limited() {
const item = {
arguments: [].slice.call(arguments)
};
const promise = new Promise((resolve, reject) => {
item.resolve = resolve;
item.reject = reject;
});
queue.push(item);
if (!running) {
running = true;
Meteor.setTimeout(processQueue, delayTillNext());
}
return promiseToSync(promise);
};
}
// Little helper to make server code wait for a Promise, there's surely a better way
function promiseToSync(promise) {
return Meteor.wrapAsync(cb =>
promise.then(
res => cb(null, res),
err => cb(err)
)
)();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment