LinkedIn Lite: A Server-Side Rendered PWA

Here’s a 50,000-foot view of the logic. On the unload of the page, the entire document is sent to the Service Worker, which caches it into the primary/hot cache against the page URL. If there’s a request for the same URL, the Service Worker responds back with the cached version of the page and makes a real request to fetch a fresh copy. The response is cached into a secondary/network cache with a very short timeout.

However, the actual implementation is a bit more detailed.

In a real-world application, there is always more than one canonical URL for a page, and LinkedIn Lite is no different. So, during the build time, all of these canonical URLs are grouped together under a common key (controller.method in our case) as a JavaScript file and bundled along with the Service Worker. For an incoming request, the Service Worker checks the URL against the key, and if a match is found, returns the response. For example, “/,” “/hp/,” and “/nhome/” are all mapped to a key FeedController.returnFeeds.

If there’s a cache miss, the Service Worker makes a request to the server and stores the content against its primary cache database. When the member navigates away from a page and subsequently returns back (either using the back button or nav bar), their browsing context and scroll position needs to be maintained. To maintain the context of the page, instead of keeping a single cache that gets overwritten on every subsequent request to the page, we maintain two caches, e.g., a primary/hot cache and a secondary/network cache. The former is always used to serve a request and preserve the context for the member, while the latter has the fresh content from the server and is returned when the member does a pull to refresh or if the primary cache goes out of date (30 mins.).

Caching strategy
The Lite Service Worker acts as a forward proxy/cache. It doesn’t know any specific details about the application, particularly regarding the static and the dynamic parts of the content. For this reason, it caches the entire page (as HTML) against the URL/cache key and responds back with the same to the browser when requested.

As with any forward proxy, the Service Worker doesn’t cache any errors, because the error status might be temporary from when the application was down or busy; only successful responses (status code 2xx) are cached. Adhering to HTTP RFC, the Service Worker caches only GET requests because POST and PUT requests are forbidden to be cached. In addition to this, any route specially annotated (@pwa noCache in our case) does not belong to the URL mapping, and hence are also not cached.

The only application-specific logic that the Service Worker has is to purge all the cache databases on any “edit” route. This design choice was made because the Service Worker neither maintains any application state nor knows the guts of the application.

If a member reloaded the page or pulled to refresh, the browser sets a “reload” flag as part of the Service Worker fetch event (event.isReload is true) and in such a scenario, the Service Worker bypasses the primary cache and responds from the secondary network cache or from the server if there’s a cache miss.

The contents of the secondary network cache are never swapped or moved to the primary cache or vice versa. Both the caches stay independent of each other. However, when there’s a page navigation either using the browser buttons (back/forward) or through the app nav bar/links, the entire page is saved to the primary cache database. Only in this case —when the content was served from the network cache—does get stored into the primary cache. This is because, on page unload, the entire content is always saved to the primary cache.

Source link