PA: Auctions may start processes before loading IGs
Auctions are often stuck waiting for processes to be created. Using the in-memory cache for interest group owners, we can anticipate which worklet processes to start in parallel to interest group loading. Bug: 365528726 Change-Id: Id6970baa964c4857ca544b7fb7a6fec6a1b3cf83 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5939361 Reviewed-by: Orr Bernstein <orrb@google.com> Reviewed-by: mmenke <mmenke@chromium.org> Commit-Queue: Abigail Katcoff <abigailkatcoff@chromium.org> Cr-Commit-Position: refs/heads/main@{#1370829}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
63dc9694d7
commit
b76139ee60
content/browser/interest_group
auction_worklet_manager.ccauction_worklet_manager.hinterest_group_auction.ccinterest_group_auction.hinterest_group_browsertest.ccinterest_group_caching_storage.cc
testing/variations
tools/metrics/histograms/metadata/others
@ -960,6 +960,13 @@ void AuctionWorkletManager::RequestWorkletByKey(
|
||||
std::move(worklet_available_callback), std::move(fatal_error_callback)));
|
||||
}
|
||||
|
||||
void AuctionWorkletManager::MaybeStartAnticipatoryProcess(
|
||||
const url::Origin& origin,
|
||||
WorkletType worklet_type) {
|
||||
auction_process_manager()->MaybeStartAnticipatoryProcess(
|
||||
origin, delegate_->GetFrameSiteInstance().get(), worklet_type);
|
||||
}
|
||||
|
||||
void AuctionWorkletManager::OnWorkletNoLongerUsable(WorkletOwner* worklet) {
|
||||
DCHECK(worklets_.count(worklet->worklet_info()));
|
||||
DCHECK_EQ(worklet, worklets_[worklet->worklet_info()]);
|
||||
|
@ -326,6 +326,14 @@ class CONTENT_EXPORT AuctionWorkletManager {
|
||||
size_t number_of_bidder_threads,
|
||||
AuctionMetricsRecorder* auction_metrics_recorder);
|
||||
|
||||
// Start an anticipatory process for an origin if we have not yet
|
||||
// done so and are able.
|
||||
//
|
||||
// Refer to AuctionProcessManager::MaybeStartAnticipatoryProcess
|
||||
// for more details.
|
||||
void MaybeStartAnticipatoryProcess(const url::Origin& origin,
|
||||
WorkletType worklet_type);
|
||||
|
||||
private:
|
||||
void OnWorkletNoLongerUsable(WorkletOwner* worklet);
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <list>
|
||||
#include <map>
|
||||
@ -2943,7 +2944,7 @@ void InterestGroupAuction::StartLoadInterestGroupsPhase(
|
||||
weak_ptr_factory_.GetWeakPtr(), component_auction));
|
||||
++num_pending_loads_;
|
||||
}
|
||||
|
||||
bool try_starting_seller_worklet = false;
|
||||
if (config_->non_shared_params.interest_group_buyers) {
|
||||
for (const auto& buyer :
|
||||
*config_->non_shared_params.interest_group_buyers) {
|
||||
@ -2956,9 +2957,28 @@ void InterestGroupAuction::StartLoadInterestGroupsPhase(
|
||||
base::BindOnce(&InterestGroupAuction::OnInterestGroupRead,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
++num_pending_loads_;
|
||||
std::optional<url::Origin> unused_signals_origin;
|
||||
if (interest_group_manager_->GetCachedOwnerAndSignalsOrigins(
|
||||
buyer, unused_signals_origin)) {
|
||||
// Try to start a process on behalf of this buyer now -- we know this
|
||||
// owner has groups in the database, so we'll likely need this process.
|
||||
auction_worklet_manager_->MaybeStartAnticipatoryProcess(
|
||||
buyer, AuctionWorkletManager::WorkletType::kBidder);
|
||||
try_starting_seller_worklet = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We want the top-level seller process to be started after the component
|
||||
// seller processes to match the order that worklets are requested. Component
|
||||
// seller processes are requested before the top-level seller processes to
|
||||
// avoid deadlock, but anticipatory processes cannot cause deadlock because
|
||||
// they can be cleared in order to respect process limits.
|
||||
if (try_starting_seller_worklet) {
|
||||
auction_worklet_manager_->MaybeStartAnticipatoryProcess(
|
||||
config_->seller, AuctionWorkletManager::WorkletType::kSeller);
|
||||
}
|
||||
|
||||
if (num_pending_loads_ == 0) {
|
||||
// There is nothing to load. We move on to the bidding and scoring phase
|
||||
// anyway, since it may need to wait for config promises to be resolved
|
||||
|
@ -511,6 +511,9 @@ class CONTENT_EXPORT InterestGroupAuction
|
||||
// completion. Passes it false if there are no interest groups that may
|
||||
// participate in the auction (possibly because sellers aren't allowed to
|
||||
// participate in the auction)
|
||||
//
|
||||
// Worklet processes may be created at this point for cached buyers, and for
|
||||
// any seller whose auction has a cached buyer.
|
||||
void StartLoadInterestGroupsPhase(
|
||||
AuctionPhaseCompletionCallback load_interest_groups_phase_callback);
|
||||
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "base/test/bind.h"
|
||||
#include "base/test/metrics/histogram_tester.h"
|
||||
#include "base/test/mock_callback.h"
|
||||
#include "base/test/scoped_command_line.h"
|
||||
#include "base/test/scoped_feature_list.h"
|
||||
#include "base/test/test_timeouts.h"
|
||||
#include "base/test/values_test_util.h"
|
||||
@ -79,6 +80,7 @@
|
||||
#include "content/public/browser/tracing_controller.h"
|
||||
#include "content/public/common/content_client.h"
|
||||
#include "content/public/common/content_features.h"
|
||||
#include "content/public/common/content_switches.h"
|
||||
#include "content/public/test/back_forward_cache_util.h"
|
||||
#include "content/public/test/browser_test.h"
|
||||
#include "content/public/test/browser_test_utils.h"
|
||||
@ -24077,6 +24079,115 @@ IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest, RunAdAuctionTraceTimeout) {
|
||||
stop_run_loop.Run();
|
||||
}
|
||||
|
||||
class UsesAnticipatoryProcessesTest : public InterestGroupBrowserTest {
|
||||
public:
|
||||
UsesAnticipatoryProcessesTest() {
|
||||
feature_list_.InitAndEnableFeatureWithParameters(
|
||||
features::kFledgeStartAnticipatoryProcesses,
|
||||
{{"AnticipatoryProcessHoldTime", "10s"}});
|
||||
// Set up the conditions so that Android will have desktop-like behavior for
|
||||
// process creation.
|
||||
scoped_command_line_.GetProcessCommandLine()->AppendSwitch(
|
||||
switches::kSitePerProcess);
|
||||
}
|
||||
|
||||
private:
|
||||
base::test::ScopedFeatureList feature_list_;
|
||||
base::test::ScopedCommandLine scoped_command_line_;
|
||||
};
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(UsesAnticipatoryProcessesTest,
|
||||
UsesAnticipatoryProcesses) {
|
||||
GURL joining_url = embedded_https_test_server().GetURL("c.test", "/echo");
|
||||
url::Origin joining_origin = url::Origin::Create(joining_url);
|
||||
ASSERT_TRUE(NavigateToURL(shell(), joining_url));
|
||||
|
||||
// Join an interest group. The owner, a.test, will be in the in-memory
|
||||
// cache.
|
||||
GURL ad_url =
|
||||
embedded_https_test_server().GetURL("a.test", "/echo?render_winner");
|
||||
// Use a bidding script that does not bid to prevent the auction from having a
|
||||
// winner. If the auction has a winner, there will be a race condition where a
|
||||
// a worklet will be created for reporting.
|
||||
GURL script_url = embedded_https_test_server().GetURL(
|
||||
"a.test", "/interest_group/bidding_logic_do_not_bid.js");
|
||||
blink::InterestGroup interest_group =
|
||||
blink::TestInterestGroupBuilder(
|
||||
/*owner=*/url::Origin::Create(script_url),
|
||||
/*name=*/"interest_group")
|
||||
.SetBiddingUrl(script_url)
|
||||
.SetAds({{{ad_url, std::nullopt}}})
|
||||
.Build();
|
||||
AttachInterestGroupObserver();
|
||||
manager_->JoinInterestGroup(interest_group, joining_url);
|
||||
WaitForAccessObserved({{"global", TestInterestGroupObserver::kJoin,
|
||||
interest_group.owner, "interest_group"}});
|
||||
std::optional<url::Origin> cached_signals_origin;
|
||||
EXPECT_TRUE(manager_->GetCachedOwnerAndSignalsOrigins(interest_group.owner,
|
||||
cached_signals_origin));
|
||||
|
||||
const char kConfigTemplate[] = R"({
|
||||
seller: $1,
|
||||
decisionLogicURL: $2,
|
||||
auctionSignals: "bidderAllowsComponentAuction,"+
|
||||
"sellerAllowsComponentAuction",
|
||||
componentAuctions:
|
||||
[{
|
||||
seller: $3,
|
||||
decisionLogicURL: $4,
|
||||
interestGroupBuyers: [$5, $6],
|
||||
auctionSignals: "bidderAllowsComponentAuction,"+
|
||||
"sellerAllowsComponentAuction"
|
||||
}]
|
||||
})";
|
||||
|
||||
GURL top_level_seller_script = embedded_https_test_server().GetURL(
|
||||
"c.test", "/interest_group/decision_logic.js");
|
||||
GURL component_seller_script = embedded_https_test_server().GetURL(
|
||||
"d.test", "/interest_group/decision_logic.js");
|
||||
content_browser_client_->AddToAllowList(
|
||||
{url::Origin::Create(component_seller_script)});
|
||||
|
||||
std::string auction_config = JsReplace(
|
||||
kConfigTemplate, url::Origin::Create(top_level_seller_script),
|
||||
top_level_seller_script, url::Origin::Create(component_seller_script),
|
||||
component_seller_script, interest_group.owner,
|
||||
url::Origin::Create(embedded_https_test_server().GetURL("b.test", "/")));
|
||||
|
||||
// Run the auction.
|
||||
//
|
||||
// 1. a.test, the first buyer, is cached. That means we should start an
|
||||
// anticipatory process for it.
|
||||
// 2. b.test, the second buyer, doesn't have any IGs. We won't request an
|
||||
// anticipatory process or create a worklet for it.
|
||||
// 3. c.test, the top-level seller, will not have an anticipatory process
|
||||
// started for it, because there are no top-level buyers cached (component
|
||||
// auctions actually don't allow top-level buyers). A worklet
|
||||
// will still be created for it.
|
||||
// 4. d.test, the component seller, will have an anticipatory process
|
||||
// started for it because there's a cached buyer in its auction (a.test).
|
||||
//
|
||||
// Overall there will be 2 anticipatory processes started and 3 worklets
|
||||
// requested.
|
||||
base::HistogramTester histogram_tester;
|
||||
auto result = RunAuctionAndWait(auction_config);
|
||||
histogram_tester.ExpectUniqueSample("Ads.InterestGroup.Auction.Result",
|
||||
AuctionResult::kNoBids, 1);
|
||||
histogram_tester.ExpectTotalCount(
|
||||
"Ads.InterestGroup.Auction.Seller.RequestWorkletServiceOutcome", 2);
|
||||
histogram_tester.ExpectBucketCount(
|
||||
"Ads.InterestGroup.Auction.Seller.RequestWorkletServiceOutcome",
|
||||
AuctionProcessManager::RequestWorkletServiceOutcome::kUsedIdleProcess, 1);
|
||||
histogram_tester.ExpectBucketCount(
|
||||
"Ads.InterestGroup.Auction.Seller.RequestWorkletServiceOutcome",
|
||||
AuctionProcessManager::RequestWorkletServiceOutcome::
|
||||
kCreatedNewDedicatedProcess,
|
||||
1);
|
||||
histogram_tester.ExpectUniqueSample(
|
||||
"Ads.InterestGroup.Auction.Buyer.RequestWorkletServiceOutcome",
|
||||
AuctionProcessManager::RequestWorkletServiceOutcome::kUsedIdleProcess, 1);
|
||||
}
|
||||
|
||||
class AuctionConfigReportingTimeoutEnabledTest
|
||||
: public InterestGroupBrowserTest {
|
||||
public:
|
||||
|
@ -649,7 +649,9 @@ void InterestGroupCachingStorage::StartTimerForInterestGroupHold(
|
||||
void InterestGroupCachingStorage::UpdateCachedOriginsIfEnabled(
|
||||
const url::Origin& owner,
|
||||
const std::vector<StorageInterestGroup>& interest_groups) {
|
||||
if (!base::FeatureList::IsEnabled(features::kFledgeUsePreconnectCache)) {
|
||||
if (!base::FeatureList::IsEnabled(features::kFledgeUsePreconnectCache) &&
|
||||
!base::FeatureList::IsEnabled(
|
||||
features::kFledgeStartAnticipatoryProcesses)) {
|
||||
return;
|
||||
}
|
||||
if (interest_groups.empty()) {
|
||||
|
@ -18609,6 +18609,26 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"ProtectedAudienceEarlyProcessCreationStudy": [
|
||||
{
|
||||
"platforms": [
|
||||
"android",
|
||||
"chromeos",
|
||||
"chromeos_lacros",
|
||||
"linux",
|
||||
"mac",
|
||||
"windows"
|
||||
],
|
||||
"experiments": [
|
||||
{
|
||||
"name": "Enabled",
|
||||
"enable_features": [
|
||||
"FledgeStartAnticipatoryProcesses"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"ProtectedAudienceMorePAggMetrics": [
|
||||
{
|
||||
"platforms": [
|
||||
|
@ -118,6 +118,7 @@ chromium-metrics-reviews@google.com.
|
||||
<int value="1" label="Used shared process"/>
|
||||
<int value="2" label="Used existing dedicated process"/>
|
||||
<int value="3" label="Created new dedicated process"/>
|
||||
<int value="4" label="Used idle process"/>
|
||||
</enum>
|
||||
|
||||
<enum name="SalientImageUrlFetchResult">
|
||||
|
Reference in New Issue
Block a user