diff --git a/img/resource-timing-cache.png b/img/resource-timing-cache.png new file mode 100644 index 0000000..88376bf Binary files /dev/null and b/img/resource-timing-cache.png differ diff --git a/img/resource-timing-fetch.png b/img/resource-timing-fetch.png new file mode 100644 index 0000000..45db2dc Binary files /dev/null and b/img/resource-timing-fetch.png differ diff --git a/img/resource-timing-network.png b/img/resource-timing-network.png new file mode 100644 index 0000000..6722194 Binary files /dev/null and b/img/resource-timing-network.png differ diff --git a/img/resource-timing-overview.png b/img/resource-timing-overview.png new file mode 100644 index 0000000..f291eea Binary files /dev/null and b/img/resource-timing-overview.png differ diff --git a/img/resource-timing-race.png b/img/resource-timing-race.png new file mode 100644 index 0000000..830dacf Binary files /dev/null and b/img/resource-timing-race.png differ diff --git a/resource-timing-api.md b/resource-timing-api.md new file mode 100644 index 0000000..4651bce --- /dev/null +++ b/resource-timing-api.md @@ -0,0 +1,407 @@ +# Explainer for timing info for Static Routing API + +## Authors + +- Keita Suzuki ([@quasi-mod](https://github.com/quasi-mod)) +- Yoshisato Yanagisawa ([@yoshisatoyanagisawa](https://github.com/yoshisatoyanagisawa)) + + +## Participate + +* [Link to issue](https://github.com/WICG/service-worker-static-routing-api/issues/19) + +## Abstract + +* Introduce timing info for the ServiceWorker Static Routing API in Resource Timing API and Navigation Timing API. + + +## Background + + +### The ServiceWorker Static Routing API + +The startup of [ServiceWorkers](https://www.w3.org/TR/service-workers/), a web platform feature that brings application-like +experience to users, is known to be a slow process. If the ServiceWorker intercepts loading the page +resources, web users may need to wait for the startup to complete in order for the page loading to start. + +[Service Worker Static Routing API](https://github.com/WICG/service-worker-static-routing-api) was introduced to mitigate this issue by letting the +developers selectively choose whether the ServiceWorker should intercept the +navigation, and allow them to specify when to not run ServiceWorker. +In addition, it allows the developers to offload simple ServiceWorker operations +like cache look up. i.e. they can return resources from CacheStorage without running +ServiceWorkers. + +### Timing Info of ServiceWorkers + +Service Worker provides [timing information](https://w3c.github.io/ServiceWorker/#service-worker-timing) to mark certain points +in time. This is exposed and used by the [navigation timing API](https://w3c.github.io/navigation-timing/#dom-performancenavigationtiming) +as well as the [resource timing API](https://w3c.github.io/resource-timing/). It currently records two times: + +* Start time +* Fetch event dispatch time + +However, it currently does not have any fields related to the ServiceWorker Static +Routing API. Developers would benefit from having fields that provide information such as: + +* the matched route (the route that the Static Routing API evaluated) +* the actual source from which the resource was retrieved +* the time it took to match the route + +This information will allow developers to measure the latency incurred by the Static +Routing API such as router evaluation time or time required to conduct cache lookup, +or determine if the matched source is the final source used (can find out if the +matched source failed to get the resource or not, and which source was used as +the alternative). + +## Proposal + + + +We add the following two timing information: + +* workerRouterEvaluationStart + * A `DOMHighResTimeStamp`, initially 0 + * Time to start matching a request with registered router rules +* workerCacheLookupStart + * A `DOMHighResTimeStamp`, initially 0 + * Time to start looking up the cache storage if the source is "cache" + +In addition to the timestamp information, we also add the following two route source information: + +* matchedRouterSource + * A `RouterSource`, initially empty string + * The enum string of the matched source (the source of result of router evaluation) + * This shall match to "network", "cache", "fetch-event", or "race-network-and-fetch-handler". If no rule is matched, it shall be an empty string. +* finalRouterSource + * A `RouterSource`, initially empty string + * The enum string of the used source + * This shall match to "network", "cache", or "fetch-event" + * When a matched router source exists, this should match to the MatchedRouterSource, unless in "race-network-and-fetch-handler", where the winner of the race will be the final source (either "network" or "fetch-event"). Otherwise, it should remain as an empty string. + + +### Example code + +#### Fetch Rule Specified + + + +```js +// Add route inside ServiceWorker +addEventListener('install', (event) => { + event.addRoutes({ + condition: { + urlPattern: {pathname: "/form/*"} + }, + source: "fetch-event" + }); +}) +``` + +```js +// Measure routerEvaluationTime +let timing = window.performance.timing; +let routerEvaluationTime = 0.0; +switch (timing.finalRouteSource) { + case "network": + // Indicates that the fetch fallback to network. + routerEvaluationTime = timing.fetchStart - timing.workerRouterEvaluationStart; + break; + case 'fetch-event': + routerEvaluationTime = timing.workerStart - timing.workerRouterEvaluationStart; + break; + case "cache": + // UNREACHABLE + break; +} +``` + +#### Network Rule Specified + + + +```js +// Add route inside ServiceWorker +addEventListener('install', (event) => { + event.addRoutes({ + condition: { + urlPattern: {pathname: "/form/*"} + }, + source: "network" + }); +}) +``` + +```js +// Measure routerEvaluationTime +let timing = window.performance.timing; +let routerEvaluationTime = 0.0; +switch (timing.finalRouteSource) { + case "network": + // Routed to network + routerEvaluationTime = timing.fetchStart - timing.workerRouterEvaluationStart; + break; + case "cache": + case "fetch-event": + // UNREACHABLE + break; +} +``` + +#### Race Rule Specified + + + +```js +// Add route inside ServiceWorker +addEventListener('install', (event) => { + event.addRoutes({ + condition: { + urlPattern: {pathname: "/form/*"} + }, + source: "race-network-and-fetch-event" + }); +}) +``` + +```js +// Measure routerEvaluationTime +let timing = window.performance.timing; +let routerEvaluationTime = 0.0; +switch (timing.finalRouteSource) { + case "network": + // Indicates that the network has won the race, + // or the fetch event has failed. + routerEvaluationTime = timing.fetchStart - timing.workerRouterEvaluationStart; + break; + case 'fetch-event': + // Indicates that the fetch has won the race. + routerEvaluationTime = timing.workerStart - timing.workerRouterEvaluationStart; + break; + case "cache": + // UNREACHABLE + break; +} +``` + +#### Cache Rule Specified + + + +```js +// Add route inside ServiceWorker +addEventListener('install', (event) => { + event.addRoutes({ + condition: { + urlPattern: {pathname: "/form/*"} + }, + source: "cache" + }); +}) +``` + +```js +// Measure routerEvaluationTime and cacheLookupTime +let timing = window.performance.timing; +let routerEvaluationTime = 0.0; +let cacheLookupTime = 0.0; +switch (timing.FinalRouteSource) { + case "network": + // Cache miss. Fallback to network. + routerEvaluationTime = timing.cacheLookupStart - timing.routerEvaluationStart; + cacheLookupTime = time.fetchStart - time.workerCacheLookupStart; + break; + case "cache": + // Cache Hit. + routerEvaluationTime = + timing.cacheLookupStart - timing.workerRouterEvaluationStart; + cacheLookupTime = + time.responseStart - time.cacheLookupStart; + case "fetch-event": + // UNREACHABLE + break; +} +``` + +### Recorded timing per matched route + +As mentioned above, the recorded fields will be different depending on the matched source. The fields to be recorded per source is as follows (✔ indicates recorded, ✘ indicates not recorded). + + +
+ | +RouterEvaluationStart
+ |
+ CacheLookupStart
+ |
+ fetchStart
+ |
+ |
Matched Source + | +Fetch + | +✔ + | +✘ + | +✔ + | +
Network + | +✔ + | +✘ + | +✔ + | +|
Race (Network vs Fetch) + | +✔ + | +✘ + | +✔ + | +|
Cache + | +✔ + | +✔ + | +✘ + | +|
None Matched + | +✔ + | +✘ + | +✔ + | +
+ | +Actual Source + | +|||
Fetch + | +Network + | +Cache + | +||
Matched Source + | +Fetch + | +Fetch (Success) + | +Network + (Fallback: Fetch handler is invalid) + |
+ N/A + | +
Network + | +N/A + | +Network (Success) + | +N/A + | +|
Race (Network vs Fetch) + | +Fetch
+ +(Fetch win) + |
+ Network (Network win or Fetch fallback) + |
+ N/A + | +|
Cache + | +N/A + | +Network + (Fallback: Cache Missed) + |
+ Cache (Cache hit) + |
+ |
None matched + | +N/A (null) + | +N/A (null) + | +N/A + | +