[8.16] [Screenshotting/PDF Worker Thread] Add type to GeneratePdfResponse (#196860) (#197128)

# Backport

This will backport the following commits from `main` to `8.16`:
- [[Screenshotting/PDF Worker Thread] Add type to GeneratePdfResponse
(#196860)](https://github.com/elastic/kibana/pull/196860)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Krzysztof
Kowalczyk","email":"krzysztof.kowalczyk@elastic.co"},"sourceCommit":{"committedDate":"2024-10-21T19:15:58Z","message":"[Screenshotting/PDF
Worker Thread] Add type to GeneratePdfResponse (#196860)\n\n##
Summary\r\n\r\nThis PR adds a `type` to `GeneratePdfResponse` and
introduces more\r\nlogging for PDF worker.\r\n\r\nCloses:
#194493","sha":"c25a97bc0a3a85ad5c7bb18aab7916410efd05b7","branchLabelMapping":{"^v9.0.0$":"main","^v8.17.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","Team:SharedUX","v8.16.0","Feature:Reporting:Screenshot","backport:version","v8.17.0"],"title":"[Screenshotting/PDF
Worker Thread] Add type to
GeneratePdfResponse","number":196860,"url":"https://github.com/elastic/kibana/pull/196860","mergeCommit":{"message":"[Screenshotting/PDF
Worker Thread] Add type to GeneratePdfResponse (#196860)\n\n##
Summary\r\n\r\nThis PR adds a `type` to `GeneratePdfResponse` and
introduces more\r\nlogging for PDF worker.\r\n\r\nCloses:
#194493","sha":"c25a97bc0a3a85ad5c7bb18aab7916410efd05b7"}},"sourceBranch":"main","suggestedTargetBranches":["8.16","8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/196860","number":196860,"mergeCommit":{"message":"[Screenshotting/PDF
Worker Thread] Add type to GeneratePdfResponse (#196860)\n\n##
Summary\r\n\r\nThis PR adds a `type` to `GeneratePdfResponse` and
introduces more\r\nlogging for PDF worker.\r\n\r\nCloses:
#194493","sha":"c25a97bc0a3a85ad5c7bb18aab7916410efd05b7"}},{"branch":"8.16","label":"v8.16.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.x","label":"v8.17.0","branchLabelMappingKey":"^v8.17.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Krzysztof Kowalczyk <krzysztof.kowalczyk@elastic.co>
This commit is contained in:
Kibana Machine 2024-10-22 08:29:45 +11:00 committed by GitHub
parent 68a67f34f6
commit 494164e179
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 91 additions and 22 deletions

View file

@ -22,7 +22,12 @@ import {
} from './constants';
import { REPORTING_TABLE_LAYOUT } from './get_doc_options';
import { getFont } from './get_font';
import type { GeneratePdfRequest, GeneratePdfResponse, WorkerData } from './worker';
import {
GeneratePdfResponseType,
type GeneratePdfRequest,
type WorkerData,
GeneratePdfResponse,
} from './worker';
// Ensure that all dependencies are included in the release bundle.
import './worker_dependencies';
@ -32,6 +37,8 @@ export class PdfMaker {
private content: Content[];
private worker?: Worker;
private workerLogger: Logger;
private pageCount: number = 0;
private transferList: ArrayBuffer[] = [];
@ -71,6 +78,7 @@ export class PdfMaker {
) {
this.title = '';
this.content = [];
this.workerLogger = logger.get('pdf-worker');
// running in dist: `worker.ts` becomes `worker.js`
// running in source: `worker_src_harness.ts` needs to be wrapped in JS and have a ts-node environment initialized.
@ -209,26 +217,37 @@ export class PdfMaker {
const { port1: myPort, port2: theirPort } = new MessageChannel();
this.worker = this.createWorker(theirPort);
this.worker.on('error', (workerError: NodeJS.ErrnoException) => {
this.workerLogger.error(`Worker error: ${workerError}`);
if (workerError.code === 'ERR_WORKER_OUT_OF_MEMORY') {
reject(new errors.PdfWorkerOutOfMemoryError(workerError.message));
} else {
reject(workerError);
}
});
this.worker.on('exit', () => {});
this.worker.on('exit', () => {
this.workerLogger.debug('Worker exited');
});
// We expect one message from the worker generating the PDF buffer.
myPort.on('message', ({ error, data }: GeneratePdfResponse) => {
if (error) {
myPort.on('message', ({ type, error, data, message }: GeneratePdfResponse) => {
if (type === GeneratePdfResponseType.Log && message) {
this.workerLogger.debug(message);
return;
}
if (type === GeneratePdfResponseType.Error) {
reject(new Error(`PDF worker returned the following error: ${error}`));
return;
}
if (!data) {
if (type === GeneratePdfResponseType.Data && !data) {
reject(new Error(`Worker did not generate a PDF!`));
return;
}
this.pageCount = data.metrics.pages;
resolve(data.buffer);
if (data) {
this.pageCount = data.metrics.pages;
resolve(data.buffer);
}
});
// Send the request

View file

@ -34,23 +34,44 @@ export interface GeneratePdfRequest {
data: GenerateReportRequestData;
}
export type GeneratePdfResponse = SuccessResponse | ErrorResponse;
export interface SuccessResponse {
error?: undefined;
data: {
buffer: Uint8Array;
metrics: {
pages: number;
};
export interface GeneratePdfData {
buffer: Uint8Array;
metrics: {
pages: number;
};
}
export interface ErrorResponse {
error: string;
data: null;
export enum GeneratePdfResponseType {
Log,
Data,
Error,
}
interface GeneratePdfLogResponse {
type: GeneratePdfResponseType.Log;
data?: GeneratePdfData;
error?: string;
message?: string;
}
interface GeneratePdfDataResponse {
type: GeneratePdfResponseType.Data;
data?: GeneratePdfData;
error?: string;
message?: string;
}
interface GeneratePdfErrorResponse {
type: GeneratePdfResponseType.Error;
data?: GeneratePdfData;
error?: string;
message?: string;
}
export type GeneratePdfResponse =
| GeneratePdfLogResponse
| GeneratePdfDataResponse
| GeneratePdfErrorResponse;
if (!isMainThread) {
const { port } = workerData as WorkerData;
port.on('message', execute);
@ -68,6 +89,11 @@ const getPageCount = (pdfDoc: PDFKit.PDFDocument): number => {
async function execute({ data: { layout, logo, title, content } }: GeneratePdfRequest) {
const { port } = workerData as WorkerData;
port.postMessage({
type: GeneratePdfResponseType.Log,
message: 'Starting execution',
});
try {
const tableBorderWidth = 1;
@ -89,6 +115,11 @@ async function execute({ data: { layout, logo, title, content } }: GeneratePdfRe
},
};
port.postMessage({
type: GeneratePdfResponseType.Log,
message: 'Initializing PDF printer',
});
const printer = new Printer(fonts);
const docDefinition = _.assign(getTemplate(layout, logo, title, tableBorderWidth, assetPath), {
@ -103,6 +134,11 @@ async function execute({ data: { layout, logo, title, content } }: GeneratePdfRe
),
});
port.postMessage({
type: GeneratePdfResponseType.Log,
message: 'Generating document stream',
});
const pdfDoc = printer.createPdfKitDocument(docDefinition, {
tableLayouts: {
noBorder: {
@ -121,6 +157,11 @@ async function execute({ data: { layout, logo, title, content } }: GeneratePdfRe
throw new Error('Document stream has not been generated');
}
port.postMessage({
type: GeneratePdfResponseType.Log,
message: 'Document stream has been generated',
});
const buffer = await new Promise<Buffer>((resolve, reject) => {
const buffers: Buffer[] = [];
pdfDoc.on('error', reject);
@ -133,7 +174,13 @@ async function execute({ data: { layout, logo, title, content } }: GeneratePdfRe
pdfDoc.end();
});
const successResponse: SuccessResponse = {
port.postMessage({
type: GeneratePdfResponseType.Log,
message: 'PDF buffer has been generated',
});
const successResponse: GeneratePdfResponse = {
type: GeneratePdfResponseType.Data,
data: {
buffer,
metrics: {
@ -143,7 +190,10 @@ async function execute({ data: { layout, logo, title, content } }: GeneratePdfRe
};
port.postMessage(successResponse, [buffer.buffer /* Transfer buffer instead of copying */]);
} catch (error) {
const errorResponse: ErrorResponse = { error: error.message, data: null };
const errorResponse: GeneratePdfResponse = {
type: GeneratePdfResponseType.Error,
error: error.message,
};
port.postMessage(errorResponse);
} finally {
process.nextTick(() => {