Bmansurov has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/387642 )

Change subject: WIP: add timeout to requests in the queue
......................................................................

WIP: add timeout to requests in the queue

Bug: T178501
Change-Id: Ib8b3ba4e609bfac457a3d1d21cef85c1da08bc20
---
M config.dev.yaml
M lib/queue.js
M routes/html2pdf-v1.js
3 files changed, 61 insertions(+), 19 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/services/chromium-render 
refs/changes/42/387642/1

diff --git a/config.dev.yaml b/config.dev.yaml
index 61f1779..d6d2ca2 100644
--- a/config.dev.yaml
+++ b/config.dev.yaml
@@ -95,4 +95,6 @@
         - '--no-sandbox'
         - '--disable-setuid-sandbox'
       # the maximum number of puppeteer instances that can be launched at a 
time
-      puppeteer_max_instances: 1
\ No newline at end of file
+      puppeteer_max_instances: 1
+      # don't wait to render a PDF after this many seconds
+      wait_time_to_render: 90
\ No newline at end of file
diff --git a/lib/queue.js b/lib/queue.js
index 868f420..6be619c 100644
--- a/lib/queue.js
+++ b/lib/queue.js
@@ -1,23 +1,52 @@
 'use strict';
 
 const asyncQueue = require('async/queue');
+const renderer = require('./renderer');
 
+const taskStatuses = {
+    waiting: 0,
+    timedout: 1
+}
 
 module.exports = class Queue {
     /**
-      * @param {Function} worker
-      * @param {number} concurrency
-      */
-    constructor(worker, concurrency) {
-        this._queueObject = asyncQueue(worker, concurrency);
+     * @param {number} concurrency
+     */
+    constructor(concurrency) {
+        this._queueObject = asyncQueue(this._worker, concurrency);
     }
 
     /**
-      * Push data to the queue
-      * @param {Object} data that the worker needs
-      * @param {Function} callback called when the worker finishes
-      */
+     * Worker that renders a PDF and calls the callback when it's done
+     * @param {Object} data that the worker needs
+     * @param {Function} callback called when the worker finishes
+     */
+    _worker (data, callback) {
+        if (data._status === taskStatuses.timedout) {
+            callback('ignore', null);
+            return;
+        }
+        clearTimeout(data._timeout);
+
+        const pdfPromise = renderer.articleToPdf(
+            data.uri, data.format, data.conf);
+        pdfPromise.then(() => {
+            callback(null, pdfPromise);
+        });
+    }
+
+    /**
+     * Push data to the queue
+     * @param {Object} data that the worker needs
+     * @param {Function} callback called when the worker finishes
+     */
     push(data, callback) {
+        data._status = taskStatuses.waiting;
+        data._timeout = setTimeout(() => {
+            data._status = taskStatuses.timedout;
+            callback('timedout', null);
+        }, data.conf.wait_time_to_render * 1000);
+
         this._queueObject.push(data, callback);
     }
 };
diff --git a/routes/html2pdf-v1.js b/routes/html2pdf-v1.js
index 4e928d4..c657fc5 100644
--- a/routes/html2pdf-v1.js
+++ b/routes/html2pdf-v1.js
@@ -2,7 +2,6 @@
 
 const Queue = require('../lib/queue');
 const sUtil = require('../lib/util');
-const renderer = require('../lib/renderer');
 
 /**
  * The main router object
@@ -31,7 +30,25 @@
         uri: restbaseRequest.uri,
         format: req.params.format,
         conf: app.conf
-    }, ((pdfPromise) => {
+    }, ((error, pdfPromise) => {
+        if (error === 'ignore') {
+            return;
+        }
+
+        if (error !== null) {
+            let message = 'unknown error',
+                status = 500;
+
+            if (error === 'timedout') {
+                message = 'Queue is busy.';
+                status = 503;
+            }
+
+            app.logger.log('trace/error', message);
+            res.status(status).send();
+            return;
+        }
+
         pdfPromise.then((pdf) => {
             const headers = {
                 'Content-Type': 'application/pdf',
@@ -54,13 +71,7 @@
 module.exports = function(appObj) {
     app = appObj;
 
-    const worker = (data, callback) => {
-        const pdfPromise = renderer.articleToPdf(
-            data.uri, data.format, data.conf);
-        pdfPromise.then(callback(pdfPromise));
-    };
-
-    app.queue = new Queue(worker, app.conf.puppeteer_max_instances);
+    app.queue = new Queue(app.conf.puppeteer_max_instances);
 
     // the returned object mounts the routes on
     // /{domain}/vX/mount/path

-- 
To view, visit https://gerrit.wikimedia.org/r/387642
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ib8b3ba4e609bfac457a3d1d21cef85c1da08bc20
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/services/chromium-render
Gerrit-Branch: master
Gerrit-Owner: Bmansurov <bmansu...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to