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

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: Rebase and integrate with Reserve plumbing and new error type parameter. 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) {
Peter Beverloo 2016/09/07 16:56:35 optional nit: could be const
harkness 2016/09/07 17:10:29 Acknowledged.
88 double total = 0;
89 BudgetInfo& info = budget_map_[origin.spec()];
90 for (const BudgetChunk& chunk : info.chunks)
91 total += chunk.amount;
92 return total;
93 }
94
88 void BudgetDatabase::AddToCache( 95 void BudgetDatabase::AddToCache(
89 const GURL& origin, 96 const GURL& origin,
90 const AddToCacheCallback& callback, 97 const AddToCacheCallback& callback,
91 bool success, 98 bool success,
92 std::unique_ptr<budget_service::Budget> budget_proto) { 99 std::unique_ptr<budget_service::Budget> budget_proto) {
93 // If the database read failed, there's nothing to add to the cache. 100 // If the database read failed, there's nothing to add to the cache.
94 if (!success || !budget_proto) { 101 if (!success || !budget_proto) {
95 callback.Run(success); 102 callback.Run(success);
96 return; 103 return;
97 } 104 }
(...skipping 12 matching lines...) Expand all
110 info.chunks.emplace_back(chunk.amount(), 117 info.chunks.emplace_back(chunk.amount(),
111 base::Time::FromInternalValue(chunk.expiration())); 118 base::Time::FromInternalValue(chunk.expiration()));
112 } 119 }
113 120
114 info.last_engagement_award = 121 info.last_engagement_award =
115 base::Time::FromInternalValue(budget_proto->engagement_last_updated()); 122 base::Time::FromInternalValue(budget_proto->engagement_last_updated());
116 123
117 callback.Run(success); 124 callback.Run(success);
118 } 125 }
119 126
120 void BudgetDatabase::GetBudgetAfterSync( 127 void BudgetDatabase::GetBudgetAfterSync(const GURL& origin,
121 const GURL& origin, 128 const GetBudgetCallback& callback,
122 const GetBudgetDetailsCallback& callback, 129 bool success) {
123 bool success) { 130 mojo::Array<blink::mojom::BudgetStatePtr> predictions;
131
124 // If the database wasn't able to read the information, return the 132 // If the database wasn't able to read the information, return the
125 // failure and an empty BudgetPrediction. 133 // failure and an empty predictions array.
126 if (!success) { 134 if (!success) {
127 callback.Run(success, BudgetPrediction()); 135 callback.Run(blink::mojom::BudgetServiceErrorType::DATABASE_ERROR,
136 std::move(predictions));
128 return; 137 return;
129 } 138 }
130 139
131 // Now, build up the BudgetExpection. This is different from the format 140 // 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 141 // 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 142 // when that budget expires. The mojo array describes a set of times
134 // and the budget at those times. 143 // and the budget at those times.
135 BudgetPrediction prediction; 144 double total = GetBudget(origin);
136 double total = 0;
137 145
138 // Starting with the chunks that expire the farthest in the future, build up 146 // Always add one entry at the front of the list for the total budget now.
139 // the budget predictions for those future times. 147 blink::mojom::BudgetStatePtr prediction(blink::mojom::BudgetState::New());
148 prediction->budget_at = total;
149 prediction->time = clock_->Now().ToDoubleT();
150 predictions.push_back(std::move(prediction));
151
152 // Starting with the soonest expiring chunks, add entries for the
153 // expiration times going forward.
140 const BudgetChunks& chunks = budget_map_[origin.spec()].chunks; 154 const BudgetChunks& chunks = budget_map_[origin.spec()].chunks;
141 for (const auto& chunk : base::Reversed(chunks)) { 155 for (const auto& chunk : chunks) {
142 prediction.emplace_front(total, chunk.expiration); 156 blink::mojom::BudgetStatePtr prediction(blink::mojom::BudgetState::New());
143 total += chunk.amount; 157 total -= chunk.amount;
158 prediction->budget_at = total;
159 prediction->time = chunk.expiration.ToDoubleT();
160 predictions.push_back(std::move(prediction));
144 } 161 }
145 162
146 // Always add one entry at the front of the list for the total budget now. 163 DCHECK_EQ(0, total);
147 prediction.emplace_front(total, clock_->Now());
148 164
149 callback.Run(true /* success */, prediction); 165 callback.Run(blink::mojom::BudgetServiceErrorType::NONE,
166 std::move(predictions));
150 } 167 }
151 168
152 void BudgetDatabase::SpendBudgetAfterSync(const GURL& origin, 169 void BudgetDatabase::SpendBudgetAfterSync(const GURL& origin,
153 double amount, 170 double amount,
154 const StoreBudgetCallback& callback, 171 const StoreBudgetCallback& callback,
155 bool success) { 172 bool success) {
156 if (!success) { 173 if (!success) {
157 callback.Run(false /* success */); 174 callback.Run(false /* success */);
158 return; 175 return;
159 } 176 }
160 177
178 // Get the current SES score, to generate UMA.
179 SiteEngagementService* service = SiteEngagementService::Get(profile_);
180 double score = service->GetScore(origin);
181
161 // Walk the list of budget chunks to see if the origin has enough budget. 182 // Walk the list of budget chunks to see if the origin has enough budget.
162 double total = 0; 183 double total = 0;
163 BudgetInfo& info = budget_map_[origin.spec()]; 184 BudgetInfo& info = budget_map_[origin.spec()];
164 for (const BudgetChunk& chunk : info.chunks) 185 for (const BudgetChunk& chunk : info.chunks)
165 total += chunk.amount; 186 total += chunk.amount;
166 187
167 if (total < amount) { 188 if (total < amount) {
189 UMA_HISTOGRAM_COUNTS_100("PushMessaging.SESForNoBudgetOrigin", score);
168 callback.Run(false /* success */); 190 callback.Run(false /* success */);
169 return; 191 return;
192 } else if (total < amount * 2) {
193 UMA_HISTOGRAM_COUNTS_100("PushMessaging.SESForLowBudgetOrigin", score);
170 } 194 }
171 195
172 // Walk the chunks and remove enough budget to cover the needed amount. 196 // Walk the chunks and remove enough budget to cover the needed amount.
173 double bill = amount; 197 double bill = amount;
174 for (auto iter = info.chunks.begin(); iter != info.chunks.end();) { 198 for (auto iter = info.chunks.begin(); iter != info.chunks.end();) {
175 if (iter->amount > bill) { 199 if (iter->amount > bill) {
176 iter->amount -= bill; 200 iter->amount -= bill;
177 bill = 0; 201 bill = 0;
178 break; 202 break;
179 } 203 }
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
245 } 269 }
246 270
247 void BudgetDatabase::SyncLoadedCache(const GURL& origin, 271 void BudgetDatabase::SyncLoadedCache(const GURL& origin,
248 const SyncCacheCallback& callback, 272 const SyncCacheCallback& callback,
249 bool success) { 273 bool success) {
250 if (!success) { 274 if (!success) {
251 callback.Run(false /* success */); 275 callback.Run(false /* success */);
252 return; 276 return;
253 } 277 }
254 278
279 // Now, cleanup any expired budget chunks for the origin.
280 bool needs_write = CleanupExpiredBudget(origin);
281
255 // Get the SES score and add engagement budget for the site. 282 // Get the SES score and add engagement budget for the site.
256 AddEngagementBudget(origin); 283 AddEngagementBudget(origin);
257 284
258 // Now, cleanup any expired budget chunks for the origin.
259 bool needs_write = CleanupExpiredBudget(origin);
260
261 if (needs_write) 285 if (needs_write)
262 WriteCachedValuesToDatabase(origin, callback); 286 WriteCachedValuesToDatabase(origin, callback);
263 else 287 else
264 callback.Run(success); 288 callback.Run(success);
265 } 289 }
266 290
267 void BudgetDatabase::AddEngagementBudget(const GURL& origin) { 291 void BudgetDatabase::AddEngagementBudget(const GURL& origin) {
268 // Get the current SES score, which we'll use to set a new budget. 292 // Get the current SES score, which we'll use to set a new budget.
269 SiteEngagementService* service = SiteEngagementService::Get(profile_); 293 SiteEngagementService* service = SiteEngagementService::Get(profile_);
270 double score = service->GetScore(origin); 294 double score = service->GetScore(origin);
(...skipping 16 matching lines...) Expand all
287 } 311 }
288 312
289 // Update the last_engagement_award to the current time. If the origin wasn't 313 // 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. 314 // already in the map, this adds a new entry for it.
291 budget_map_[origin.spec()].last_engagement_award = clock_->Now(); 315 budget_map_[origin.spec()].last_engagement_award = clock_->Now();
292 316
293 // Add a new chunk of budget for the origin at the default expiration time. 317 // Add a new chunk of budget for the origin at the default expiration time.
294 base::Time expiration = 318 base::Time expiration =
295 clock_->Now() + base::TimeDelta::FromHours(kBudgetDurationInHours); 319 clock_->Now() + base::TimeDelta::FromHours(kBudgetDurationInHours);
296 budget_map_[origin.spec()].chunks.emplace_back(ratio * score, expiration); 320 budget_map_[origin.spec()].chunks.emplace_back(ratio * score, expiration);
321
322 // Any time we award engagement budget, which is done at most once an hour
323 // whenever any budget action is taken, record the budget.
324 double budget = GetBudget(origin);
325 UMA_HISTOGRAM_COUNTS_100("PushMessaging.BackgroundBudget", budget);
297 } 326 }
298 327
299 // Cleans up budget in the cache. Relies on the caller eventually writing the 328 // Cleans up budget in the cache. Relies on the caller eventually writing the
300 // cache back to the database. 329 // cache back to the database.
301 bool BudgetDatabase::CleanupExpiredBudget(const GURL& origin) { 330 bool BudgetDatabase::CleanupExpiredBudget(const GURL& origin) {
302 if (!IsCached(origin)) 331 if (!IsCached(origin))
303 return false; 332 return false;
304 333
305 base::Time now = clock_->Now(); 334 base::Time now = clock_->Now();
306 BudgetChunks& chunks = budget_map_[origin.spec()].chunks; 335 BudgetChunks& chunks = budget_map_[origin.spec()].chunks;
(...skipping 10 matching lines...) Expand all
317 clock_->Now() - base::TimeDelta::FromHours(kBudgetDurationInHours)) { 346 clock_->Now() - base::TimeDelta::FromHours(kBudgetDurationInHours)) {
318 budget_map_.erase(origin.spec()); 347 budget_map_.erase(origin.spec());
319 return true; 348 return true;
320 } 349 }
321 350
322 // Although some things may have expired, there are some chunks still valid. 351 // 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 352 // Don't write to the DB now, write either when all chunks expire or when the
324 // origin spends some budget. 353 // origin spends some budget.
325 return false; 354 return false;
326 } 355 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698