MHogge
MHogge

Reputation: 5196

express - Limit a request to one at a time

I'm using express to build a API that should be used internally. One of the request trigger an heavy process on the server and should return a CSV out of that. This process might take more than 10 minutes.

To not overloading the server I want to restrict the call of this API and make it so that, since the process isn't terminated, we can't request the same URL again.

For this I tried to use express-rate-limit with the following configuration:

new RateLimit({
    windowMs: 30 * 60 * 1000, // 30 minutes
    max: 1,
    delayMs: 0, // disabled
    message: 'Their is already a running execution of the request. You must wait for it to be finished before starting a new one.',
    handler: function handler(req, res) {
        logger.log('max request achieved');
        logger.log(res);
    },
});

But it seems that the 'max request' is reached every time after exactly 2 mins even if I start only once. I suspect the browser to retry the request after 2 min if doesn't get any answer, is it possible?

I would like that this request doesn't have any retry-strategy and that the only way the max request is reached is by manually asking the server to execute this request 2 times in a row.

Thank's.


Edit

Here is my full code:

const app = express();
const port = process.env.API_PORT || 3000;

app.enable('trust proxy');

function haltOnTimedout(req, res, next) {
    if (!req.timedout) { next(); }
}

app.use(timeout(30 * 60 * 1000)); // 30min
app.use(haltOnTimedout);

app.listen(port, () => {
    logger.log(`Express server listening on port ${port}`);
});

// BILLING
const billingApiLimiter = new RateLimit({
    windowMs: 30 * 60 * 1000, // 30 minutes
    max: 1,
    delayMs: 0, // disabled
    message: 'Their is already a running execution of the request. You must wait for it to be finished before starting a new one.',
    handler: function handler(req, res) {
        logger.log('max request achieved');
    },
});

app.use('/billing', billingApiLimiter);
app.use('/billing', BillingController);

And the code of my route:

router.get('/billableElements', async (request, response) => {
    logger.log('Route [billableElements] called');
    const { startDate } = request.query;
    const { endDate } = request.query;
    try {
        const configDoc = await metadataBucket.getAsync(process.env.BILLING_CONFIG_FILE || 'CONFIG_BILLING');
        const billableElements = await getBillableElements(startDate, endDate, configDoc.value);
        const csv = await produceCSV(billableElements);
        logger.log('csv produced');
        response.status(200).send(`${csv}`);
    } catch (err) {
        logger.error('An error occured while getting billable elements.', err);
        response.status(500).send('An internal error occured.');
    }
});

Upvotes: 0

Views: 10293

Answers (1)

MHogge
MHogge

Reputation: 5196

I found the answer thank's to this GitHub issue: https://github.com/expressjs/express/issues/2512.

TLDR: I added request.connection.setTimeout(1000 * 60 * 30); to avoid firing the request every 2 minutes.

But considering the code I wrote inside my question, @Paul's advices are still good to be taken into account.

Upvotes: 1

Related Questions