fix(trace): survive sw restart (#37442)
This commit is contained in:
parent
a008e126c0
commit
25f89ac4d2
|
|
@ -4802,6 +4802,12 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/idb-keyval": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz",
|
||||
"integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
||||
|
|
@ -8377,6 +8383,7 @@
|
|||
"packages/trace-viewer": {
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"idb-keyval": "^6.2.2",
|
||||
"yaml": "^2.6.0"
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"idb-keyval": "^6.2.2",
|
||||
"yaml": "^2.6.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as idbKeyval from 'idb-keyval';
|
||||
|
||||
import { splitProgress } from './progress';
|
||||
import { unwrapPopoutUrl } from './snapshotRenderer';
|
||||
import { SnapshotServer } from './snapshotServer';
|
||||
|
|
@ -33,11 +35,14 @@ self.addEventListener('activate', function(event: any) {
|
|||
});
|
||||
|
||||
const scopePath = new URL(self.registration.scope).pathname;
|
||||
|
||||
const loadedTraces = new Map<string, { traceModel: TraceModel, snapshotServer: SnapshotServer }>();
|
||||
|
||||
const clientIdToTraceUrls = new Map<string, { limit: number | undefined, traceUrls: Set<string>, traceViewerServer: TraceViewerServer }>();
|
||||
|
||||
function simulateServiceWorkerRestart() {
|
||||
loadedTraces.clear();
|
||||
clientIdToTraceUrls.clear();
|
||||
}
|
||||
|
||||
async function loadTrace(traceUrl: string, traceFileName: string | null, client: any | undefined, limit: number | undefined, progress: (done: number, total: number) => undefined): Promise<TraceModel> {
|
||||
await gc();
|
||||
const clientId = client?.id ?? '';
|
||||
|
|
@ -49,6 +54,7 @@ async function loadTrace(traceUrl: string, traceFileName: string | null, client:
|
|||
clientIdToTraceUrls.set(clientId, data);
|
||||
}
|
||||
data.traceUrls.add(traceUrl);
|
||||
await saveClientIdParams();
|
||||
|
||||
const traceModel = new TraceModel();
|
||||
try {
|
||||
|
|
@ -101,6 +107,10 @@ async function doFetch(event: FetchEvent): Promise<Response> {
|
|||
await gc();
|
||||
return new Response(null, { status: 200 });
|
||||
}
|
||||
if (relativePath === '/restartServiceWorker') {
|
||||
simulateServiceWorkerRestart();
|
||||
return new Response(null, { status: 200 });
|
||||
}
|
||||
|
||||
const traceUrl = url.searchParams.get('trace');
|
||||
|
||||
|
|
@ -122,6 +132,16 @@ async function doFetch(event: FetchEvent): Promise<Response> {
|
|||
}
|
||||
}
|
||||
|
||||
if (!clientIdToTraceUrls.has(event.clientId)) {
|
||||
// Service worker was restarted upon subresource fetch.
|
||||
// It was stopped because ping did not keep it alive since the tab itself was throttled.
|
||||
const params = await loadClientIdParams(event.clientId);
|
||||
if (params) {
|
||||
for (const traceUrl of params.traceUrls)
|
||||
await loadTrace(traceUrl, null, client, params.limit, () => {});
|
||||
}
|
||||
}
|
||||
|
||||
if (relativePath.startsWith('/snapshotInfo/')) {
|
||||
const { snapshotServer } = loadedTraces.get(traceUrl!) || {};
|
||||
if (!snapshotServer)
|
||||
|
|
@ -221,6 +241,36 @@ async function gc() {
|
|||
if (!usedTraces.has(traceUrl))
|
||||
loadedTraces.delete(traceUrl);
|
||||
}
|
||||
|
||||
await saveClientIdParams();
|
||||
}
|
||||
|
||||
// Persist clientIdToTraceUrls to localStorage to avoid losing it when the service worker is restarted.
|
||||
async function saveClientIdParams() {
|
||||
const serialized: Record<string, {
|
||||
limit: number | undefined,
|
||||
traceUrls: string[]
|
||||
}> = {};
|
||||
for (const [clientId, data] of clientIdToTraceUrls) {
|
||||
serialized[clientId] = {
|
||||
limit: data.limit,
|
||||
traceUrls: [...data.traceUrls]
|
||||
};
|
||||
}
|
||||
|
||||
const newValue = JSON.stringify(serialized);
|
||||
const oldValue = await idbKeyval.get('clientIdToTraceUrls');
|
||||
if (newValue === oldValue)
|
||||
return;
|
||||
idbKeyval.set('clientIdToTraceUrls', newValue);
|
||||
}
|
||||
|
||||
async function loadClientIdParams(clientId: string): Promise<{ limit: number | undefined, traceUrls: string[] } | undefined> {
|
||||
const serialized = await idbKeyval.get('clientIdToTraceUrls') as string | undefined;
|
||||
if (!serialized)
|
||||
return;
|
||||
const deserialized = JSON.parse(serialized);
|
||||
return deserialized[clientId];
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
|
|
|
|||
|
|
@ -2021,3 +2021,22 @@ test.describe(() => {
|
|||
await expect(frame.getByRole('button')).toHaveCSS('color', 'rgb(255, 0, 0)');
|
||||
});
|
||||
});
|
||||
|
||||
test('should survive service worker restart', async ({ page, runAndTrace, server }) => {
|
||||
const traceViewer = await runAndTrace(async () => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.setContent('Old world');
|
||||
await page.evaluate(() => document.body.textContent = 'New world');
|
||||
});
|
||||
const snapshot1 = await traceViewer.snapshotFrame('Evaluate');
|
||||
await expect(snapshot1.locator('body')).toHaveText('New world');
|
||||
|
||||
const status = await traceViewer.page.evaluate(async () => {
|
||||
const response = await fetch('restartServiceWorker');
|
||||
return response.status;
|
||||
});
|
||||
expect(status).toBe(200);
|
||||
|
||||
const snapshot2 = await traceViewer.snapshotFrame('Set content');
|
||||
await expect(snapshot2.locator('body')).toHaveText('Old world');
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue