PDF に変換するために puppeteer を使うが、GROWI のコンテナ上でヘッドレスブラウザを立ち上げるのは、一つのコンテナが担う処理としては多すぎるため、別のコンテナ上で puppeteer を立ち上げる。その設計を以下に示す。別のコンテナで立ち上げるアプリ名は growi-pdf-converter とする。
エクスポートまでの全体フロー
- GROWI のコンテナと growi-pdf-converter のコンテナを、共有ディレクトリを設定して立ち上げる
- GROWI.cloud: GCS FUSE でバケットを共有する
- OSS: docker-compose で shared volume を設定する
- GROWI アプリで bulk export を開始する
- GROWI アプリから pdf 変換部分を growi-pdf-converter にリクエストし、growi-pdf-converter は変換した pdf ファイルを共有ディレクトリに出力する
- pdf の変換が完了したら、GROWI アプリはクラウドストレージにアップロードする
growi-pdf-converter
API
/pdf/html-to-pdf
パラメータ
- htmlString: page の markdown body を html に変換した string
- fileName: エクスポートするファイル名 (page の path)
- jobId: PageBulkExportJob の id
- 共有ディレクトリ上に job id のディレクトリを作り、そこにエクスポートするファイルを出力する
処理
- #{共有ディレクトリパス}/#{jobId} というディレクトリがない場合は作成する
- 渡された htmlString を puppeteer を使って pdf に変換し、#{共有ディレクトリパス}/#{jobId} に出力する
growi
- PDF の bulk export を
POST /_api/v3/page-bulk-export
当てにリクエスト - 各ページデータをストリームで読み込む
- 読み込んだページの body の PDF への変換と fs への書き込みを growi-pdf-converter にリクエストし、処理が終わるのを await する
- 変換が timeout (120s) する場合は全体のエクスポートを失敗させる
- 上記処理はストリームの中で行われるため、growi-pdf-converter の処理よりページデータの読み込みが早い場合は back pressure が生じ、ページデータの読み込みがストップする
- fs へのエクスポートが終了したら、共有ディレクトリから PDF データを読み込み、クラウドストレージにアップロードする
検証
実行時間
ランダムな30,000文字を記述したファイルを
- 10,000 ページ (1ページツリー, 1並列) エクスポート: 28min
- 50,000 ページ (5ページツリー, 5並列) エクスポート: 57min
各コンテナの使用リソース量
変換1並列
growi-pdf-converter
メモリ
- 実行前: 265MB
- 実行中: 350 - 450MB
CPU
00:12 からエクスポート実行
growi
メモリ
- 実行前: 4.94GB
- 実行中: 4.91 - 5.12GB
- 最初すぐ max 値まで上がり、それ以降は実行前と同じ量を維持
CPU (大きな変化は無し)
00:12 からエクスポート実行
変換5並列 (max)
growi-pdf-converter
メモリ
- 実行前: 289MB
- 実行中: 400 - 650MB
CPU
00:12 からエクスポート実行
growi
メモリ
- 実行前: 4.62MB
- 実行中: 4.98 - 5.43GB
- 最初すぐ max 値まで上がり、それ以降は実行前と同じ量を維持
CPU (大きな変化は無し)
00:12 からエクスポート実行
実行前の大きな揺れはコンパイルの影響