retrofit
Given an existing simple priority queue, e.g. rabbitmq, you could
easily retrofit it with approximately the same epoch
behavior.
Allocate a (zero-origin) vector of queues.
Define index active_queue = 0
, where new arrivals always go.
We can always find alternate_queue
as 1 minus that.
Define current_epoch
according to gran
and a recent timestamp.
New arrivals are thrown into the active queue in the usual way, and the alternate queue is empty.
At some point we notice that we need to update current_epoch
to a subsequent epoch, and we follow this procedure:
- Unconditionally assign
current_epoch
to be current. - Ask whether the alternate queue is empty. Iff empty, toggle the active queue to use it, via
active_queue = 1 - active_queue
.
So at start of each epoch, leftover jobs from previous epoch(s) are being processed in the alternate queue. If we are lightly or moderately busy, we toggle which queue shall be the active one exactly once per epoch.
If we're getting behind, some leftovers will languish
in the alternate queue for more than gran
seconds,
and we don't toggle.
Hence the active queue depth becomes unusually large.
At some point the alternate queue drains,
the epoch ticks over, and at last we toggle.
Since there's an unusually large number of leftovers
that may be from multiple epochs, it may take
multiple epochs for the alternate queue to drain,
even if offered load suddenly goes to zero.
As offered load tapers down, so will queue depths,
and eventually we go back to toggling once per epoch.
In this way we ensure that newly arriving high priority tasks cannot starve ancient low priority ones.