Comments:"Don't Spam Your Users: Batch Notifications in Rails — Meldium"
URL:http://blog.meldium.com/home/2013/4/22/dont-spam-your-users-batch-notifications-in-rails
A simple batching design
Before I discuss the implementation of batching, I want to talk briefly about a simple batching algorithm that doesn't involve too much state tracking. The idea is that we want to wake up periodically, see if there are any new notification batches to deliver, and then either deliver them or go back to sleep, without adding much bookkeeping. At a high level, the algorithm looks like this:
every POLLING_PERIOD minutes:
for each user:
undelivered <- all the undelivered notifications for the user
newest <- the maximum timestamp in undelivered
if (now - newest) > COOLDOWN_PERIOD:
deliver_as_batch undelivered
This algorithm effectively starts a timer of length COOLDOWN_PERIOD
every time a notification is created, and resets that timer if another one is created in the meantime. There's one flaw in this algorithm that may or may not be critical for your application - old notifications can be starved and never sent if new notifications keep coming in (since our timer keeps getting reset). If your notifications are time-critical, you can use this modified algorithm to make sure they don't get starved for too long:
every POLLING_PERIOD minutes:
for each user:
undelivered <- all the undelivered notifications for the user
oldest, newest <- the minimum and maximum timestamps in undelivered
if (now - newest) > COOLDOWN_PERIOD:
deliver_as_batch undelivered
else if (now - oldest) > MAX_STALE_MESSAGE_PERIOD:
deliver_as_batch undelivered
The else if
clause above ensures that messages won't remain in the batching state for too long, but also means that users might receive multiple batches in the event of frequent notifications. The values of POLLING_PERIOD
, COOLDOWN_PERIOD
, and MAX_STALE_MESSAGE_PERIOD
must all be tuned to your application - after looking at our historic sharing data, we decided to set POLLING_PERIOD
at 1 minute, COOLDOWN_PERIOD
at 4 minutes, and we did not implement MAX_STALE_MESSAGE_PERIOD
.