Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(443)

Side by Side Diff: chrome/browser/budget_service/budget_database.cc

Issue 2620393002: Refactor budget computation to be more tuneable. (Closed)
Patch Set: Formatting Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/budget_service/budget_database.h" 5 #include "chrome/browser/budget_service/budget_database.h"
6 6
7 #include "base/memory/ptr_util.h" 7 #include "base/memory/ptr_util.h"
8 #include "base/metrics/histogram_macros.h" 8 #include "base/metrics/histogram_macros.h"
9 #include "base/time/clock.h" 9 #include "base/time/clock.h"
10 #include "base/time/default_clock.h" 10 #include "base/time/default_clock.h"
11 #include "chrome/browser/budget_service/budget.pb.h" 11 #include "chrome/browser/budget_service/budget.pb.h"
12 #include "chrome/browser/engagement/site_engagement_score.h" 12 #include "chrome/browser/engagement/site_engagement_score.h"
13 #include "chrome/browser/engagement/site_engagement_service.h" 13 #include "chrome/browser/engagement/site_engagement_service.h"
14 #include "chrome/browser/profiles/profile.h" 14 #include "chrome/browser/profiles/profile.h"
15 #include "components/leveldb_proto/proto_database_impl.h" 15 #include "components/leveldb_proto/proto_database_impl.h"
16 #include "content/public/browser/browser_thread.h" 16 #include "content/public/browser/browser_thread.h"
17 #include "url/gurl.h" 17 #include "url/gurl.h"
18 #include "url/origin.h" 18 #include "url/origin.h"
19 19
20 using content::BrowserThread; 20 using content::BrowserThread;
21 21
22 namespace { 22 namespace {
23 23
24 // UMA are logged for the database with this string as part of the name. 24 // UMA are logged for the database with this string as part of the name.
25 // They will be LevelDB.*.BudgetManager. Changes here should be synchronized 25 // They will be LevelDB.*.BudgetManager. Changes here should be synchronized
26 // with histograms.xml. 26 // with histograms.xml.
27 const char kDatabaseUMAName[] = "BudgetManager"; 27 const char kDatabaseUMAName[] = "BudgetManager";
28 28
29 // The default amount of time during which a budget will be valid. 29 // The default amount of time during which a budget will be valid.
30 // This is 4 days = 96 hours. 30 constexpr int kBudgetDurationInDays = 4;
31 constexpr double kBudgetDurationInHours = 96; 31
32 // The amount of budget that a maximally engaged site should receive per hour.
33 // This allows 6 silent push messages a day for a maximal site.
Peter Beverloo 2017/01/12 14:40:04 Can we make this comment slightly clearer, since i
harkness 2017/01/12 17:44:14 I updated the comment here. I'd like to keep the c
34 constexpr double kMaximumHourlyBudget = 12.0 / 24.0;
32 35
33 } // namespace 36 } // namespace
34 37
35 BudgetDatabase::BudgetInfo::BudgetInfo() {} 38 BudgetDatabase::BudgetInfo::BudgetInfo() {}
36 39
37 BudgetDatabase::BudgetInfo::BudgetInfo(const BudgetInfo&& other) 40 BudgetDatabase::BudgetInfo::BudgetInfo(const BudgetInfo&& other)
38 : last_engagement_award(other.last_engagement_award) { 41 : last_engagement_award(other.last_engagement_award) {
39 chunks = std::move(other.chunks); 42 chunks = std::move(other.chunks);
40 } 43 }
41 44
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after
157 // expiration times going forward. 160 // expiration times going forward.
158 const BudgetChunks& chunks = budget_map_[origin].chunks; 161 const BudgetChunks& chunks = budget_map_[origin].chunks;
159 for (const auto& chunk : chunks) { 162 for (const auto& chunk : chunks) {
160 blink::mojom::BudgetStatePtr prediction(blink::mojom::BudgetState::New()); 163 blink::mojom::BudgetStatePtr prediction(blink::mojom::BudgetState::New());
161 total -= chunk.amount; 164 total -= chunk.amount;
162 prediction->budget_at = total; 165 prediction->budget_at = total;
163 prediction->time = chunk.expiration.ToDoubleT(); 166 prediction->time = chunk.expiration.ToDoubleT();
164 predictions.push_back(std::move(prediction)); 167 predictions.push_back(std::move(prediction));
165 } 168 }
166 169
167 DCHECK_EQ(0, total);
168
169 callback.Run(blink::mojom::BudgetServiceErrorType::NONE, 170 callback.Run(blink::mojom::BudgetServiceErrorType::NONE,
170 std::move(predictions)); 171 std::move(predictions));
171 } 172 }
172 173
173 void BudgetDatabase::SpendBudgetAfterSync(const url::Origin& origin, 174 void BudgetDatabase::SpendBudgetAfterSync(const url::Origin& origin,
174 double amount, 175 double amount,
175 const SpendBudgetCallback& callback, 176 const SpendBudgetCallback& callback,
176 bool success) { 177 bool success) {
177 if (!success) { 178 if (!success) {
178 callback.Run(blink::mojom::BudgetServiceErrorType::DATABASE_ERROR, 179 callback.Run(blink::mojom::BudgetServiceErrorType::DATABASE_ERROR,
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after
297 // Get the SES score and add engagement budget for the site. 298 // Get the SES score and add engagement budget for the site.
298 AddEngagementBudget(origin); 299 AddEngagementBudget(origin);
299 300
300 if (needs_write) 301 if (needs_write)
301 WriteCachedValuesToDatabase(origin, callback); 302 WriteCachedValuesToDatabase(origin, callback);
302 else 303 else
303 callback.Run(success); 304 callback.Run(success);
304 } 305 }
305 306
306 void BudgetDatabase::AddEngagementBudget(const url::Origin& origin) { 307 void BudgetDatabase::AddEngagementBudget(const url::Origin& origin) {
307 // Get the current SES score, which we'll use to set a new budget. 308 // Calculate how much budget should be awarded. The award depends on the
309 // time elapsed since the last award and the SES score.
310 base::TimeDelta elapsed = base::TimeDelta::FromDays(kBudgetDurationInDays);
Peter Beverloo 2017/01/12 14:40:04 nit: I think the "By default..." comment would sti
harkness 2017/01/12 17:44:14 Done.
311 if (IsCached(origin)) {
312 elapsed = clock_->Now() - budget_map_[origin].last_engagement_award;
313 // Don't give engagement awards for periods less than an hour.
314 if (elapsed.InHours() < 1)
315 return;
316 // Cap elapsed time to the budget duration.
317 if (elapsed.InDays() > kBudgetDurationInDays)
318 elapsed = base::TimeDelta::FromDays(kBudgetDurationInDays);
319 }
320
321 // Get the current SES score, and calculate the hourly budget for that score.
308 SiteEngagementService* service = SiteEngagementService::Get(profile_); 322 SiteEngagementService* service = SiteEngagementService::Get(profile_);
309 double score = service->GetScore(origin.GetURL()); 323 double hourly_budget = kMaximumHourlyBudget *
310 324 service->GetScore(origin.GetURL()) /
311 // By default we award the "full" award. Then that ratio is decreased if 325 service->GetMaxPoints();
312 // there have been other awards recently.
313 double ratio = 1.0;
314
315 // Calculate how much budget should be awarded. If there is no entry in the
316 // cache then we award a full amount.
317 if (IsCached(origin)) {
318 base::TimeDelta elapsed =
319 clock_->Now() - budget_map_[origin].last_engagement_award;
320 int elapsed_hours = elapsed.InHours();
321 // Don't give engagement awards for periods less than an hour.
322 if (elapsed_hours < 1)
323 return;
324 if (elapsed_hours < kBudgetDurationInHours)
325 ratio = elapsed_hours / kBudgetDurationInHours;
326 }
327 326
328 // Update the last_engagement_award to the current time. If the origin wasn't 327 // Update the last_engagement_award to the current time. If the origin wasn't
329 // already in the map, this adds a new entry for it. 328 // already in the map, this adds a new entry for it.
330 budget_map_[origin].last_engagement_award = clock_->Now(); 329 budget_map_[origin].last_engagement_award = clock_->Now();
331 330
332 // Add a new chunk of budget for the origin at the default expiration time. 331 // Add a new chunk of budget for the origin at the default expiration time.
333 base::Time expiration = 332 base::Time expiration =
334 clock_->Now() + base::TimeDelta::FromHours(kBudgetDurationInHours); 333 clock_->Now() + base::TimeDelta::FromDays(kBudgetDurationInDays);
335 budget_map_[origin].chunks.emplace_back(ratio * score, expiration); 334 budget_map_[origin].chunks.emplace_back(elapsed.InHours() * hourly_budget,
335 expiration);
336 336
337 // Any time we award engagement budget, which is done at most once an hour 337 // Any time we award engagement budget, which is done at most once an hour
338 // whenever any budget action is taken, record the budget. 338 // whenever any budget action is taken, record the budget.
339 double budget = GetBudget(origin); 339 double budget = GetBudget(origin);
340 UMA_HISTOGRAM_COUNTS_100("PushMessaging.BackgroundBudget", budget); 340 UMA_HISTOGRAM_COUNTS_100("PushMessaging.BackgroundBudget", budget);
341 } 341 }
342 342
343 // Cleans up budget in the cache. Relies on the caller eventually writing the 343 // Cleans up budget in the cache. Relies on the caller eventually writing the
344 // cache back to the database. 344 // cache back to the database.
345 bool BudgetDatabase::CleanupExpiredBudget(const url::Origin& origin) { 345 bool BudgetDatabase::CleanupExpiredBudget(const url::Origin& origin) {
346 if (!IsCached(origin)) 346 if (!IsCached(origin))
347 return false; 347 return false;
348 348
349 base::Time now = clock_->Now(); 349 base::Time now = clock_->Now();
350 BudgetChunks& chunks = budget_map_[origin].chunks; 350 BudgetChunks& chunks = budget_map_[origin].chunks;
351 auto cleanup_iter = chunks.begin(); 351 auto cleanup_iter = chunks.begin();
352 352
353 // This relies on the list of chunks being in timestamp order. 353 // This relies on the list of chunks being in timestamp order.
354 while (cleanup_iter != chunks.end() && cleanup_iter->expiration <= now) 354 while (cleanup_iter != chunks.end() && cleanup_iter->expiration <= now)
355 cleanup_iter = chunks.erase(cleanup_iter); 355 cleanup_iter = chunks.erase(cleanup_iter);
356 356
357 // If the entire budget is empty now AND there have been no engagements 357 // If the entire budget is empty now AND there have been no engagements
358 // in the last kBudgetDurationInHours hours, remove this from the cache. 358 // in the last kBudgetDurationInDays days, remove this from the cache.
359 if (chunks.empty() && 359 if (chunks.empty() &&
360 budget_map_[origin].last_engagement_award < 360 budget_map_[origin].last_engagement_award <
361 clock_->Now() - base::TimeDelta::FromHours(kBudgetDurationInHours)) { 361 clock_->Now() - base::TimeDelta::FromDays(kBudgetDurationInDays)) {
362 budget_map_.erase(origin); 362 budget_map_.erase(origin);
363 return true; 363 return true;
364 } 364 }
365 365
366 // Although some things may have expired, there are some chunks still valid. 366 // Although some things may have expired, there are some chunks still valid.
367 // Don't write to the DB now, write either when all chunks expire or when the 367 // Don't write to the DB now, write either when all chunks expire or when the
368 // origin spends some budget. 368 // origin spends some budget.
369 return false; 369 return false;
370 } 370 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698