WICG / turtledove

TURTLEDOVE
https://wicg.github.io/turtledove/
Other
511 stars 215 forks source link

API change to trigger a daily update right after a JoinAdInterestGroup event. #1191

Open gianni-99 opened 1 month ago

gianni-99 commented 1 month ago

This issue relates to improving the process of adding users to interest groups. Specifically the latency and processing of the tagging request and response from advertiser page scripts.

Moving to the header api as proposed elsewhere will reduce the need for iFrames on the advertiser page and it is a first step in this direction.

A second idea is to have a “lightweight” tagging request and response that only sets up the IG scaffolding in joinAdInterestGroup and rely on the update request to propagate the other IG data and ads. This would reduce the server side processing at tagging time.

To avoid losing the first (few?) auction opportunities due to 0 ads on device, we propose an API change for joinAdInterestGroup where, optionally, the first update request is triggered shortly after the join event. That could be achieved with:

The delay should be in the order of a few seconds at most.

dmdabbs commented 3 weeks ago

Hello @gianni-99. Interesting proposal. Agree with lightening the initial join-time footprint and deferring remainder to updates. Will you be bringing this to a Wednesday Chrome team WICG meeting?

bmayd commented 3 weeks ago

Think the proposal is good. I expect it will be common for participants to minimize the effort around managing IGs by localizing definitions on update servers and having all new IGs just created as shells that aren't filled out until the update call.

Occurred to me in reading:

A second idea is to have a “lightweight” tagging request and response that only sets up the IG scaffolding in joinAdInterestGroup and rely on the update request to propagate the other IG data and ads.

It maybe useful to have the browser identify IGs that aren't complete enough to be functional and prioritize them for updating with a limited number of retries after which they are deleted.

Also noticed that the updateUrl is not a required property in the IG definition per this, seems like it should be required to avoid creating zombie IGs.

gianni-99 commented 3 weeks ago

Yes it seems a good idea to present this in a Wednesday meeting.

RE: non functional IGs: they might still be useful. For example, if a user goes to an advertiser site before the campaign is fully rolled out, one might want to keep the relevant IG on device with low resource utilization (0 ads for example). The update would propagate the new ads as the campaign is fully rolled out. Hence, it might be a little difficult to identify truly zombie non functional IGs (no updateUrl and ads=[] could be a starting point).

RE: mandatory updateUrl. I agree that, if we implement the delayed ads population option, updateUrl would be necessary. I am not sure we need that requirement when one registers IGs with ads.

dmdabbs commented 3 weeks ago

Hence, it might be a little difficult to identify truly zombie non functional IGs (no updateUrl and ads=[] could be a starting point).

Yes, I have observed IGs joining with neither updateURL nor ads, sometimes quite a few on a single page load, page after page. If these have appreciable impacts, either to storage or auction performance, then consider preventing their joining.

bmayd commented 3 weeks ago

Yes, I have observed IGs joining with neither updateURL nor ads, sometimes quite a few on a single page load, page after page.

I've seen this as well. If folks don't have a way to query for the existence of an IG, the alternative is to assume it doesn't exist and try to create it at every opportunity. I think we're going to want some way of telling the browser to silently fail the request to create IG if it already exists.

bmayd commented 3 weeks ago

RE: mandatory updateUrl. I agree that, if we implement the delayed ads population option, updateUrl would be necessary. I am not sure we need that requirement when one registers IGs with ads.

I suppose updateUrl isn't strictly necessary, but it means having IGs floating around that are unmodifiable and that raises a question about what to do if problems are discovered. I've worked on systems with similar restrictions on querying existing state and all we could do was blindly modify state and blindly request actions with no idea if they would be or applied or not and one of the capabilities we found we wanted that I think we may want with IGs also was the ability to ask the receiver to delete itself. I imagine folks are going to want to be able to proactively request browsers to immediately expire IGs if they exist and that would presumably have to happen via the updateUrl.

dmdabbs commented 2 weeks ago

Thanks @gianni-99. Since frames/script activation is well-established, here's a cut at describing the deferral mechanism and a quick follow-on header implementation that would inherit it.

To avoid losing the first (few?) auction opportunities due to 0 ads on device, we propose an API change for joinAdInterestGroup where, optionally, the first update request is triggered shortly after the join event. That could be achieved with:

To confirm, the feature seeks to transfer the complete IG rendering from the page critical path to updateURL's background thirty-second timeout. In other words, no reduction in the number of complete render calls. @thegreatfatzby, reducing expensive calls was in your sights in the Wednesday discussion, and it's desirable, but let's sketch a K.I.S.S. baseline.

  • a parameter called something like triggerFirstUpdateDelayMs Example: navigator.joinAdInterestGroup(myGroup,triggerFirstUpdateDelayMs);
  • an optional fixed delay enabled by boolean called triggerEarlyUpdate that requests an early update with say a 1,000 ms delay (1 second). Example: navigator.joinAdInterestGroup(myGroup,triggerEarlyUpdate);

The delay should be in the order of a few seconds at most.

Since PA still supports the 'legacy' two parameter joinAdInterestGroup signature, how about a read-and-discard attribute (like lifetimeMs that is converted to expirationTime). Your first option offers flexibility, so (extending the explainer example):

const myGroup = {
  "owner": "https://www.example-dsp.com",
  "name": "womens-running-shoes",
  "lifetimeMs": 2592000000,
  "updateURL": "https://www.example-dsp.com/...",
  "triggerFirstUpdateDelayMs": 3000
};
joinAdInterestGroup(myGroup);

Behaviors

Closing the page and navigations should be handled (not closing the browser), so the desired triggering is something analagous to fetchLater's contract. That is, Chrome will process the updateURL after the timeout or when the page is unloaded, whichever comes first.

If triggerFirstUpdateDelayMs is specified and updateURL is not the call will throw.



Rapid Follow-on Header Implementation Proposal

A joinAdInterestGroup header implementation could initially support only the 'shell' use case's minimal item set, later on solving marshaling arbitrary IG Javascript values from complex/nested Structured Field Values:

Join-Ad-Interest-Group: owner=%"https://www.example-dsp.com", name=%"womens-running-shoes", lifetimeMs=2592000000, updateURL=%"https://www.example-dsp.com/...", triggerFirstUpdateDelayMs=3000

A header that is not a dictionary or that does not include all these valid keys would not be processed. Any other failures (parsing, Permissions, enrollment, &c) would likewise fail silently (unless some .well-known error reporting callback would be considered). The header may appear multiple times and must not be coalesced (similar to how Set-Cookie is handled). String values are Display Strings since this seems closest to how PA specifies these values.

dmdabbs commented 2 weeks ago

Hello @gianni-99. We discussed this issue in today's FLEDGE call. Response was positive - it all seems like a good idea.

bmayd commented 2 weeks ago

To avoid losing the first (few?) auction opportunities due to 0 ads on device, we propose an API change for joinAdInterestGroup where, optionally, the first update request is triggered shortly after the join event. That could be achieved with:

  • a parameter called something like triggerFirstUpdateDelayMs Example:navigator.joinAdInterestGroup(myGroup,triggerFirstUpdateDelayMs);
  • an optional fixed delay enabled by boolean called triggerEarlyUpdate that requests an early update with say a 1,000 ms delay (1 second). Example:navigator.joinAdInterestGroup(myGroup,triggerEarlyUpdate); The delay should be in the order of a few seconds at most.

I suggest we allow IGs to be created with a minimum set of values and flagged to indicate an update is required before they will be operational. If the flag is present, the browser adds the IG to a FIFO update queue which is processed by the browser opportunistically. Until the update is completed, the IGs are otherwise ignored. Attempts to create IGs with this flag that don't include the minimum set of values fail with an error.

bmayd commented 2 weeks ago

Also, as suggested previously: I think we're going to want some way of telling the browser to silently fail the request to create IG if it already exists.

bmayd commented 2 weeks ago

Reading through the meeting notes I see discussion about not persisting the update queue with @michaelkleber indicating it is undesirable, in part because of the possibility that errors would create failure cycles. I want to push back on that as I think it disincentivizes adoption of what I think may be a generally healthful capability.

I expect what I'll call 2-phase IG creation which separates IG creation concerns from IG asset population is going to be a popular pattern.

However, if 2-phase IG creation is considered to be less reliable than all-at-once creation because there is a meaningful possibility the ephemeral update queue will be destroyed before the second phase is completed, folks will be reluctant to use it and in cases where they're compelled to, there will be a lot of competition to be at the front of the queue which is likely to cause problems.

So, rather than making the update queue ephemeral, I suggest we make it a persistent FIFO queue and just remove an IG to be updated from the queue prior to attempting to update it. If a crash occurs, the IG update won't be retried. If the update fails with an error indicating a transient problem, we allow the IG to be queued again up to N number of times, potentially with a backoff.

Summarizing 2-phase IG creation suggestions: