Skip to content

Commit

Permalink
[Screenshotting/PDF Worker Thread] Add type to GeneratePdfResponse (e…
Browse files Browse the repository at this point in the history
…lastic#196860)

## Summary

This PR adds a `type` to `GeneratePdfResponse` and introduces more
logging for PDF worker.

Closes: elastic#194493
(cherry picked from commit c25a97b)
  • Loading branch information
kowalczyk-krzysztof committed Oct 21, 2024
1 parent 5248361 commit 82eeb77
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -32,6 +37,8 @@ export class PdfMaker {
private content: Content[];

private worker?: Worker;
private workerLogger: Logger;

private pageCount: number = 0;
private transferList: ArrayBuffer[] = [];

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;

Expand All @@ -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), {
Expand All @@ -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: {
Expand All @@ -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);
Expand All @@ -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: {
Expand All @@ -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(() => {
Expand Down

0 comments on commit 82eeb77

Please sign in to comment.