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

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: 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/containers/adapters.h"
Peter Beverloo 2016/08/26 14:56:43 nit: unused
harkness 2016/08/31 13:15:35 Done.
8 #include "base/metrics/histogram_macros.h"
8 #include "base/time/clock.h" 9 #include "base/time/clock.h"
9 #include "base/time/default_clock.h" 10 #include "base/time/default_clock.h"
10 #include "chrome/browser/budget_service/budget.pb.h" 11 #include "chrome/browser/budget_service/budget.pb.h"
11 #include "chrome/browser/engagement/site_engagement_score.h" 12 #include "chrome/browser/engagement/site_engagement_score.h"
12 #include "chrome/browser/engagement/site_engagement_service.h" 13 #include "chrome/browser/engagement/site_engagement_service.h"
13 #include "chrome/browser/profiles/profile.h" 14 #include "chrome/browser/profiles/profile.h"
14 #include "components/leveldb_proto/proto_database_impl.h" 15 #include "components/leveldb_proto/proto_database_impl.h"
15 #include "content/public/browser/browser_thread.h" 16 #include "content/public/browser/browser_thread.h"
16 #include "url/gurl.h" 17 #include "url/gurl.h"
17 18
18 using content::BrowserThread; 19 using content::BrowserThread;
19 20
20 namespace { 21 namespace {
21 22
22 // UMA are logged for the database with this string as part of the name. 23 // 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 24 // They will be LevelDB.*.BudgetManager. Changes here should be synchronized
24 // with histograms.xml. 25 // with histograms.xml.
25 const char kDatabaseUMAName[] = "BudgetManager"; 26 const char kDatabaseUMAName[] = "BudgetManager";
26 27
27 // The default amount of time during which a budget will be valid. 28 // The default amount of time during which a budget will be valid.
28 // This is 3 days = 72 hours. 29 // This is 10 days = 240 hours.
harkness 2016/08/25 19:51:19 I went ahead and kept it at 10 days. We can collec
Peter Beverloo 2016/08/26 14:56:43 Acknowledged.
29 constexpr double kBudgetDurationInHours = 72; 30 constexpr double kBudgetDurationInHours = 240;
30 31
31 } // namespace 32 } // namespace
32 33
33 BudgetDatabase::BudgetInfo::BudgetInfo() {} 34 BudgetDatabase::BudgetInfo::BudgetInfo() {}
34 35
35 BudgetDatabase::BudgetInfo::BudgetInfo(const BudgetInfo&& other) 36 BudgetDatabase::BudgetInfo::BudgetInfo(const BudgetInfo&& other)
36 : last_engagement_award(other.last_engagement_award) { 37 : last_engagement_award(other.last_engagement_award) {
37 chunks = std::move(other.chunks); 38 chunks = std::move(other.chunks);
38 } 39 }
39 40
40 BudgetDatabase::BudgetInfo::~BudgetInfo() {} 41 BudgetDatabase::BudgetInfo::~BudgetInfo() {}
41 42
42 BudgetDatabase::BudgetDatabase( 43 BudgetDatabase::BudgetDatabase(
43 Profile* profile, 44 Profile* profile,
44 const base::FilePath& database_dir, 45 const base::FilePath& database_dir,
45 const scoped_refptr<base::SequencedTaskRunner>& task_runner) 46 const scoped_refptr<base::SequencedTaskRunner>& task_runner)
46 : profile_(profile), 47 : profile_(profile),
47 db_(new leveldb_proto::ProtoDatabaseImpl<budget_service::Budget>( 48 db_(new leveldb_proto::ProtoDatabaseImpl<budget_service::Budget>(
48 task_runner)), 49 task_runner)),
49 clock_(base::WrapUnique(new base::DefaultClock)), 50 clock_(base::WrapUnique(new base::DefaultClock)),
50 weak_ptr_factory_(this) { 51 weak_ptr_factory_(this) {
51 db_->Init(kDatabaseUMAName, database_dir, 52 db_->Init(kDatabaseUMAName, database_dir,
52 base::Bind(&BudgetDatabase::OnDatabaseInit, 53 base::Bind(&BudgetDatabase::OnDatabaseInit,
53 weak_ptr_factory_.GetWeakPtr())); 54 weak_ptr_factory_.GetWeakPtr()));
54 } 55 }
55 56
56 BudgetDatabase::~BudgetDatabase() {} 57 BudgetDatabase::~BudgetDatabase() {}
57 58
58 void BudgetDatabase::GetBudgetDetails( 59 void BudgetDatabase::GetBudgetDetails(const GURL& origin,
59 const GURL& origin, 60 const GetBudgetCallback& callback) {
60 const GetBudgetDetailsCallback& callback) {
61 DCHECK_EQ(origin.GetOrigin(), origin); 61 DCHECK_EQ(origin.GetOrigin(), origin);
62 62
63 SyncCache(origin, 63 SyncCache(origin,
64 base::Bind(&BudgetDatabase::GetBudgetAfterSync, 64 base::Bind(&BudgetDatabase::GetBudgetAfterSync,
65 weak_ptr_factory_.GetWeakPtr(), origin, callback)); 65 weak_ptr_factory_.GetWeakPtr(), origin, callback));
66 } 66 }
67 67
68 void BudgetDatabase::SpendBudget(const GURL& origin, 68 void BudgetDatabase::SpendBudget(const GURL& origin,
69 double amount, 69 double amount,
70 const StoreBudgetCallback& callback) { 70 const StoreBudgetCallback& callback) {
71 SyncCache(origin, base::Bind(&BudgetDatabase::SpendBudgetAfterSync, 71 SyncCache(origin, base::Bind(&BudgetDatabase::SpendBudgetAfterSync,
72 weak_ptr_factory_.GetWeakPtr(), origin, amount, 72 weak_ptr_factory_.GetWeakPtr(), origin, amount,
73 callback)); 73 callback));
74 } 74 }
75 75
76 void BudgetDatabase::SetClockForTesting(std::unique_ptr<base::Clock> clock) { 76 void BudgetDatabase::SetClockForTesting(std::unique_ptr<base::Clock> clock) {
77 clock_ = std::move(clock); 77 clock_ = std::move(clock);
78 } 78 }
79 79
80 void BudgetDatabase::OnDatabaseInit(bool success) { 80 void BudgetDatabase::OnDatabaseInit(bool success) {
81 // TODO(harkness): Consider caching the budget database now? 81 // TODO(harkness): Consider caching the budget database now?
82 } 82 }
83 83
84 bool BudgetDatabase::IsCached(const GURL& origin) const { 84 bool BudgetDatabase::IsCached(const GURL& origin) const {
85 return budget_map_.find(origin.spec()) != budget_map_.end(); 85 return budget_map_.find(origin.spec()) != budget_map_.end();
86 } 86 }
87 87
88 double BudgetDatabase::GetBudget(const GURL& origin) {
harkness 2016/08/25 19:51:19 We need this because the data structure for mojo i
89 double total = 0.0;
90 BudgetInfo& info = budget_map_[origin.spec()];
91 for (const BudgetChunk& chunk : info.chunks)
92 total += chunk.amount;
93 return total;
94 }
95
88 void BudgetDatabase::AddToCache( 96 void BudgetDatabase::AddToCache(
89 const GURL& origin, 97 const GURL& origin,
90 const AddToCacheCallback& callback, 98 const AddToCacheCallback& callback,
91 bool success, 99 bool success,
92 std::unique_ptr<budget_service::Budget> budget_proto) { 100 std::unique_ptr<budget_service::Budget> budget_proto) {
93 // If the database read failed, there's nothing to add to the cache. 101 // If the database read failed, there's nothing to add to the cache.
94 if (!success || !budget_proto) { 102 if (!success || !budget_proto) {
95 callback.Run(success); 103 callback.Run(success);
96 return; 104 return;
97 } 105 }
(...skipping 12 matching lines...) Expand all
110 info.chunks.emplace_back(chunk.amount(), 118 info.chunks.emplace_back(chunk.amount(),
111 base::Time::FromInternalValue(chunk.expiration())); 119 base::Time::FromInternalValue(chunk.expiration()));
112 } 120 }
113 121
114 info.last_engagement_award = 122 info.last_engagement_award =
115 base::Time::FromInternalValue(budget_proto->engagement_last_updated()); 123 base::Time::FromInternalValue(budget_proto->engagement_last_updated());
116 124
117 callback.Run(success); 125 callback.Run(success);
118 } 126 }
119 127
120 void BudgetDatabase::GetBudgetAfterSync( 128 void BudgetDatabase::GetBudgetAfterSync(const GURL& origin,
121 const GURL& origin, 129 const GetBudgetCallback& callback,
122 const GetBudgetDetailsCallback& callback, 130 bool success) {
123 bool success) { 131 mojo::Array<blink::mojom::BudgetStatePtr> predictions;
132
124 // If the database wasn't able to read the information, return the 133 // If the database wasn't able to read the information, return the
125 // failure and an empty BudgetPrediction. 134 // failure and an empty predictions array.
126 if (!success) { 135 if (!success) {
127 callback.Run(success, BudgetPrediction()); 136 // TODO(harkness): Add a status enum to this call.
137 callback.Run(std::move(predictions));
harkness 2016/08/25 19:51:19 I'll add this when I add Reserve to the mojo servi
128 return; 138 return;
129 } 139 }
130 140
131 // Now, build up the BudgetExpection. This is different from the format 141 // 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 142 // 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 143 // when that budget expires. The mojo array describes a set of times
134 // and the budget at those times. 144 // and the budget at those times.
135 BudgetPrediction prediction; 145 double total = GetBudget(origin);
136 double total = 0;
137 146
138 // Starting with the chunks that expire the farthest in the future, build up 147 // Always add one entry at the front of the list for the total budget now.
139 // the budget predictions for those future times. 148 blink::mojom::BudgetStatePtr prediction(blink::mojom::BudgetState::New());
149 prediction->budget_at = total;
150 prediction->time = clock_->Now().ToDoubleT();
151 predictions.push_back(std::move(prediction));
152
153 // Starting with the soonest expiring chunks, add entries for the
154 // expiration times going forward.
140 const BudgetChunks& chunks = budget_map_[origin.spec()].chunks; 155 const BudgetChunks& chunks = budget_map_[origin.spec()].chunks;
141 for (const auto& chunk : base::Reversed(chunks)) { 156 for (const auto& chunk : chunks) {
142 prediction.emplace_front(total, chunk.expiration); 157 blink::mojom::BudgetStatePtr prediction(blink::mojom::BudgetState::New());
143 total += chunk.amount; 158 total -= chunk.amount;
159 prediction->budget_at = total;
160 prediction->time = chunk.expiration.ToDoubleT();
161 predictions.push_back(std::move(prediction));
144 } 162 }
145 163
146 // Always add one entry at the front of the list for the total budget now. 164 // TODO(harkness) Add status enum.
147 prediction.emplace_front(total, clock_->Now()); 165 callback.Run(std::move(predictions));
148
149 callback.Run(true /* success */, prediction);
150 } 166 }
151 167
152 void BudgetDatabase::SpendBudgetAfterSync(const GURL& origin, 168 void BudgetDatabase::SpendBudgetAfterSync(const GURL& origin,
153 double amount, 169 double amount,
154 const StoreBudgetCallback& callback, 170 const StoreBudgetCallback& callback,
155 bool success) { 171 bool success) {
156 if (!success) { 172 if (!success) {
157 callback.Run(false /* success */); 173 callback.Run(false /* success */);
158 return; 174 return;
159 } 175 }
160 176
177 // Get the current SES score, to generate UMA..
178 SiteEngagementService* service = SiteEngagementService::Get(profile_);
179 double score = service->GetScore(origin);
180
161 // Walk the list of budget chunks to see if the origin has enough budget. 181 // Walk the list of budget chunks to see if the origin has enough budget.
162 double total = 0; 182 double total = 0;
163 BudgetInfo& info = budget_map_[origin.spec()]; 183 BudgetInfo& info = budget_map_[origin.spec()];
164 for (const BudgetChunk& chunk : info.chunks) 184 for (const BudgetChunk& chunk : info.chunks)
165 total += chunk.amount; 185 total += chunk.amount;
166 186
167 if (total < amount) { 187 if (total < amount) {
188 UMA_HISTOGRAM_COUNTS_100("PushMessaging.SESForNoBudgetOrigin", score);
168 callback.Run(false /* success */); 189 callback.Run(false /* success */);
169 return; 190 return;
191 } else if (total < amount * 2) {
192 UMA_HISTOGRAM_COUNTS_100("PushMessaging.SESForLowBudgetOrigin", score);
Peter Beverloo 2016/08/26 14:56:43 We tried to hard to keep these out of here :( Let'
harkness 2016/08/31 13:15:35 Acknowledged.
170 } 193 }
171 194
172 // Walk the chunks and remove enough budget to cover the needed amount. 195 // Walk the chunks and remove enough budget to cover the needed amount.
173 double bill = amount; 196 double bill = amount;
174 for (auto iter = info.chunks.begin(); iter != info.chunks.end();) { 197 for (auto iter = info.chunks.begin(); iter != info.chunks.end();) {
175 if (iter->amount > bill) { 198 if (iter->amount > bill) {
176 iter->amount -= bill; 199 iter->amount -= bill;
177 bill = 0; 200 bill = 0;
178 break; 201 break;
179 } 202 }
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
287 } 310 }
288 311
289 // Update the last_engagement_award to the current time. If the origin wasn't 312 // 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. 313 // already in the map, this adds a new entry for it.
291 budget_map_[origin.spec()].last_engagement_award = clock_->Now(); 314 budget_map_[origin.spec()].last_engagement_award = clock_->Now();
292 315
293 // Add a new chunk of budget for the origin at the default expiration time. 316 // Add a new chunk of budget for the origin at the default expiration time.
294 base::Time expiration = 317 base::Time expiration =
295 clock_->Now() + base::TimeDelta::FromHours(kBudgetDurationInHours); 318 clock_->Now() + base::TimeDelta::FromHours(kBudgetDurationInHours);
296 budget_map_[origin.spec()].chunks.emplace_back(ratio * score, expiration); 319 budget_map_[origin.spec()].chunks.emplace_back(ratio * score, expiration);
320
321 // Any time we award engagement budget, which is done at most once an hour
322 // whenever any budget action is taken, record the budget.
323 double budget = GetBudget(origin);
324 UMA_HISTOGRAM_COUNTS_100("PushMessaging.BackgroundBudget", budget);
297 } 325 }
298 326
299 // Cleans up budget in the cache. Relies on the caller eventually writing the 327 // Cleans up budget in the cache. Relies on the caller eventually writing the
300 // cache back to the database. 328 // cache back to the database.
301 bool BudgetDatabase::CleanupExpiredBudget(const GURL& origin) { 329 bool BudgetDatabase::CleanupExpiredBudget(const GURL& origin) {
302 if (!IsCached(origin))
303 return false;
304
305 base::Time now = clock_->Now(); 330 base::Time now = clock_->Now();
306 BudgetChunks& chunks = budget_map_[origin.spec()].chunks; 331 BudgetChunks& chunks = budget_map_[origin.spec()].chunks;
307 auto cleanup_iter = chunks.begin(); 332 auto cleanup_iter = chunks.begin();
308 333
309 // This relies on the list of chunks being in timestamp order. 334 // This relies on the list of chunks being in timestamp order.
310 while (cleanup_iter != chunks.end() && cleanup_iter->expiration <= now) 335 while (cleanup_iter != chunks.end() && cleanup_iter->expiration <= now)
311 cleanup_iter = chunks.erase(cleanup_iter); 336 cleanup_iter = chunks.erase(cleanup_iter);
312 337
313 // If the entire budget is empty now AND there have been no engagements 338 // If the entire budget is empty now AND there have been no engagements
314 // in the last kBudgetDurationInHours hours, remove this from the cache. 339 // in the last kBudgetDurationInHours hours, remove this from the cache.
315 if (chunks.empty() && 340 if (chunks.empty() &&
316 budget_map_[origin.spec()].last_engagement_award < 341 budget_map_[origin.spec()].last_engagement_award <
317 clock_->Now() - base::TimeDelta::FromHours(kBudgetDurationInHours)) { 342 clock_->Now() - base::TimeDelta::FromHours(kBudgetDurationInHours)) {
318 budget_map_.erase(origin.spec()); 343 budget_map_.erase(origin.spec());
319 return true; 344 return true;
320 } 345 }
321 346
322 // Although some things may have expired, there are some chunks still valid. 347 // 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 348 // Don't write to the DB now, write either when all chunks expire or when the
324 // origin spends some budget. 349 // origin spends some budget.
325 return false; 350 return false;
326 } 351 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698