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

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

Issue 2281673002: Full hookup of BudgetManager interfaces to BudgetDatabase. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@manager
Patch Set: nits Created 4 years, 3 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/containers/adapters.h" 7 #include "base/metrics/histogram_macros.h"
8 #include "base/time/clock.h" 8 #include "base/time/clock.h"
9 #include "base/time/default_clock.h" 9 #include "base/time/default_clock.h"
10 #include "chrome/browser/budget_service/budget.pb.h" 10 #include "chrome/browser/budget_service/budget.pb.h"
11 #include "chrome/browser/engagement/site_engagement_score.h" 11 #include "chrome/browser/engagement/site_engagement_score.h"
12 #include "chrome/browser/engagement/site_engagement_service.h" 12 #include "chrome/browser/engagement/site_engagement_service.h"
13 #include "chrome/browser/profiles/profile.h" 13 #include "chrome/browser/profiles/profile.h"
14 #include "components/leveldb_proto/proto_database_impl.h" 14 #include "components/leveldb_proto/proto_database_impl.h"
15 #include "content/public/browser/browser_thread.h" 15 #include "content/public/browser/browser_thread.h"
16 #include "url/gurl.h" 16 #include "url/gurl.h"
17 17
18 using content::BrowserThread; 18 using content::BrowserThread;
19 19
20 namespace { 20 namespace {
21 21
22 // UMA are logged for the database with this string as part of the name. 22 // UMA are logged for the database with this string as part of the name.
23 // They will be LevelDB.*.BudgetManager. Changes here should be synchronized 23 // They will be LevelDB.*.BudgetManager. Changes here should be synchronized
24 // with histograms.xml. 24 // with histograms.xml.
25 const char kDatabaseUMAName[] = "BudgetManager"; 25 const char kDatabaseUMAName[] = "BudgetManager";
26 26
27 // The default amount of time during which a budget will be valid. 27 // The default amount of time during which a budget will be valid.
28 // This is 3 days = 72 hours. 28 // This is 10 days = 240 hours.
29 constexpr double kBudgetDurationInHours = 72; 29 constexpr double kBudgetDurationInHours = 240;
30 30
31 } // namespace 31 } // namespace
32 32
33 BudgetDatabase::BudgetInfo::BudgetInfo() {} 33 BudgetDatabase::BudgetInfo::BudgetInfo() {}
34 34
35 BudgetDatabase::BudgetInfo::BudgetInfo(const BudgetInfo&& other) 35 BudgetDatabase::BudgetInfo::BudgetInfo(const BudgetInfo&& other)
36 : last_engagement_award(other.last_engagement_award) { 36 : last_engagement_award(other.last_engagement_award) {
37 chunks = std::move(other.chunks); 37 chunks = std::move(other.chunks);
38 } 38 }
39 39
40 BudgetDatabase::BudgetInfo::~BudgetInfo() {} 40 BudgetDatabase::BudgetInfo::~BudgetInfo() {}
41 41
42 BudgetDatabase::BudgetDatabase( 42 BudgetDatabase::BudgetDatabase(
43 Profile* profile, 43 Profile* profile,
44 const base::FilePath& database_dir, 44 const base::FilePath& database_dir,
45 const scoped_refptr<base::SequencedTaskRunner>& task_runner) 45 const scoped_refptr<base::SequencedTaskRunner>& task_runner)
46 : profile_(profile), 46 : profile_(profile),
47 db_(new leveldb_proto::ProtoDatabaseImpl<budget_service::Budget>( 47 db_(new leveldb_proto::ProtoDatabaseImpl<budget_service::Budget>(
48 task_runner)), 48 task_runner)),
49 clock_(base::WrapUnique(new base::DefaultClock)), 49 clock_(base::WrapUnique(new base::DefaultClock)),
50 weak_ptr_factory_(this) { 50 weak_ptr_factory_(this) {
51 db_->Init(kDatabaseUMAName, database_dir, 51 db_->Init(kDatabaseUMAName, database_dir,
52 base::Bind(&BudgetDatabase::OnDatabaseInit, 52 base::Bind(&BudgetDatabase::OnDatabaseInit,
53 weak_ptr_factory_.GetWeakPtr())); 53 weak_ptr_factory_.GetWeakPtr()));
54 } 54 }
55 55
56 BudgetDatabase::~BudgetDatabase() {} 56 BudgetDatabase::~BudgetDatabase() {}
57 57
58 void BudgetDatabase::GetBudgetDetails( 58 void BudgetDatabase::GetBudgetDetails(const GURL& origin,
59 const GURL& origin, 59 const GetBudgetCallback& callback) {
60 const GetBudgetDetailsCallback& callback) {
61 DCHECK_EQ(origin.GetOrigin(), origin); 60 DCHECK_EQ(origin.GetOrigin(), origin);
62 61
63 SyncCache(origin, 62 SyncCache(origin,
64 base::Bind(&BudgetDatabase::GetBudgetAfterSync, 63 base::Bind(&BudgetDatabase::GetBudgetAfterSync,
65 weak_ptr_factory_.GetWeakPtr(), origin, callback)); 64 weak_ptr_factory_.GetWeakPtr(), origin, callback));
66 } 65 }
67 66
68 void BudgetDatabase::SpendBudget(const GURL& origin, 67 void BudgetDatabase::SpendBudget(const GURL& origin,
69 double amount, 68 double amount,
70 const StoreBudgetCallback& callback) { 69 const StoreBudgetCallback& callback) {
71 SyncCache(origin, base::Bind(&BudgetDatabase::SpendBudgetAfterSync, 70 SyncCache(origin, base::Bind(&BudgetDatabase::SpendBudgetAfterSync,
72 weak_ptr_factory_.GetWeakPtr(), origin, amount, 71 weak_ptr_factory_.GetWeakPtr(), origin, amount,
73 callback)); 72 callback));
74 } 73 }
75 74
76 void BudgetDatabase::SetClockForTesting(std::unique_ptr<base::Clock> clock) { 75 void BudgetDatabase::SetClockForTesting(std::unique_ptr<base::Clock> clock) {
77 clock_ = std::move(clock); 76 clock_ = std::move(clock);
78 } 77 }
79 78
80 void BudgetDatabase::OnDatabaseInit(bool success) { 79 void BudgetDatabase::OnDatabaseInit(bool success) {
81 // TODO(harkness): Consider caching the budget database now? 80 // TODO(harkness): Consider caching the budget database now?
82 } 81 }
83 82
84 bool BudgetDatabase::IsCached(const GURL& origin) const { 83 bool BudgetDatabase::IsCached(const GURL& origin) const {
85 return budget_map_.find(origin.spec()) != budget_map_.end(); 84 return budget_map_.find(origin.spec()) != budget_map_.end();
86 } 85 }
87 86
87 double BudgetDatabase::GetBudget(const GURL& origin) const {
88 double total = 0;
89 auto iter = budget_map_.find(origin.spec());
90 if (iter == budget_map_.end())
91 return total;
92
93 const BudgetInfo& info = iter->second;
94 for (const BudgetChunk& chunk : info.chunks)
95 total += chunk.amount;
96 return total;
97 }
98
88 void BudgetDatabase::AddToCache( 99 void BudgetDatabase::AddToCache(
89 const GURL& origin, 100 const GURL& origin,
90 const AddToCacheCallback& callback, 101 const AddToCacheCallback& callback,
91 bool success, 102 bool success,
92 std::unique_ptr<budget_service::Budget> budget_proto) { 103 std::unique_ptr<budget_service::Budget> budget_proto) {
93 // If the database read failed, there's nothing to add to the cache. 104 // If the database read failed, there's nothing to add to the cache.
94 if (!success || !budget_proto) { 105 if (!success || !budget_proto) {
95 callback.Run(success); 106 callback.Run(success);
96 return; 107 return;
97 } 108 }
(...skipping 12 matching lines...) Expand all
110 info.chunks.emplace_back(chunk.amount(), 121 info.chunks.emplace_back(chunk.amount(),
111 base::Time::FromInternalValue(chunk.expiration())); 122 base::Time::FromInternalValue(chunk.expiration()));
112 } 123 }
113 124
114 info.last_engagement_award = 125 info.last_engagement_award =
115 base::Time::FromInternalValue(budget_proto->engagement_last_updated()); 126 base::Time::FromInternalValue(budget_proto->engagement_last_updated());
116 127
117 callback.Run(success); 128 callback.Run(success);
118 } 129 }
119 130
120 void BudgetDatabase::GetBudgetAfterSync( 131 void BudgetDatabase::GetBudgetAfterSync(const GURL& origin,
121 const GURL& origin, 132 const GetBudgetCallback& callback,
122 const GetBudgetDetailsCallback& callback, 133 bool success) {
123 bool success) { 134 mojo::Array<blink::mojom::BudgetStatePtr> predictions;
135
124 // If the database wasn't able to read the information, return the 136 // If the database wasn't able to read the information, return the
125 // failure and an empty BudgetPrediction. 137 // failure and an empty predictions array.
126 if (!success) { 138 if (!success) {
127 callback.Run(success, BudgetPrediction()); 139 callback.Run(blink::mojom::BudgetServiceErrorType::DATABASE_ERROR,
140 std::move(predictions));
128 return; 141 return;
129 } 142 }
130 143
131 // Now, build up the BudgetExpection. This is different from the format 144 // Now, build up the BudgetExpection. This is different from the format
132 // in which the cache stores the data. The cache stores chunks of budget and 145 // in which the cache stores the data. The cache stores chunks of budget and
133 // when that budget expires. The BudgetPrediction describes a set of times 146 // when that budget expires. The mojo array describes a set of times
134 // and the budget at those times. 147 // and the budget at those times.
135 BudgetPrediction prediction; 148 double total = GetBudget(origin);
136 double total = 0;
137 149
138 // Starting with the chunks that expire the farthest in the future, build up 150 // Always add one entry at the front of the list for the total budget now.
139 // the budget predictions for those future times. 151 blink::mojom::BudgetStatePtr prediction(blink::mojom::BudgetState::New());
152 prediction->budget_at = total;
153 prediction->time = clock_->Now().ToDoubleT();
154 predictions.push_back(std::move(prediction));
155
156 // Starting with the soonest expiring chunks, add entries for the
157 // expiration times going forward.
140 const BudgetChunks& chunks = budget_map_[origin.spec()].chunks; 158 const BudgetChunks& chunks = budget_map_[origin.spec()].chunks;
141 for (const auto& chunk : base::Reversed(chunks)) { 159 for (const auto& chunk : chunks) {
142 prediction.emplace_front(total, chunk.expiration); 160 blink::mojom::BudgetStatePtr prediction(blink::mojom::BudgetState::New());
143 total += chunk.amount; 161 total -= chunk.amount;
162 prediction->budget_at = total;
163 prediction->time = chunk.expiration.ToDoubleT();
164 predictions.push_back(std::move(prediction));
144 } 165 }
145 166
146 // Always add one entry at the front of the list for the total budget now. 167 DCHECK_EQ(0, total);
147 prediction.emplace_front(total, clock_->Now());
148 168
149 callback.Run(true /* success */, prediction); 169 callback.Run(blink::mojom::BudgetServiceErrorType::NONE,
170 std::move(predictions));
150 } 171 }
151 172
152 void BudgetDatabase::SpendBudgetAfterSync(const GURL& origin, 173 void BudgetDatabase::SpendBudgetAfterSync(const GURL& origin,
153 double amount, 174 double amount,
154 const StoreBudgetCallback& callback, 175 const StoreBudgetCallback& callback,
155 bool success) { 176 bool success) {
156 if (!success) { 177 if (!success) {
157 callback.Run(false /* success */); 178 callback.Run(false /* success */);
158 return; 179 return;
159 } 180 }
160 181
182 // Get the current SES score, to generate UMA.
183 SiteEngagementService* service = SiteEngagementService::Get(profile_);
184 double score = service->GetScore(origin);
185
161 // Walk the list of budget chunks to see if the origin has enough budget. 186 // Walk the list of budget chunks to see if the origin has enough budget.
162 double total = 0; 187 double total = 0;
163 BudgetInfo& info = budget_map_[origin.spec()]; 188 BudgetInfo& info = budget_map_[origin.spec()];
164 for (const BudgetChunk& chunk : info.chunks) 189 for (const BudgetChunk& chunk : info.chunks)
165 total += chunk.amount; 190 total += chunk.amount;
166 191
167 if (total < amount) { 192 if (total < amount) {
193 UMA_HISTOGRAM_COUNTS_100("PushMessaging.SESForNoBudgetOrigin", score);
168 callback.Run(false /* success */); 194 callback.Run(false /* success */);
169 return; 195 return;
196 } else if (total < amount * 2) {
197 UMA_HISTOGRAM_COUNTS_100("PushMessaging.SESForLowBudgetOrigin", score);
170 } 198 }
171 199
172 // Walk the chunks and remove enough budget to cover the needed amount. 200 // Walk the chunks and remove enough budget to cover the needed amount.
173 double bill = amount; 201 double bill = amount;
174 for (auto iter = info.chunks.begin(); iter != info.chunks.end();) { 202 for (auto iter = info.chunks.begin(); iter != info.chunks.end();) {
175 if (iter->amount > bill) { 203 if (iter->amount > bill) {
176 iter->amount -= bill; 204 iter->amount -= bill;
177 bill = 0; 205 bill = 0;
178 break; 206 break;
179 } 207 }
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
245 } 273 }
246 274
247 void BudgetDatabase::SyncLoadedCache(const GURL& origin, 275 void BudgetDatabase::SyncLoadedCache(const GURL& origin,
248 const SyncCacheCallback& callback, 276 const SyncCacheCallback& callback,
249 bool success) { 277 bool success) {
250 if (!success) { 278 if (!success) {
251 callback.Run(false /* success */); 279 callback.Run(false /* success */);
252 return; 280 return;
253 } 281 }
254 282
283 // Now, cleanup any expired budget chunks for the origin.
284 bool needs_write = CleanupExpiredBudget(origin);
285
255 // Get the SES score and add engagement budget for the site. 286 // Get the SES score and add engagement budget for the site.
256 AddEngagementBudget(origin); 287 AddEngagementBudget(origin);
257 288
258 // Now, cleanup any expired budget chunks for the origin.
259 bool needs_write = CleanupExpiredBudget(origin);
260
261 if (needs_write) 289 if (needs_write)
262 WriteCachedValuesToDatabase(origin, callback); 290 WriteCachedValuesToDatabase(origin, callback);
263 else 291 else
264 callback.Run(success); 292 callback.Run(success);
265 } 293 }
266 294
267 void BudgetDatabase::AddEngagementBudget(const GURL& origin) { 295 void BudgetDatabase::AddEngagementBudget(const GURL& origin) {
268 // Get the current SES score, which we'll use to set a new budget. 296 // Get the current SES score, which we'll use to set a new budget.
269 SiteEngagementService* service = SiteEngagementService::Get(profile_); 297 SiteEngagementService* service = SiteEngagementService::Get(profile_);
270 double score = service->GetScore(origin); 298 double score = service->GetScore(origin);
(...skipping 16 matching lines...) Expand all
287 } 315 }
288 316
289 // Update the last_engagement_award to the current time. If the origin wasn't 317 // Update the last_engagement_award to the current time. If the origin wasn't
290 // already in the map, this adds a new entry for it. 318 // already in the map, this adds a new entry for it.
291 budget_map_[origin.spec()].last_engagement_award = clock_->Now(); 319 budget_map_[origin.spec()].last_engagement_award = clock_->Now();
292 320
293 // Add a new chunk of budget for the origin at the default expiration time. 321 // Add a new chunk of budget for the origin at the default expiration time.
294 base::Time expiration = 322 base::Time expiration =
295 clock_->Now() + base::TimeDelta::FromHours(kBudgetDurationInHours); 323 clock_->Now() + base::TimeDelta::FromHours(kBudgetDurationInHours);
296 budget_map_[origin.spec()].chunks.emplace_back(ratio * score, expiration); 324 budget_map_[origin.spec()].chunks.emplace_back(ratio * score, expiration);
325
326 // Any time we award engagement budget, which is done at most once an hour
327 // whenever any budget action is taken, record the budget.
328 double budget = GetBudget(origin);
329 UMA_HISTOGRAM_COUNTS_100("PushMessaging.BackgroundBudget", budget);
297 } 330 }
298 331
299 // Cleans up budget in the cache. Relies on the caller eventually writing the 332 // Cleans up budget in the cache. Relies on the caller eventually writing the
300 // cache back to the database. 333 // cache back to the database.
301 bool BudgetDatabase::CleanupExpiredBudget(const GURL& origin) { 334 bool BudgetDatabase::CleanupExpiredBudget(const GURL& origin) {
302 if (!IsCached(origin)) 335 if (!IsCached(origin))
303 return false; 336 return false;
304 337
305 base::Time now = clock_->Now(); 338 base::Time now = clock_->Now();
306 BudgetChunks& chunks = budget_map_[origin.spec()].chunks; 339 BudgetChunks& chunks = budget_map_[origin.spec()].chunks;
(...skipping 10 matching lines...) Expand all
317 clock_->Now() - base::TimeDelta::FromHours(kBudgetDurationInHours)) { 350 clock_->Now() - base::TimeDelta::FromHours(kBudgetDurationInHours)) {
318 budget_map_.erase(origin.spec()); 351 budget_map_.erase(origin.spec());
319 return true; 352 return true;
320 } 353 }
321 354
322 // Although some things may have expired, there are some chunks still valid. 355 // Although some things may have expired, there are some chunks still valid.
323 // Don't write to the DB now, write either when all chunks expire or when the 356 // Don't write to the DB now, write either when all chunks expire or when the
324 // origin spends some budget. 357 // origin spends some budget.
325 return false; 358 return false;
326 } 359 }
OLDNEW
« no previous file with comments | « chrome/browser/budget_service/budget_database.h ('k') | chrome/browser/budget_service/budget_database_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698