Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bidding script not loaded asynchronously #1355

Open
fhoering opened this issue Dec 2, 2024 · 9 comments
Open

Bidding script not loaded asynchronously #1355

fhoering opened this issue Dec 2, 2024 · 9 comments

Comments

@fhoering
Copy link
Contributor

fhoering commented Dec 2, 2024

We made some experimentations with Chrome network throttling and auctionConfig timeouts and it seems that with slow network connections like 3G there are cases where we are never able to actually load the bidding script. The auction just times out with the following error and the network request is left of pending.

Worklet error: https://fledge.us.preprod.criteo.com/simplebid?.. perBuyerCumulativeTimeout exceeded during bid generation.

config:

  • bidding script size 100 KB, using HEADER cache-control: private, max-age=180
  • network throttling 3G
  • perBuyerTimeouts 50ms
  • perBuyerCumulativeTimeouts 2sec

image

When using HTTP caching it is our expectation that the bidding script would just continue downloading and be available from cache for the next auction (as loading the script from cache would be significantly faster on devices with slow network connections, going down from seconds to ms)

I attaching the export logs and Chrome traces for 2 successive auctions done on some test pages with a real bidding script.

It looks related to issue #1347 but might not be the only reason for this behavior mentioned there.

chrome-net-export-log.json
trace_aborted_biddingscipt_download.json.gz

@MattMenke2
Copy link
Contributor

MattMenke2 commented Dec 2, 2024

This is expected behavior. In general, network requests that are cancelled at not continued for the purposes of caching, with the exception of explicit prefetches, and perhaps <a ping>, and the like.

@fhoering
Copy link
Contributor Author

fhoering commented Dec 3, 2024

This is expected behavior. In general, network requests that are cancelled at not continued for the purposes of caching, with the exception of explicit prefetches, and perhaps , and the like.

@MattMenke2 I think you should reconsider this choice and fix it as it strongly impacts PA supply availability and therefore usability of PA in general.

@anderagakura
Copy link

@MattMenke2 Where can I see this expected behavior in the doc?

@michaelkleber
Copy link
Collaborator

@fhoering Try changing your Cache-Control header to also include a directive like "max-age=180,stale-while-revalidate=3600".

Your current header, max-age=180, only lets the browser use the cached version of the JS for three minutes. So even if the browser did behave as you hoped, and continued the cancelled fetch in order to fill the cache, you would lose all benefit three minutes later.

Adding stale-while-revalidate=3600 tells the browser that it is allowed to use that cached response for the next hour, but also that it should try to fetch a fresh copy, to insert into the cache, if the currently-cached one is more than 3 minutes old. This would let the auction progress right away, and avoid the perBuyerCumulativeTimeout shutting it down while waiting for the script to load.

I'm not sure how long the background request for the stale cache refresh will persist for, though.  @MattMenke2 do you know if it will be able to outlast the bidding worklet lifetime? It seems like we should be able to make this stale-while-revalidate pattern work well in principle, but there might be tricky bits around process lifetimes.

@morlovich
Copy link
Collaborator

stale-while-revalidate won't do anything for worklet scripts, sorry.

@ankwok
Copy link

ankwok commented Dec 3, 2024

stale-while-revalidate won't do anything for worklet scripts, sorry.

Do you mean that

  • The browser will completely ignore this directive when trying to fetch the bidding script? Once past the max-age, but within the the stale-while-revalidate period, the worklet will still block and wait to fetch a fresh script.
  • or... The request to fetch a fresh copy will get killed when the auction ends?

@MattMenke2
Copy link
Contributor

As morlovich says, stale-while-revalidate was written in a way such that it only works for a single consumer that knows how to deal with it, as opposed to a general thing that works for all consumers of network requests, unfortunately. A caller asks for a potentially stale response, and then issues a new request if it gets a stale response. I think any new requests for the resource will then be blocked by the revalidation request.

I'm reluctant to add something that continues downloads of potentially unlimited size that are uncancellable (since they aren't associated with a tab, and thus closing a tab won't cancel them). Prefetches do actually do that, I believe (S-W-R callers may or may not do that), but I don't want to introduce yet another case of it, and am reluctant yet more arbitrary size limits and timeouts (and an internal API to support them) - I don't really feel that sort of thing is good for the web platform.

You could imagine attaching those downloads to the lifetime of the frame, though that would introduce a timing side channel (though I guess cancelling the auction on navigation away is basically the exact same timing side channel).

@michaelkleber
Copy link
Collaborator

Okay, I'm sorry that stale-while-revalidate isn't a magic fix here. Let's try a different approach.

@fhoering Have you tried your experiments in the flow where perBuyerSignals is provided as a promise? I believe this will let the network fetch of the script begin early, in parallel with the publisher’s adserver call and before the perBuyerCumulativeTimeout timer starts ticking. Would that mitigate the problem?

@fhoering
Copy link
Contributor Author

fhoering commented Dec 9, 2024

Setting perBuyerSignals as a promise indeed mitigates the issue for parallel auctions in some form because the perBuyerCumulativeTimeout will only start counting when the promise is resolved. The buyer will continue being affected by the global auction abort signal timeout which also might stop downloading the script.

It looks like this is not a setting the buyer is in control of, which means any seller can impact this behavior. So I would not see this as a workaround. Whatever values the seller sets for perBuyerCumulativeTimeout, the perBuyerSignals promise timeouts or the global auction abort signal the script should be loaded asynchronously.

We also made some projections on sequential auctions vs parallel auctions a during the investigations for #1347 and we don't see any noticeable difference. But potentially this can just mean that the extended timeout of the promise resolution is small vs the initial perBuyerCumulativeTimeout setting or that the global auction timeout comes into play. We also still see many sequential auctions today and Prebid support for parallel auctions is very recent (prebid/Prebid.js#12205) and still needs to be implemented and integrated with GAM.

If we want to reduce on-device auction latency, being able to load resources asynchronously seems like something to explore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants