Skip to content

Commit 8f9d107

Browse files
Cleaning up stale puppeteer pages
1 parent 4d22776 commit 8f9d107

File tree

1 file changed

+48
-18
lines changed

1 file changed

+48
-18
lines changed

apps/pwabuilder/Services/PuppeteerService.cs

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
using Azure;
1+
using System.Collections.Concurrent;
22
using PuppeteerSharp;
33
using PWABuilder.Common;
4-
using PWABuilder.Models;
54

65
namespace PWABuilder.Services
76
{
87
public class PuppeteerService : IPuppeteerService
98
{
109
private readonly Task<IBrowser> reusableBrowser;
1110
private readonly ILogger<PuppeteerService> logger;
11+
private readonly ConcurrentBag<PageReference> openPages = [];
1212

1313
public PuppeteerService(Task<IBrowser> reusableBrowser, ILogger<PuppeteerService> logger)
1414
{
@@ -101,24 +101,12 @@ public async Task<IPage> NavigateAsync(Uri site)
101101
{
102102
logger.LogWarning("There are {pageCount} pages open in the Puppeteer browser. Possible page leak detected.", pages.Length);
103103
}
104-
if (pages.Length > 100)
105-
{
106-
// Extreme case: close all pages to free up resources.
107-
logger.LogError("Too many pages ({pageCount}) open in the Puppeteer browser. Closing all pages to free up resources.", pages.Length);
108-
foreach (var openPage in pages)
109-
{
110-
try
111-
{
112-
await openPage.CloseAsync();
113-
}
114-
catch (Exception ex)
115-
{
116-
logger.LogError(ex, "Error closing page during cleanup.");
117-
}
118-
}
119-
}
104+
105+
// Close any pages that have been opened for more than an hour.
106+
await TryCleanupOldPages();
120107

121108
var page = await browser.NewPageAsync();
109+
this.openPages.Add(new PageReference(page, site));
122110

123111
// Disable performance monitoring to avoid Protocol error (Performance.enable) issues
124112
await page.SetCacheEnabledAsync(false);
@@ -154,5 +142,47 @@ public async ValueTask DisposeAsync()
154142
var browser = await this.reusableBrowser;
155143
await browser.DisposeAsync();
156144
}
145+
146+
private async Task TryCleanupOldPages()
147+
{
148+
var hourAgo = DateTimeOffset.UtcNow.AddHours(-1);
149+
foreach (var openPage in this.openPages)
150+
{
151+
if (openPage.CreatedAt < hourAgo)
152+
{
153+
logger.LogInformation("Closing stale Puppeteer page for {url} opened {time} ago.", openPage.Url, (DateTimeOffset.UtcNow - openPage.CreatedAt).ToString(@"hh\:mm"));
154+
openPage.Page.TryGetTarget(out var page);
155+
if (page != null)
156+
{
157+
openPages.TryTake(out var _);
158+
try
159+
{
160+
await page.CloseAsync();
161+
}
162+
catch (Exception closeError)
163+
{
164+
logger.LogWarning(closeError, "Error closing stale Puppeteer page for {url}.", openPage.Url);
165+
}
166+
}
167+
}
168+
}
169+
}
170+
}
171+
172+
/// <summary>
173+
/// An open page in Puppeteer. Uses a weak reference to allow for garbage collection. Used for cleaning up old pages that weren't properly disposed.
174+
/// </summary>
175+
public class PageReference
176+
{
177+
public WeakReference<IPage> Page { get; }
178+
public DateTimeOffset CreatedAt { get; }
179+
public Uri Url { get; }
180+
181+
public PageReference(IPage page, Uri url)
182+
{
183+
Page = new WeakReference<IPage>(page);
184+
CreatedAt = DateTimeOffset.UtcNow;
185+
Url = url;
186+
}
157187
}
158188
}

0 commit comments

Comments
 (0)