GoogleChrome / rendertron

A Headless Chrome rendering solution

Home Page:https://render-tron.appspot.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Graceful Shutdown

hatappi opened this issue · comments

Background

I run Rendertron on docker.

When we execute docker stop, Docker sends SIGTERM and after that, if the container ignore SIGTERM, Docker sends SIGKILL.
https://docs.docker.com/engine/reference/commandline/stop/#extended-description

Environment

  • Docker: 20.10.5, build 55c4c88
  • Rendertron: 3.1.0

Problem

The current Rendertron doesn't support Graceful Shutdown, so I introduced godaddy/terminus to our application using Rendertron to support Graceful Shutdown.

const Rendertron = require('rendertron').Rendertron;
const { createTerminus } = require('@godaddy/terminus');

const rendertron = new Rendertron();
(async () => {
  createTerminus(await rendertron.initialize());
})()

If Rendertron doesn't receive any request, Graceful Shutdown was successful.
But If we try Graceful Shutdown while Rendertron receive traffic (/render/), Graaceful Shutdown was failed.

Error is here:

Error: Protocol error (Page.navigate): Target closed.
    at /opt/rendertron-test/node_modules/rendertron/node_modules/puppeteer/lib/cjs/puppeteer/common/Connection.js:208:63
    at new Promise (<anonymous>)
    at CDPSession.send (/opt/rendertron-test/node_modules/rendertron/node_modules/puppeteer/lib/cjs/puppeteer/common/Connection.js:207:16)
    at navigate (/opt/rendertron-test/node_modules/rendertron/node_modules/puppeteer/lib/cjs/puppeteer/common/FrameManager.js:108:47)
    at FrameManager.navigateFrame (/opt/rendertron-test/node_modules/rendertron/node_modules/puppeteer/lib/cjs/puppeteer/common/FrameManager.js:91:13)
    at Frame.goto (/opt/rendertron-test/node_modules/rendertron/node_modules/puppeteer/lib/cjs/puppeteer/common/FrameManager.js:417:41)
    at Page.goto (/opt/rendertron-test/node_modules/rendertron/node_modules/puppeteer/lib/cjs/puppeteer/common/Page.js:784:53)
    at Renderer.serialize (/opt/rendertron-test/node_modules/rendertron/build/renderer.js:108:35)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
    at async Rendertron.handleRenderRequest (/opt/rendertron-test/node_modules/rendertron/build/rendertron.js:129:28)
response does not exist
  xxx GET /render/http://localhost:8080 500 1,138ms -

  Error: Protocol error: Connection closed. Most likely the page has been closed.
      at Object.exports.assert (/opt/rendertron-test/node_modules/rendertron/node_modules/puppeteer/lib/cjs/puppeteer/common/assert.js:26:15)
      at Page.close (/opt/rendertron-test/node_modules/rendertron/node_modules/puppeteer/lib/cjs/puppeteer/common/Page.js:1198:21)
      at Renderer.serialize (/opt/rendertron-test/node_modules/rendertron/build/renderer.js:117:24)
      at async Rendertron.handleRenderRequest (/opt/rendertron-test/node_modules/rendertron/build/rendertron.js:129:28)
      at async bodyParser (/opt/rendertron-test/node_modules/koa-bodyparser/index.js:95:5)
      at async /opt/rendertron-test/node_modules/koa-compress/lib/index.js:38:5
      at async logger (/opt/rendertron-test/node_modules/koa-logger/index.js:67:7)

(node:91430) UnhandledPromiseRejectionWarning: Error: Failed to launch the browser process!


TROUBLESHOOTING: https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md

    at onClose (/opt/rendertron-test/node_modules/rendertron/node_modules/puppeteer/lib/cjs/puppeteer/node/BrowserRunner.js:193:20)
    at Interface.<anonymous> (/opt/rendertron-test/node_modules/rendertron/node_modules/puppeteer/lib/cjs/puppeteer/node/BrowserRunner.js:183:68)
    at Interface.emit (events.js:327:22)
    at Interface.close (readline.js:424:8)
    at Socket.onend (readline.js:202:10)
    at Socket.emit (events.js:327:22)
    at endReadableNT (internal/streams/readable.js:1327:12)
    at processTicksAndRejections (internal/process/task_queues.js:80:21)
(Use `node --trace-warnings ...` to show where the warning was created)
(node:91430) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:91430) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

If my understanding is correct, Rendertron (Koa) doesn't handle SIGTERM, but Puppeteer handles SIGTERM.
Puppeteer handles SIGTERM as default option (handleSIGTERM), and it closes the browser process.
https://github.com/puppeteer/puppeteer/blob/v10.1.0/docs/api.md#puppeteerlaunchoptions

Rendertron doesn't specify handleSIGTERM option. This option default value is true. As a result, Puppeteer handles SIGTERM.

const browser = await puppeteer.launch({ args: config.puppeteerArgs });

Expected behavior

Rendertron supports Graceful Shutdown.
OR
Developer can control Graceful Shutdown.

In general, as we're deprecating the project, you should look into alternative approaches to rendering on the web.