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
This commit is contained in:
George Kalpakas 2021-03-06 16:30:42 +02:00 committed by Andrew Kushnir
parent 7a27cd262f
commit f281310d39
2 changed files with 73 additions and 5 deletions

View File

@ -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<void> {
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<void> {
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}`);
}
}

View File

@ -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
}
}
}
}
}