Bug 204147 - Handle page closure for stale-while-revalidate revalidations
Summary: Handle page closure for stale-while-revalidate revalidations
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: WebCore Misc. (show other bugs)
Version: Other
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Rob Buis
URL:
Keywords: InRadar
Depends on:
Blocks: 201461
  Show dependency treegraph
 
Reported: 2019-11-13 01:19 PST by Rob Buis
Modified: 2020-03-19 08:15 PDT (History)
9 users (show)

See Also:


Attachments
Patch (8.76 KB, patch)
2019-11-22 05:46 PST, Rob Buis
no flags Details | Formatted Diff | Diff
Patch (11.47 KB, patch)
2019-11-26 09:20 PST, Rob Buis
no flags Details | Formatted Diff | Diff
Patch (11.14 KB, patch)
2019-11-26 11:23 PST, Rob Buis
no flags Details | Formatted Diff | Diff
Patch (15.10 KB, patch)
2019-12-03 04:38 PST, Rob Buis
no flags Details | Formatted Diff | Diff
Patch (15.10 KB, patch)
2019-12-03 06:15 PST, Rob Buis
no flags Details | Formatted Diff | Diff
Patch (15.44 KB, patch)
2019-12-13 01:51 PST, Rob Buis
no flags Details | Formatted Diff | Diff
Patch (19.99 KB, patch)
2020-01-06 07:19 PST, Rob Buis
no flags Details | Formatted Diff | Diff
Patch (29.39 KB, patch)
2020-01-09 06:53 PST, Rob Buis
no flags Details | Formatted Diff | Diff
Patch (29.42 KB, patch)
2020-01-10 00:01 PST, Rob Buis
no flags Details | Formatted Diff | Diff
Patch (29.72 KB, patch)
2020-01-23 02:30 PST, Rob Buis
no flags Details | Formatted Diff | Diff
Patch (29.71 KB, patch)
2020-01-23 05:29 PST, Rob Buis
no flags Details | Formatted Diff | Diff
Patch (29.72 KB, patch)
2020-01-23 07:10 PST, Rob Buis
no flags Details | Formatted Diff | Diff
Patch (29.88 KB, patch)
2020-01-24 00:24 PST, Rob Buis
no flags Details | Formatted Diff | Diff
Patch (28.97 KB, patch)
2020-01-27 00:44 PST, Rob Buis
no flags Details | Formatted Diff | Diff
Patch (29.71 KB, patch)
2020-01-27 01:10 PST, Rob Buis
no flags Details | Formatted Diff | Diff
Patch (29.60 KB, patch)
2020-02-07 00:41 PST, Rob Buis
no flags Details | Formatted Diff | Diff
Patch (29.67 KB, patch)
2020-02-24 06:02 PST, Rob Buis
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Rob Buis 2019-11-13 01:19:42 PST
See https://bugs.webkit.org/show_bug.cgi?id=201461#c10.
Comment 1 Rob Buis 2019-11-22 05:46:20 PST
Created attachment 384145 [details]
Patch
Comment 2 Rob Buis 2019-11-26 09:20:22 PST
Created attachment 384361 [details]
Patch
Comment 3 Rob Buis 2019-11-26 11:23:15 PST
Created attachment 384367 [details]
Patch
Comment 4 Rob Buis 2019-12-03 04:38:42 PST
Created attachment 384705 [details]
Patch
Comment 5 Rob Buis 2019-12-03 06:15:12 PST
Created attachment 384711 [details]
Patch
Comment 6 Rob Buis 2019-12-04 01:22:45 PST
Comment on attachment 384711 [details]
Patch

This still needs a test but I think it can be reviewed to check if it is the right direction. Some remarks:

- I picked stopAllLoaders to catch both page close and starting a new navigation within a page.
- I do not like adding another HashMap but I think it is hard to avoid.
- The cleanup only gets triggered for main frames.
Comment 7 youenn fablet 2019-12-12 00:36:48 PST
Comment on attachment 384711 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=384711&action=review

> Source/WebCore/loader/FrameLoader.cpp:1844
> +        platformStrategies()->loaderStrategy()->pageContextRemoved(*m_frame.page());

We might actually want to send the message for all contexts since we want to cancel the revalidation when a context is going away and not when its top level context is going away.
We may thus need to send pageID/frameID and rename pageContextRemoved.

> Source/WebKit/NetworkProcess/cache/NetworkCache.cpp:339
> +    m_pendingAsyncRevalidationByPage.remove(webPageID);

I do not see here where we are cancelling the async revalidation of the page.

> Source/WebKit/NetworkProcess/cache/NetworkCache.h:204
> +    HashMap<WebCore::PageIdentifier, std::unique_ptr<Vector<Key>>> m_pendingAsyncRevalidationByPage;

Why is it a unique_ptr<Vector> and not a Vector?
Comment 8 Rob Buis 2019-12-13 01:51:15 PST
Created attachment 385586 [details]
Patch
Comment 9 Rob Buis 2019-12-13 02:58:35 PST
Comment on attachment 384711 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=384711&action=review

I started on the test but maybe you can give some clues, it seems really hard to verify that the right thing is done. Even internal testing API seems difficult since we need to verify things in the network process?

>> Source/WebCore/loader/FrameLoader.cpp:1844
>> +        platformStrategies()->loaderStrategy()->pageContextRemoved(*m_frame.page());
> 
> We might actually want to send the message for all contexts since we want to cancel the revalidation when a context is going away and not when its top level context is going away.
> We may thus need to send pageID/frameID and rename pageContextRemoved.

Ok, I initially did it this way to reduce the IPC since I was not sure how intrusive it would be. I now renamed it to browserContextRemoved.

>> Source/WebKit/NetworkProcess/cache/NetworkCache.cpp:339
>> +    m_pendingAsyncRevalidationByPage.remove(webPageID);
> 
> I do not see here where we are cancelling the async revalidation of the page.

Oops you are right, should be fixed now.

>> Source/WebKit/NetworkProcess/cache/NetworkCache.h:204
>> +    HashMap<WebCore::PageIdentifier, std::unique_ptr<Vector<Key>>> m_pendingAsyncRevalidationByPage;
> 
> Why is it a unique_ptr<Vector> and not a Vector?

No real reason, now Use just Vector.
Comment 10 youenn fablet 2019-12-15 23:50:14 PST
Comment on attachment 385586 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=385586&action=review

> Source/WebKit/NetworkProcess/cache/NetworkCache.cpp:341
> +        m_pendingAsyncRevalidations.remove(key);

It seems we take all keys of the page while we should only take the keys that match the frame ID and remove the corresponding revalidations.
Comment 11 youenn fablet 2019-12-15 23:56:50 PST
(In reply to Rob Buis from comment #9)
> Comment on attachment 384711 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=384711&action=review
> 
> I started on the test but maybe you can give some clues, it seems really
> hard to verify that the right thing is done. Even internal testing API seems
> difficult since we need to verify things in the network process?

One possibility would be to write a python script that is hanging for 0.5 seconds in case this is a stale revalidation.
We could first load an iframe to get the resource.
We could then load a new iframe to get the same resource, it should trigger revalidation. Revalidation will be hanging.
We could then remove the iframe, this should remove the ongoing revalidation.
We could then load a new iframe to get the same resource, it should probably get the cached resource and trigger a revalidation instead of reusing the cancelled revalidation (that would still be hanging without the patch).

It might be good to load in parallel another iframe with a different URL that triggers revalidation so that we can check that one frame revalidation is cancelled but not all.
Comment 12 Rob Buis 2020-01-06 07:19:16 PST
Created attachment 386844 [details]
Patch
Comment 13 Rob Buis 2020-01-09 06:53:05 PST
Created attachment 387221 [details]
Patch
Comment 14 Rob Buis 2020-01-10 00:01:42 PST
Created attachment 387317 [details]
Patch
Comment 15 Rob Buis 2020-01-10 06:00:24 PST
(In reply to youenn fablet from comment #11)
> (In reply to Rob Buis from comment #9)
> > Comment on attachment 384711 [details]
> > Patch
> > 
> > View in context:
> > https://bugs.webkit.org/attachment.cgi?id=384711&action=review
> > 
> > I started on the test but maybe you can give some clues, it seems really
> > hard to verify that the right thing is done. Even internal testing API seems
> > difficult since we need to verify things in the network process?
> 
> One possibility would be to write a python script that is hanging for 0.5
> seconds in case this is a stale revalidation.

It seems not so easy to detect stale revalidations. One way I found that worked is responding with ETag and then the stale revalidation request will contain If-None-Match. However the memory cache can also send out If-None-Match so maybe we need to skip memory cache for s-w-r responses first:
https://bugs.webkit.org/show_bug.cgi?id=205992

> We could first load an iframe to get the resource.
> We could then load a new iframe to get the same resource, it should trigger
> revalidation. Revalidation will be hanging.
> We could then remove the iframe, this should remove the ongoing revalidation.
> We could then load a new iframe to get the same resource, it should probably
> get the cached resource and trigger a revalidation instead of reusing the
> cancelled revalidation (that would still be hanging without the patch).

I tried to add that test case to this patch. Although I think the memory cache issue maybe needs to be fixed first (https://bugs.webkit.org/show_bug.cgi?id=205992), I think this patch can be reviewed again.
Comment 16 youenn fablet 2020-01-22 07:45:42 PST
Comment on attachment 387317 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=387317&action=review

> Source/WebKit/NetworkProcess/cache/NetworkCache.cpp:329
> +            keys.removeFirst(key);

keys will probably be a new vector copied from m_pendingAsyncRevalidationByPage owned vector.
So removing might not work here.
Can you add some ASSERTS as well so that we are sure 'keys' contains key for instance.

Also, why not simplify things and have something like a Map<GlobalID, WeakHashSet<AsyncRevalidation>>.
This would allow to write something like:
auto loaders = m_pendingAsyncRevalidationByPage.take(globalID);
for (auto& loader : loaders)
    loader->cancel();

> Source/WebKit/NetworkProcess/cache/NetworkCache.cpp:340
> +    for (auto key : keys) {

s/auto/auto&/

> Source/WebKit/NetworkProcess/cache/NetworkCache.cpp:342
> +        if (revalidation)

if (auto revalidation = ...)
Should we use take instead of get here as well, or cancel will do it automatically?
Comment 17 Rob Buis 2020-01-23 02:30:58 PST
Created attachment 388529 [details]
Patch
Comment 18 Rob Buis 2020-01-23 05:29:55 PST
Created attachment 388536 [details]
Patch
Comment 19 Rob Buis 2020-01-23 07:10:40 PST
Created attachment 388540 [details]
Patch
Comment 20 Rob Buis 2020-01-24 00:24:08 PST
Created attachment 388659 [details]
Patch
Comment 21 Rob Buis 2020-01-24 01:58:58 PST
Comment on attachment 387317 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=387317&action=review

>> Source/WebKit/NetworkProcess/cache/NetworkCache.cpp:329
>> +            keys.removeFirst(key);
> 
> keys will probably be a new vector copied from m_pendingAsyncRevalidationByPage owned vector.
> So removing might not work here.
> Can you add some ASSERTS as well so that we are sure 'keys' contains key for instance.
> 
> Also, why not simplify things and have something like a Map<GlobalID, WeakHashSet<AsyncRevalidation>>.
> This would allow to write something like:
> auto loaders = m_pendingAsyncRevalidationByPage.take(globalID);
> for (auto& loader : loaders)
>     loader->cancel();

Great idea, things get simpler when using WeakHashSet.

>> Source/WebKit/NetworkProcess/cache/NetworkCache.cpp:340
>> +    for (auto key : keys) {
> 
> s/auto/auto&/

Done.

>> Source/WebKit/NetworkProcess/cache/NetworkCache.cpp:342
>> +        if (revalidation)
> 
> if (auto revalidation = ...)
> Should we use take instead of get here as well, or cancel will do it automatically?

I changed the logic so cancel will do it automatically by calling the completion handler.
Comment 22 youenn fablet 2020-01-24 06:45:35 PST
Comment on attachment 388659 [details]
Patch

Seems almost ready to me, I have just one question with regards to the new CachedResourceLoader check.

View in context: https://bugs.webkit.org/attachment.cgi?id=388659&action=review

> Source/WebCore/loader/cache/CachedResourceLoader.cpp:1173
> +    if (existingResource->response().cacheControlStaleWhileRevalidate()) {

The comment does not match the check, which is about checking for cache control stale while revalidate.
Ditto for the message.
Also, I am not sure we need this check.
It seems that we should reload only if the resource is expired, otherwise we should be able to use it.
Can you clarify?

> Source/WebKit/NetworkProcess/cache/NetworkCache.cpp:325
> +        auto revalidation = makeUnique<AsyncRevalidation>(*this, frameID, request, WTFMove(entry), [this, key](AsyncRevalidation::Result result) {

s/AsyncRevalidation::Result/auto

> Source/WebKit/NetworkProcess/cache/NetworkCache.h:236
> +    HashMap<GlobalFrameID, WTF::WeakHashSet<AsyncRevalidation>> m_pendingAsyncRevalidationByPage;

Do we need WTF::?
Comment 23 Rob Buis 2020-01-27 00:44:19 PST
Created attachment 388838 [details]
Patch
Comment 24 Rob Buis 2020-01-27 01:10:56 PST
Created attachment 388839 [details]
Patch
Comment 25 Rob Buis 2020-01-27 02:57:29 PST
Comment on attachment 388659 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=388659&action=review

>> Source/WebCore/loader/cache/CachedResourceLoader.cpp:1173
>> +    if (existingResource->response().cacheControlStaleWhileRevalidate()) {
> 
> The comment does not match the check, which is about checking for cache control stale while revalidate.
> Ditto for the message.
> Also, I am not sure we need this check.
> It seems that we should reload only if the resource is expired, otherwise we should be able to use it.
> Can you clarify?

Sorry, that was a copy pasta mistake.
The problem is that frame-removal.htmlt needs to be able to identify stale revalidations to check that the cancel is performed. The standard revalidate for the memory cache sends the same headers, so I propose for stale memory resources with s-w-r headers we leave it up to the network process to handle the revalidate process, and then the test will work fine, but will fail if the revalidation cancel is not done, as the test should.

>> Source/WebKit/NetworkProcess/cache/NetworkCache.cpp:325
>> +        auto revalidation = makeUnique<AsyncRevalidation>(*this, frameID, request, WTFMove(entry), [this, key](AsyncRevalidation::Result result) {
> 
> s/AsyncRevalidation::Result/auto

Done.

>> Source/WebKit/NetworkProcess/cache/NetworkCache.h:236
>> +    HashMap<GlobalFrameID, WTF::WeakHashSet<AsyncRevalidation>> m_pendingAsyncRevalidationByPage;
> 
> Do we need WTF::?

No, fixed.
Comment 26 youenn fablet 2020-02-07 00:35:43 PST
Comment on attachment 388839 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=388839&action=review

> Source/WebKit/NetworkProcess/cache/NetworkCache.cpp:341
> +        fprintf(stderr, "browsingContextRemoved cancel!!!\n");

Remove fprintf.

> Source/WebKit/NetworkProcess/cache/NetworkCache.h:76
> +};

We normally do not add ; for namespaces.
We could add // NetworkCache comments as well.
Comment 27 Rob Buis 2020-02-07 00:41:10 PST
Created attachment 390061 [details]
Patch
Comment 28 Rob Buis 2020-02-07 00:42:48 PST
Comment on attachment 388839 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=388839&action=review

>> Source/WebKit/NetworkProcess/cache/NetworkCache.cpp:341
>> +        fprintf(stderr, "browsingContextRemoved cancel!!!\n");
> 
> Remove fprintf.

Ouch! Maybe there should be a pre upload warning hook to protect me :) Fixed.

>> Source/WebKit/NetworkProcess/cache/NetworkCache.h:76
>> +};
> 
> We normally do not add ; for namespaces.
> We could add // NetworkCache comments as well.

Sure, done.
Comment 29 youenn fablet 2020-02-24 02:48:07 PST
Comment on attachment 390061 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=390061&action=review

> Source/WebKit/NetworkProcess/cache/NetworkCacheSpeculativeLoad.cpp:72
> +    if (m_networkLoad) {

We could also write it if (!m_networkLoad) return;
Comment 30 Rob Buis 2020-02-24 06:02:44 PST
Created attachment 391530 [details]
Patch
Comment 31 WebKit Commit Bot 2020-02-24 07:50:44 PST
Comment on attachment 391530 [details]
Patch

Clearing flags on attachment: 391530

Committed r257206: <https://trac.webkit.org/changeset/257206>
Comment 32 WebKit Commit Bot 2020-02-24 07:50:46 PST
All reviewed patches have been landed.  Closing bug.
Comment 33 Radar WebKit Bug Importer 2020-02-24 07:51:17 PST
<rdar://problem/59725101>
Comment 34 Carlos Alberto Lopez Perez 2020-03-16 20:50:07 PDT
Comment on attachment 391530 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=391530&action=review

> Source/WebCore/ChangeLog:11
> +        Test: imported/w3c/web-platform-tests/fetch/stale-while-revalidate/frame-removal.html

Has this test been merged into WPT? I can't see it on wpt/master. Is there any PR open for it?
Comment 35 Rob Buis 2020-03-17 01:41:31 PDT
(In reply to Carlos Alberto Lopez Perez from comment #34)
> Comment on attachment 391530 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=391530&action=review
> 
> > Source/WebCore/ChangeLog:11
> > +        Test: imported/w3c/web-platform-tests/fetch/stale-while-revalidate/frame-removal.html
> 
> Has this test been merged into WPT? I can't see it on wpt/master. Is there
> any PR open for it?

Good find, I started to try to add it here:
https://github.com/web-platform-tests/wpt/pull/22292