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

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: Fixed hang and cleaned up browsertests 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) {
88 double total = 0.0;
Peter Beverloo 2016/09/05 14:39:03 nit: s/0.0/0/
harkness 2016/09/06 13:28:39 Done.
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 // TODO(harkness): Add a status enum to this call.
136 callback.Run(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
Peter Beverloo 2016/09/05 14:39:03 Can we DCHECK_EQ(0, total)?
harkness 2016/09/06 13:28:39 Done.
146 // Always add one entry at the front of the list for the total budget now. 163 // TODO(harkness) Add status enum.
147 prediction.emplace_front(total, clock_->Now()); 164 callback.Run(std::move(predictions));
148
149 callback.Run(true /* success */, prediction);
150 } 165 }
151 166
152 void BudgetDatabase::SpendBudgetAfterSync(const GURL& origin, 167 void BudgetDatabase::SpendBudgetAfterSync(const GURL& origin,
153 double amount, 168 double amount,
154 const StoreBudgetCallback& callback, 169 const StoreBudgetCallback& callback,
155 bool success) { 170 bool success) {
156 if (!success) { 171 if (!success) {
157 callback.Run(false /* success */); 172 callback.Run(false /* success */);
158 return; 173 return;
159 } 174 }
160 175
176 // Get the current SES score, to generate UMA..
Peter Beverloo 2016/09/05 14:39:03 nit: .. -> .
harkness 2016/09/06 13:28:39 Done.
177 SiteEngagementService* service = SiteEngagementService::Get(profile_);
178 double score = service->GetScore(origin);
179
161 // Walk the list of budget chunks to see if the origin has enough budget. 180 // Walk the list of budget chunks to see if the origin has enough budget.
162 double total = 0; 181 double total = 0;
163 BudgetInfo& info = budget_map_[origin.spec()]; 182 BudgetInfo& info = budget_map_[origin.spec()];
164 for (const BudgetChunk& chunk : info.chunks) 183 for (const BudgetChunk& chunk : info.chunks)
165 total += chunk.amount; 184 total += chunk.amount;
166 185
167 if (total < amount) { 186 if (total < amount) {
187 UMA_HISTOGRAM_COUNTS_100("PushMessaging.SESForNoBudgetOrigin", score);
168 callback.Run(false /* success */); 188 callback.Run(false /* success */);
169 return; 189 return;
190 } else if (total < amount * 2) {
191 UMA_HISTOGRAM_COUNTS_100("PushMessaging.SESForLowBudgetOrigin", score);
170 } 192 }
171 193
172 // Walk the chunks and remove enough budget to cover the needed amount. 194 // Walk the chunks and remove enough budget to cover the needed amount.
173 double bill = amount; 195 double bill = amount;
174 for (auto iter = info.chunks.begin(); iter != info.chunks.end();) { 196 for (auto iter = info.chunks.begin(); iter != info.chunks.end();) {
175 if (iter->amount > bill) { 197 if (iter->amount > bill) {
176 iter->amount -= bill; 198 iter->amount -= bill;
177 bill = 0; 199 bill = 0;
178 break; 200 break;
179 } 201 }
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
287 } 309 }
288 310
289 // Update the last_engagement_award to the current time. If the origin wasn't 311 // 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. 312 // already in the map, this adds a new entry for it.
291 budget_map_[origin.spec()].last_engagement_award = clock_->Now(); 313 budget_map_[origin.spec()].last_engagement_award = clock_->Now();
292 314
293 // Add a new chunk of budget for the origin at the default expiration time. 315 // Add a new chunk of budget for the origin at the default expiration time.
294 base::Time expiration = 316 base::Time expiration =
295 clock_->Now() + base::TimeDelta::FromHours(kBudgetDurationInHours); 317 clock_->Now() + base::TimeDelta::FromHours(kBudgetDurationInHours);
296 budget_map_[origin.spec()].chunks.emplace_back(ratio * score, expiration); 318 budget_map_[origin.spec()].chunks.emplace_back(ratio * score, expiration);
319
320 // Any time we award engagement budget, which is done at most once an hour
321 // whenever any budget action is taken, record the budget.
322 double budget = GetBudget(origin);
323 UMA_HISTOGRAM_COUNTS_100("PushMessaging.BackgroundBudget", budget);
297 } 324 }
298 325
299 // Cleans up budget in the cache. Relies on the caller eventually writing the 326 // Cleans up budget in the cache. Relies on the caller eventually writing the
300 // cache back to the database. 327 // cache back to the database.
301 bool BudgetDatabase::CleanupExpiredBudget(const GURL& origin) { 328 bool BudgetDatabase::CleanupExpiredBudget(const GURL& origin) {
302 if (!IsCached(origin))
303 return false;
304
305 base::Time now = clock_->Now(); 329 base::Time now = clock_->Now();
306 BudgetChunks& chunks = budget_map_[origin.spec()].chunks; 330 BudgetChunks& chunks = budget_map_[origin.spec()].chunks;
307 auto cleanup_iter = chunks.begin(); 331 auto cleanup_iter = chunks.begin();
308 332
309 // This relies on the list of chunks being in timestamp order. 333 // This relies on the list of chunks being in timestamp order.
310 while (cleanup_iter != chunks.end() && cleanup_iter->expiration <= now) 334 while (cleanup_iter != chunks.end() && cleanup_iter->expiration <= now)
311 cleanup_iter = chunks.erase(cleanup_iter); 335 cleanup_iter = chunks.erase(cleanup_iter);
312 336
313 // If the entire budget is empty now AND there have been no engagements 337 // If the entire budget is empty now AND there have been no engagements
314 // in the last kBudgetDurationInHours hours, remove this from the cache. 338 // in the last kBudgetDurationInHours hours, remove this from the cache.
315 if (chunks.empty() && 339 if (chunks.empty() &&
316 budget_map_[origin.spec()].last_engagement_award < 340 budget_map_[origin.spec()].last_engagement_award <
317 clock_->Now() - base::TimeDelta::FromHours(kBudgetDurationInHours)) { 341 clock_->Now() - base::TimeDelta::FromHours(kBudgetDurationInHours)) {
318 budget_map_.erase(origin.spec()); 342 budget_map_.erase(origin.spec());
319 return true; 343 return true;
320 } 344 }
321 345
322 // Although some things may have expired, there are some chunks still valid. 346 // 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 347 // Don't write to the DB now, write either when all chunks expire or when the
324 // origin spends some budget. 348 // origin spends some budget.
325 return false; 349 return false;
326 } 350 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698