kiwix-js icon indicating copy to clipboard operation
kiwix-js copied to clipboard

Prevent redundant extraction of assets after navigation

Open Jaifroid opened this issue 5 years ago • 7 comments

Kiwix JS continues to extract assets for a given page (CSS, images, JavaScript) that have been queued up for extraction even after the user has navigated away from that page. This issue affects both jQuery and Service Worker modes. On short and simple pages, extraction is now so quick that it is not noticeable. But on longer pages with hundreds of images, and especially on pages with SVG images that are heavily compressed, it can significantly delay either the display of the next page or the extraction of assets from it. The problem is noticeable particularly when using Kiwix JS with, e.g., wikipedia_en_maths_novid_2018-06.zim where almost every article has many SVG images of equations.

Jaifroid avatar Sep 27 '18 19:09 Jaifroid

[ Comment moved from #425 ]

I've just done a quick test on Edge and Firefox, with a couple of console log entries in zimarchive.js to log the files extracted. Basically, if we've sent images to be extracted or decompressed in loadImagesJQuery() or have sent them to the backend in SW, the engine will continue to work on them, even after the next page is loaded. We don't have a "stop" mechanism (and maybe it would be good to put one in).

The test was done on master (not on the spinner branch) by loading full English Wikipedia, letting the landing page settle, then searching for "Paris". After a few seconds of letting "Paris" load, I quickly press the Home button to take me back to the landing page. You can see the console log entries below (in Edge, but exactly the same in FF). I've highlighted where the html of each page was extracted. Visually, the landing page was visible in the browser, while in the console log the images from the Paris page continued to be extracted for several seconds. (Having Console open usefully slows things down in this case!) I need to do more tests in SW mode, because there are so many console messages it's a little hard to pin things down, but I've just reproduced the issue in FF a few times.

In Kiwix JS Windows, I use "lazy loading" of images: the visible area plus the next x images are loaded, then no more, so the issue is minimized (JQuery only, since mobile doesn't support SW). But it would be good to work out a "stop" signal to send to the backend?

image

Jaifroid avatar Sep 27 '18 19:09 Jaifroid

[ Comment moved from #425 ]

I pushed a branch to test the issue of redundant image calls after navigation. The branch is:

https://github.com/kiwix/kiwix-js/tree/Test-redundant-image-loads-after-navigation

(It removes most of the console log spam for Service Worker and just leaves simple "Extracted …." messages as above, and it adds in the simple cssCache (our current memory cache) to SW only for the purpose of clearly testing the issue on the same basis as jQuery.)

Quick test results in Service Worker mode (I tested with Trigonometric Functions, because it has so many SVGs that are slowish to extract, but it's the same test as the one with Paris above):

Chromium seems to do things properly. It aborts all image/SVG loads almost immediately that I press the Home button (after I've let Trig Functions load for a second or two).

Firefox Quantum continues to extract a large number of SVGs from Trig Functions after I press the Home button, and this delays the extraction of the HTML for the landing page. Once the HTML for the landing page is finally extracted, the SVG image loads stop, unlike in jQuery mode (where they continue after the page is visible).

Edge acts in almost exactly the same way as Firefox Q (Edge is very slow with the console open, so the issue seems worse, but in fact it's about the same number of redundant SVGs extracted).

My speculation as to the differences here (remember, this is SW mode): it's not that Chromium sends some magical stop signal to the extraction engine, it's that Chromium seems to enact a "lazy loading" strategy of its own for items off-screen. It seems to wait for a response on such items before requesting the next one, or does it in small batches. Hence, it hasn't queued the engine up with requests, and is able to abort straight away. This fits with my observation that sometimes, in SW mode, when loading a page with hundreds of SVGs, in Chromium I can scroll down to an area not yet loaded, and it will instantly load that area. In other words, it prioritizes the visible area. Edge and Firefox don't do this, so they send all the image requests they can to the engine, or at least a lot more than Chromium. This blocks extraction of the next page's HTML, which gets extracted only after all images on the queue are extracted (not necessarily all images in the page, though). I'm pretty sure this is what's going on!

I think there is clearly an issue for JQuery mode. I'm not sure about SW mode: it could be better in Edge and FF. But it ought to be possible to send a "reset" signal from the Service Worker, if "prefetch" is not a real thing we have to worry about?

Jaifroid avatar Sep 27 '18 19:09 Jaifroid

[ Comment moved from #425 ]

And, I've just caught FF acting the same way in SW mode as it does in jQuery, so I think I can confirm an issue in SW mode too. I've highlighted where FF extracted the HTML for the landing page. The request is many lines further up (not shown here), so in this case, FF asked for the landing page, continued to extract SVGs from Trig. Functions, then got the HTML (and displayed it, because the CSS is coming from the cache), and continue to extract many more SVGs from Trig. Functions (there are only 2 SVGs on the landing page):

image

Jaifroid avatar Sep 27 '18 19:09 Jaifroid

I was wondering whether this behaviour might be a regression caused by #398 (No parallel decompression), in particular by the setTimeout we use in xzdec_wrapper. However, using a debugger on the readSliceSingleThread function suggests that it is not touched when extracting JPEGs, PNGs, etc., and this behaviour occurs equally with those filetypes as with SVGs. So I believe the problem is not there.

Jaifroid avatar Sep 28 '18 10:09 Jaifroid

I believe implementing a kill switch would require us to mark every dirEntry as belonging to a calling html article, so they could be filtered out before engaging the blob reader (or return an empty Uint8Array) if a different html article has been requested. Or does Q have a global abort / reset mechanism for queued-up promises?

Jaifroid avatar Sep 28 '18 11:09 Jaifroid

I've got nowhere with a viable kill switch. Once promises for images have been queued with Q, there is no clean way to stop them from reading the blobs, and due to the structure that zimfile is used to read blobs, and zimDirEntry is used for dirEntries, there seems to be no way to mark a requested blob as having been called by a specific dirEntry at the moment that it is due to be read, which can be long after other dirEntries are being processed. The association must still be somewhere, but possibly due to Require, I've drawn a blank on accessing it (or even actively persisting it). Maybe there's a simple solution beyond my grasp!

The only other solution to this issue that I can think of could be something like https://github.com/verlok/lazyload . It has an AMD module for RequireJS. It's a natural fit for the way Service Worker loads images. However, the script would be altering the DOM to hide all off-screen images and load visible ones after a scroll event. It may also be overkill to use a library script for this.

Jaifroid avatar Oct 10 '18 12:10 Jaifroid

I don't think this issue is a high priority. As you said, it's probably complicated to solve for a small benefit. And, if we're lucky, optimizations like #116 might make the issue less noticeable.

mossroy avatar Oct 14 '18 11:10 mossroy