Skip to content

Commit

Permalink
Refactor PDF generation with ReportSheetCache (#190)
Browse files Browse the repository at this point in the history
* Refactor PDF generation with ReportSheetCache
* Pass a `CancellationToken` to `CreateReportSheetPdfChromium`.
* Enhanced `CreateReportSheetPdfChromium` to handle process timeouts using `Task.WhenAny` and `TimeSpan`, and to throw `OperationCanceledException` if the process exceeds the timeout.
* Minor comment adjustment and clarification of `ProtocolTimeout` in `GetPathToCacheFile`.
* ReportSheetCache.UsePuppeteer default to false
  • Loading branch information
axunonb authored Sep 25, 2024
1 parent 0aa0d1a commit 65f19e7
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 20 deletions.
35 changes: 16 additions & 19 deletions League/Caching/ReportSheetCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,14 @@ public ReportSheetCache(ITenantContext tenantContext, IConfiguration configurati
_pathToChromium = Path.Combine(webHostEnvironment.ContentRootPath, configuration["Chromium:ExecutablePath"] ?? string.Empty);
_loggerFactory = loggerFactory;
_logger = loggerFactory.CreateLogger<ReportSheetCache>();
UsePuppeteer = false;
}

/// <summary>
/// Gets or sets a value indicating whether to use Puppeteer for generating the report sheet,
/// instead of Chromium command line.
/// </summary>
public bool UsePuppeteer { get; set; } = false;
public bool UsePuppeteer { get; set; }

private void EnsureCacheFolder()
{
Expand Down Expand Up @@ -108,7 +109,7 @@ private static bool IsOutdated(string cacheFile, DateTime dataModifiedOn)
// Temporary file with HTML content - extension must be ".html"!
var htmlUri = await CreateHtmlFile(html, tempFolder, cancellationToken);

var pdfFile = await CreateReportSheetPdfChromium(tempFolder, htmlUri);
var pdfFile = await CreateReportSheetPdfChromium(tempFolder, htmlUri, cancellationToken);

var cacheFile = MovePdfToCache(pdfFile, matchId);

Expand Down Expand Up @@ -154,7 +155,7 @@ private string GetPathToCacheFile(long matchId)
{ "--no-sandbox", "--disable-gpu", "--disable-extensions", "--use-cmd-decoder=passthrough" },
ExecutablePath = _pathToChromium,
Timeout = 5000,
ProtocolTimeout = 10000 // default is 180,000
ProtocolTimeout = 10000 // default is 180,000 - used for page.PdfDataAsync
};
// Use Puppeteer as a wrapper for the browser, which can generate PDF from HTML
// Start command line arguments set by Puppeteer v20:
Expand All @@ -168,7 +169,6 @@ private string GetPathToCacheFile(long matchId)
var fullPath = GetPathToCacheFile(matchId);
try
{
// page.PdfDataAsync times out after 180,000ms (3 minutes)
var bytes = await page.PdfDataAsync(new PuppeteerSharp.PdfOptions
{ Scale = 1.0M, Format = PuppeteerSharp.Media.PaperFormat.A4 }).ConfigureAwait(false);

Expand All @@ -183,9 +183,10 @@ private string GetPathToCacheFile(long matchId)
return fullPath;
}

private async Task<string> CreateReportSheetPdfChromium(string tempFolder, string htmlUri)
private async Task<string> CreateReportSheetPdfChromium(string tempFolder, string htmlUri, CancellationToken cancellationToken)
{
// Temporary file for the PDF stream form Chromium
// Temporary file for the PDF stream from Chromium
// Note: non-existing file is handled in MovePdfToCache
var pdfFile = Path.Combine(tempFolder, Path.GetRandomFileName() + ".pdf");

// Run Chromium
Expand All @@ -196,25 +197,21 @@ private async Task<string> CreateReportSheetPdfChromium(string tempFolder, strin
{ CreateNoWindow = true, UseShellExecute = false };
var proc = System.Diagnostics.Process.Start(startInfo);

if (proc is null)
if (proc == null)
{
_logger.LogError("Process '{PathToChromium}' could not be started.", _pathToChromium);
return pdfFile;
}

const int timeout = 8000;
var timePassed = 0;
while (proc is { HasExited: false })
{
timePassed += 100;
await Task.Delay(100, default);
if (timePassed < timeout) continue;
var timeout = TimeSpan.FromMilliseconds(5000);
var processTask = proc.WaitForExitAsync(cancellationToken);

proc.Kill(true);
throw new OperationCanceledException($"Chromium timed out after {timeout}ms.");
}
await Task.WhenAny(processTask, Task.Delay(timeout, cancellationToken));

if (processTask.IsCompleted) return pdfFile;

// non-existing file is handled in MovePdfToCache
return pdfFile;
proc.Kill(true);
throw new OperationCanceledException($"Chromium timed out after {timeout.TotalMilliseconds}ms.");
}

private static async Task<string> CreateHtmlFile(string html, string tempFolder, CancellationToken cancellationToken)
Expand Down
2 changes: 1 addition & 1 deletion League/Controllers/Match.cs
Original file line number Diff line number Diff line change
Expand Up @@ -714,7 +714,7 @@ private async Task<EnterResultViewModel> GetEnterResultViewModel(MatchEntity mat
public async Task<IActionResult> ReportSheet(long id, [FromServices] ReportSheetCache cache, CancellationToken cancellationToken)
{
MatchReportSheetRow? model = null;
cache.UsePuppeteer = true;
cache.UsePuppeteer = false;

try
{
Expand Down

0 comments on commit 65f19e7

Please sign in to comment.