How to distribute unique coupon codes with 70 requests/sec using Node.js How to distribute unique coupon codes with 70 requests/sec using Node.js node.js node.js

How to distribute unique coupon codes with 70 requests/sec using Node.js


The problem lies in the non-blocking/asynchronous nature of Node. Calls to the same function from simultaneous requests don't wait for each other to finish. A lot of requests come in and concurrently access the global codes array.

You give out the same code multiple times because the counter is incremented by multiple requests concurrently so it can happen that more than one requests sees the same counter state.

One approach to manage the concurrency problem is to allow only one access (to getUserACoupon in your case) at a time, so that part of execution where a coupon is consumed is synchronised or mutually exclusive. One way to achieve this is a locking mechanism so when one request gains access to the lock, further requests wait until the lock is released. In pseudo code it could look something like this:

wait until lock existscreate lockif any left, consume one couponremove lock

But this approach goes against the non-blocking nature of Node and also introduces the problem of who gets the lock when released if more than one request was waiting.

A better way is more likely a queue system. It should work so a code is not consumed at the time of the request but placed in a queue as a callable, waiting to kick off. You can read the length of the queue and stop accepting new requests ("sold out"), however, this will be still concurrent over a global queue/counter so you might end up with a few more queued items than coupons, but this is not a problem because the queue will be processed synchronously so it can be exactly determined when the number of allocated coupons are reached and just give "sold out" to the rest if any and, more importantly, ensure each code is served only once.

Using temporal, it could be quite easy to create a linear, delayed task list:

var temporal = require("temporal");global.queues = {};

app.post('/getCoupon', urlencodedParser, function(req, res){   if (!req.body) return res.status(400).send("Bad Request");   if (!req.body.category) return res.status(200).send("Please Refresh the Page.");    // Create global queue at first request or return to it.    var queue;    if( !global.queues[req.body.objectId] ) {        queue = global.queues[req.body.objectId] = temporal.queue([]);    }    else {        queue = global.queues[req.body.objectId];    }    // Prevent queuing after limit    // This will be still concurrent access so in case of large     // number of requests a few more may end up queued    if( global.given[objectId] >= global.coupons[objectId].length ) {        res.type('text/plain');        res.status(200).send("Sold out!");        return;    }    queue.add([{      delay: 200,      task: function() {        //Go grab a coupon        var coupon = getUserACoupon(req.body.objectId);        res.type('text/plain');        res.status(200).send(coupon);        if(coupon != "Sold Out!" && coupon != "Bad Request: Object does not exist."){          //Update user & product analytics          setStatsAfterCouponsSent(req.body.objectId, req.body.sellerProduct, req.body.userEmail, req.body.purchaseProfileId, coupon, req.body.category);        }      }      }]);});

One key point here is that temporal executes the tasks sequentially adding up the delays, so if the delay is more then it is needed for the task to run, no more than one task will access the counter/codes array at once.

You could implement you own solution based on this logic using timed queue processing as well, but temporal seems worth a try.


Have you considered queueing the http requests as they come in, in order to maintain their order like in this question. How to queue http get requests in Nodejs in order to control their rate?