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

Is it possible to run PAA (Protected Audience API) auction in parallel to Header Bidding and publisher’s adserver call? #851

Open
wojciech-bialy-wpm opened this issue Oct 10, 2023 · 10 comments

Comments

@wojciech-bialy-wpm
Copy link
Contributor

We have tried to test an approach in which PAA auction is initiated by the publisher - using modified fledgeForGPT prebid module and componentAuctions received from RTB House bidder.

Our test setup - with standalone PAA auction - can be described as follows:

PAA auction activated by the publisher

For more detailed information about our experiment, please see: https://github.com/grupawp/PAapi

Experiment conclusions:

Our experiment shows that this integration option may result in a significant revenue drop for the publisher.

The revenue drop reasons are:

  1. PAA auction always extends the time needed to receive and render the ad. Our statistics show that:

    • median duration time of empty PAA auctions is 52ms (161ms for the 80th percentile)

    • median duration time of PAA auctions that return URN / fenced frame config is 733ms (1773ms for the 80th percentile)

Additional significant delay caused by a PAA auction (especially for non-zero results) decreases adslot viewability – reducing ad slot value and negatively impacting user experience – affecting user retention and overall business success. Please see: https://web.dev/fast-ads-matter/ for more information about the impact of ad latency on publisher’s revenue.

  1. Since neither PAA, nor Google Ad Manager returns comparable ad value, the publisher cannot select best (highest valued) ad between those two demand sources. Publisher is forced to select an ad randomly or always prefer one of those sources over the other. This results in a significant publisher’s revenue drop.

Solution:

Running PAA auction in parallel to Header Bidding and publisher’s adserver would be possible if PAA auction would return comparable ad value (with precision preserving k-anonymity). Parallel architecture would minimize PAA impact on ad latency and would not force the publisher to choose demand sources randomly.

@thegreatfatzby
Copy link
Contributor

I believe the parallelization could be achieved if the final ranking didn't have to occur inside the private auction. One thing we've talked about internally is having a final opaque function that could run in the browser, that takes opaque winner but also other winners, and then turn out the final winner. We could add additional opaque callbacks to minimize the bit leaks, so something like "onPrivateWin" that handles FF rendering, "onOtherWin", etc.

@thegreatfatzby
Copy link
Contributor

Couldn't you actually just re-run the scoreAd process, just with only the winner from the Private Auction, and then winner from the other auction?

@dmdabbs
Copy link
Contributor

dmdabbs commented Oct 10, 2023

Chrome is working on improving IG retrieval latency, but that's not going to be a silver bullet.

@michaelkleber
Copy link
Collaborator

michaelkleber commented Oct 10, 2023

Hello @wojciech-bialy-wpm,

We would not recommend the approach of randomly selecting between two different auctions. We designed the Protected Audience API so that information from multiple sources could flow into the PA auction and decision process. If the best ad is not one that comes from inside the PA auction, then the PA auction should return no ad at all, and the caller should instead render the best non-PA ad they know about. This logic is the job of the script in the seller's decisionLogicURL.

The bidding inside the PA auction generally depends on contextual signals that are returned by other servers (for both Header Bidding and GAM). That means it's impossible for the PA auction to finish before those signals are available. We have indeed built ways that the PA auction can be started in parallel, though: see Section 2.1.1 Providing Signals Asynchronously for details on this feature.

Regarding latency, the seller who initiates the auction is again the party in control of what resources it is allowed to consume. Please look at the auction config options perBuyerTimeouts, perBuyerCumulativeTimeouts, and perBuyerGroupLimits (perhaps along with perBuyerPrioritySignals, if buyers use the priorityVector feature) for ways that the seller can control the time allocated to each buyer during the auction.

@wojciech-bialy-wpm
Copy link
Contributor Author

Hello @thegreatfatzby,

Regarding final ranking / function, we thought about a structure like that. A service, that would take winning bids/ads from different channels (including the PAA), compare them and then send win/loss signal to buyers, and winning ad to the seller. There are ways to make it secure, such as running in safe environment (a browser worklet?), or allowing only limited amount of comparisons (to prevent information leakage via multiple win/loss tests).

As for - re-running scoreAd, how would it be possible? Would it be a solution where PAA and non-PAA channels are running in parallel, PAA bids are generated and scored (based on ad metadata, trusted scoring signals etc.) and finally, the winning non-PAA ad value is used to re-score (apply floor price) the PAA auction result?

@wojciech-bialy-wpm
Copy link
Contributor Author

Hello @michaelkleber,

I wholeheartedly agree that the publisher should never have to select ads randomly.
In our setup, this is due to the fact that two channels do not return ad value. In our test environment we have:

  • ad server (returns an ad, and ad value equal to the effective CPM)
  • header bidding framework (returns bids + bid CPMs)
  • Google Ad Manager (renders an ad, or returns no ad callback)
  • PAA (returns fenced frame URN / config, or null)

If we'd run all the following in parallel, then we'd potentially end with two results that cannot be compared with each other.

Regarding using promises in PAA auction config, do I understand correctly that we can:

  • start PAA auction in parallel to other channels, providing sellerSignals as a Promise
  • adserver / header bidder return ads/bids, while PAA spins auction worklets and runs the buyer logic (Interest Group worklet -> buyer.js -> generateBid)
  • adserver / header bidding results are used to generate the floor price and resolve sellerSignals Promise
  • as sellerSignals Promise is resolved, PAA runs the seller logic (Interest Group worklet -> seller.js -> scoreAd)
  • PAA selects the winning ad, and resolves the auction Promise

I'll add it to the test, and we'll see how much of auction duration is taken by buyer / seller workflows.

@wojciech-bialy-wpm
Copy link
Contributor Author

Hello @michaelkleber,

We have ran tests, where PAA auction initiation and buy-side logic is executed in parallel with header bidding and adserver calls. PAA sell-side logic is blocked by sellerSignals promise, that is resolved when adslot is executed and winning bid / ad
(from header bidding and adserver, respectively) are known.

This setup can be described as follows:

async PAA example

From what we've seen, the auction duration (a median of ~730ms) is split into:

  • init + buy side part, with a median duration of ~700ms
  • sell side part, with a median duration of ~20-40ms (20ms for desktop, 40ms for mobile)

Since in our setup header bidding timeout is 800ms, only sell side impacts ad rendering time - meaning
that delay incurred by PAA auction is reduced by ~95% (from 740ms to 40ms).

However, this will only work as long as we can control when PAA auction starts (and start it concurrently with other demand channels). As seen in Privacy Sandbox Progress Report for Q3 (https://assets.publishing.service.gov.uk/media/653a491d80884d000df71b70/Google_s_Q3_2023_report_.pdf),
Google Ad Manager intents to maintain control over top-level PAA auction, and execute it (plus all component Auction) only after receiving response from the adserver.

This unfortunately means that such optimization is not achievable in any ad stack that includes the Google Ad Manager, and without it the publisher cannot monetize the demand that is unique to Google.

@michaelkleber
Copy link
Collaborator

Thank you for reporting back! It sounds like our past parallelization work is a great success when it gets used. I agree that the essential question now is how GAM can also start feeding into the parallelization mechanism as well.

@patmmccann
Copy link
Contributor

@michaelkleber @wojciech-bialy-wpm @rahulkooverjee-google we believe we've solved for this: prebid/Prebid.js#12205

@wojciech-bialy-wpm
Copy link
Contributor Author

wojciech-bialy-wpm commented Sep 26, 2024

@patmmccann

Agreed! I haven't had an opportunity deploy test branch with parallel-paapi, but it does sound like it would:

  • query adapters for component auction configs
  • prepare multiSeller PAAPI auction (with signals for component auction provided as Promises)
  • resolve component auction Promises as bid responses are being received

In effect the initial PAAPI workload will start with prebid auction, component-level workloads will start during auction, leaving just top-level workload to run in sequence (after auction end). And (unlike my proposal) it does not require a separate logic to generate component level configs/signals, so that's definitely a plus.

For practical applications, we still would need the Google Ad Manager to separate contextual/paapi calls, or to relinquish control over the top level auction - but that is an issue for another repository :)

I think we can close this issue.

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

5 participants