From f281310d39b7ae662c01a4332b4fae9fc1065fa7 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Sat, 6 Mar 2021 16:30:42 +0200 Subject: [PATCH] fix(docs-infra): print info to help debugging SW cache issue (#41106) From time to time, an angular.io page fails to load due to requesting a file that cannot be found neither on the server nor in the cache. We believe this is caused by the browser's partially clearing a cache. See #28114 for more details. Some time ago, we introduced [SwUpdate#unrecoverable][1] to help work around this issue by [reloading the page][2] when such an error is detected. However, this issue still pops up occasionally (for example, #41073). In an attempt to help diagnose the issue, this commit prints more info regarding the SW state and cache content when this error occurs. It will result in something like the following being printed to the console: ``` ServiceWorker: activated Cache: ngsw:/:db:control (2 entries) - https://angular.io/assignments: {"f5f02035-ee1f-463c-946c-e8b85badca25":"5c95f89a85255a6fefb4045a20f751ef32b2f3a4"} - https://angular.io/latest: {"latest":"5c95f89a85255a6fefb4045a20f751ef32b2f3a4"} Cache: ngsw:/:5c95f89a85255a6fefb4045a20f751ef32b2f3a4:assets:app-shell:cache (24 entries) - https://angular.io/0-es2015.867022f8bb092ae1efb1.worker.js - https://angular.io/announcement-bar-announcement-bar-module-es2015.1b5b762c9c8837c770f8.js - https://angular.io/api-api-list-module-es2015.40a43cd22f50f64d63bb.js ... Cache: ngsw:/:db:ngsw:/:5c95f89a85255a6fefb4045a20f751ef32b2f3a4:assets:app-shell:meta (1 entries) - https://angular.io/https://fonts.gstatic.com/s/robotomono/v13/L0x5DF4xlVMF-BfR8bXMIjhLq3-cXbKD.woff2: {"ts":1615031956601,"used":true} If you see this error, please report an issue at https://github.com/angular/angular/issues/new?template=3-docs-bug.md including the above logs. ``` NOTE: This change increases the main bundle by 1649B (0.37%), but it can be reverted as soon as we gather enough info to diagnose the issue. [1]: https://angular.io/api/service-worker/SwUpdate#unrecoverable [2]: https://github.com/angular/angular/blob/c676ec1ce5d586d4bc46/aio/src/app/sw-updates/sw-updates.service.ts#L55-L61 PR Close #41106 --- .../layout/doc-viewer/doc-viewer.component.ts | 70 ++++++++++++++++++- goldens/size-tracking/aio-payloads.json | 8 +-- 2 files changed, 73 insertions(+), 5 deletions(-) diff --git a/aio/src/app/layout/doc-viewer/doc-viewer.component.ts b/aio/src/app/layout/doc-viewer/doc-viewer.component.ts index 5a5ed3f4d0..26c0528a84 100644 --- a/aio/src/app/layout/doc-viewer/doc-viewer.component.ts +++ b/aio/src/app/layout/doc-viewer/doc-viewer.component.ts @@ -143,10 +143,18 @@ export class DocViewerComponent implements OnDestroy { switchMap(() => this.swapViews(addTitleAndToc)), tap(() => this.docRendered.emit()), catchError(err => { - const errorMessage = (err instanceof Error) ? err.stack : err; + const errorMessage = `${(err instanceof Error) ? err.stack : err}`; this.logger.error(new Error(`[DocViewer] Error preparing document '${doc.id}': ${errorMessage}`)); this.nextViewContainer.innerHTML = ''; this.setNoIndex(true); + + // TODO(gkalpak): Remove this once gathering debug info is no longer needed. + if (/loading chunk \d+ failed/i.test(errorMessage)) { + // Print some info to help with debugging. + // (There is no reason to wait for this async call to complete before continuing.) + printSwDebugInfo(); + } + return this.void$; }), ); @@ -244,3 +252,63 @@ export class DocViewerComponent implements OnDestroy { ); } } + +// Helpers +/** + * Print some info regarding the ServiceWorker and the caches contents to help debugging potential + * issues with failing to find resources in the cache. + * (See https://github.com/angular/angular/issues/28114.) + */ +async function printSwDebugInfo(): Promise { + console.log(`\nServiceWorker: ${navigator.serviceWorker?.controller?.state ?? 'N/A'}`); + + if (typeof caches === 'undefined') { + console.log('\nCaches: N/A'); + } else { + const allCacheNames = await caches.keys(); + const swCacheNames = allCacheNames.filter(name => name.startsWith('ngsw:/:')); + + await findCachesAndPrintEntries(swCacheNames, 'db:control', true, ['manifests']); + await findCachesAndPrintEntries(swCacheNames, 'assets:app-shell:cache', false); + await findCachesAndPrintEntries(swCacheNames, 'assets:app-shell:meta', true); + } + + console.warn( + '\nIf you see this error, please report an issue at ' + + 'https://github.com/angular/angular/issues/new?template=3-docs-bug.md including the above logs.'); + + // Internal helpers + async function findCachesAndPrintEntries( + swCacheNames: string[], nameSuffix: string, includeValues: boolean, + ignoredKeys: string[] = []): Promise { + const cacheNames = swCacheNames.filter(name => name.endsWith(nameSuffix)); + + for (const cacheName of cacheNames) { + const cacheEntries = await getCacheEntries(cacheName, includeValues, ignoredKeys); + await printCacheEntries(cacheName, cacheEntries); + } + } + + async function getCacheEntries( + name: string, includeValues: boolean, + ignoredKeys: string[] = []): Promise<{key: string, value?: object}[]> { + const ignoredUrls = new Set(ignoredKeys.map(key => new Request(key).url)); + + const cache = await caches.open(name); + const keys = (await cache.keys()).map(req => req.url).filter(url => !ignoredUrls.has(url)); + const entries = await Promise.all(keys.map(async key => ({ + key, + value: !includeValues ? undefined : await (await cache.match(key))?.json(), + }))); + + return entries; + } + + function printCacheEntries(name: string, entries: {key: string, value?: object}[]): void { + const entriesStr = entries + .map(({key, value}) => ` - ${key}${!value ? '' : `: ${JSON.stringify(value)}`}`) + .join('\n'); + + console.log(`\nCache: ${name} (${entries.length} entries)\n${entriesStr}`); + } +} diff --git a/goldens/size-tracking/aio-payloads.json b/goldens/size-tracking/aio-payloads.json index fef0112d62..18cd9550d2 100755 --- a/goldens/size-tracking/aio-payloads.json +++ b/goldens/size-tracking/aio-payloads.json @@ -3,7 +3,7 @@ "master": { "uncompressed": { "runtime-es2015": 3033, - "main-es2015": 449310, + "main-es2015": 450953, "polyfills-es2015": 52343 } } @@ -12,7 +12,7 @@ "master": { "uncompressed": { "runtime-es2015": 3033, - "main-es2015": 449759, + "main-es2015": 451402, "polyfills-es2015": 52493 } } @@ -21,9 +21,9 @@ "master": { "uncompressed": { "runtime-es2015": 3153, - "main-es2015": 435362, + "main-es2015": 437005, "polyfills-es2015": 52493 } } } -} \ No newline at end of file +}