diff --git a/mermaid/src/browser-instance.js b/mermaid/src/browser-instance.js index 3ad8a9cd5..b2e841040 100644 --- a/mermaid/src/browser-instance.js +++ b/mermaid/src/browser-instance.js @@ -2,6 +2,8 @@ import puppeteer from 'puppeteer' import { logger } from './logger.js' +let INSTANCE + const createBrowser = async () => { const browser = await puppeteer.launch({ headless: 'new', @@ -30,8 +32,9 @@ const createBrowser = async () => { '--disable-software-rasterizer' ] }) - const browserProcess = browser.process() + logger.info(`Chrome instance launched with pid ${browserProcess.pid}`) + browserProcess.stdout.unpipe() browserProcess.stderr.unpipe() browserProcess.stdout.on('data', (data) => { @@ -50,6 +53,9 @@ const createBrowser = async () => { }) browserProcess.on('exit', (code, signal) => { logger.error({ code, signal }, 'chrome process exited') + browserProcess.kill() + browser.close() + INSTANCE = undefined }) browserProcess.on('message', (message) => { logger.warn({ message }, 'chrome process message') @@ -60,10 +66,14 @@ const createBrowser = async () => { await browser.close() throw err } finally { - browser.disconnect() + await browser.disconnect() } } -export async function create () { - return createBrowser() +export async function getBrowserWSEndpoint () { + if (INSTANCE === undefined) { + INSTANCE = await createBrowser() + logger.info(`Chrome accepting connections on endpoint ${INSTANCE.wsEndpoint()}`) + } + return INSTANCE.wsEndpoint() } diff --git a/mermaid/src/index.js b/mermaid/src/index.js index 250e7124f..f70318a53 100644 --- a/mermaid/src/index.js +++ b/mermaid/src/index.js @@ -5,13 +5,10 @@ import http from 'node:http' import micro from 'micro' import { SyntaxError, TimeoutError, Worker } from './worker.js' import Task from './task.js' -import { create } from './browser-instance.js' (async () => { // QUESTION: should we create a pool of Chrome instances ? - const browser = await create() - logger.info(`Chrome accepting connections on endpoint ${browser.wsEndpoint()}`) - const worker = new Worker(browser) + const worker = new Worker() const server = new http.Server( micro.serve(async (req, res) => { // Add a /health route that renders a sample diagram by calling the worker diff --git a/mermaid/src/worker.js b/mermaid/src/worker.js index 7153f1d9d..9c0449953 100644 --- a/mermaid/src/worker.js +++ b/mermaid/src/worker.js @@ -4,6 +4,7 @@ import path from 'node:path' import puppeteer, { HTTPResponse, Page } from 'puppeteer' import { logger } from './logger.js' import { updateConfig } from './config.js' +import { getBrowserWSEndpoint } from './browser-instance.js' const __dirname = fileURLToPath(new URL('.', import.meta.url)) @@ -22,8 +23,7 @@ export class SyntaxError extends Error { } export class Worker { - constructor (browserInstance) { - this.browserWSEndpoint = browserInstance.wsEndpoint() + constructor () { this.pageUrl = process.env.KROKI_MERMAID_PAGE_URL || `file://${path.join(__dirname, '..', 'assets', 'index.html')}` this.convertTimeout = process.env.KROKI_MERMAID_CONVERT_TIMEOUT || '10000' } @@ -109,8 +109,9 @@ export class Worker { * @private */ async _connect () { + const browserWSEndpoint = await getBrowserWSEndpoint() return await puppeteer.connect({ - browserWSEndpoint: this.browserWSEndpoint, + browserWSEndpoint, ignoreHTTPSErrors: true }) }