Chromium Code Reviews| OLD | NEW |
|---|---|
| 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/metrics/histogram_macros.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" |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 59 const GetBudgetCallback& callback) { | 59 const GetBudgetCallback& callback) { |
| 60 DCHECK_EQ(origin.GetOrigin(), origin); | 60 DCHECK_EQ(origin.GetOrigin(), origin); |
| 61 | 61 |
| 62 SyncCache(origin, | 62 SyncCache(origin, |
| 63 base::Bind(&BudgetDatabase::GetBudgetAfterSync, | 63 base::Bind(&BudgetDatabase::GetBudgetAfterSync, |
| 64 weak_ptr_factory_.GetWeakPtr(), origin, callback)); | 64 weak_ptr_factory_.GetWeakPtr(), origin, callback)); |
| 65 } | 65 } |
| 66 | 66 |
| 67 void BudgetDatabase::SpendBudget(const GURL& origin, | 67 void BudgetDatabase::SpendBudget(const GURL& origin, |
| 68 double amount, | 68 double amount, |
| 69 const StoreBudgetCallback& callback) { | 69 const SpendBudgetCallback& callback) { |
| 70 SyncCache(origin, base::Bind(&BudgetDatabase::SpendBudgetAfterSync, | 70 SyncCache(origin, base::Bind(&BudgetDatabase::SpendBudgetAfterSync, |
| 71 weak_ptr_factory_.GetWeakPtr(), origin, amount, | 71 weak_ptr_factory_.GetWeakPtr(), origin, amount, |
| 72 callback)); | 72 callback)); |
| 73 } | 73 } |
| 74 | 74 |
| 75 void BudgetDatabase::SetClockForTesting(std::unique_ptr<base::Clock> clock) { | 75 void BudgetDatabase::SetClockForTesting(std::unique_ptr<base::Clock> clock) { |
| 76 clock_ = std::move(clock); | 76 clock_ = std::move(clock); |
| 77 } | 77 } |
| 78 | 78 |
| 79 void BudgetDatabase::OnDatabaseInit(bool success) { | 79 void BudgetDatabase::OnDatabaseInit(bool success) { |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 91 return total; | 91 return total; |
| 92 | 92 |
| 93 const BudgetInfo& info = iter->second; | 93 const BudgetInfo& info = iter->second; |
| 94 for (const BudgetChunk& chunk : info.chunks) | 94 for (const BudgetChunk& chunk : info.chunks) |
| 95 total += chunk.amount; | 95 total += chunk.amount; |
| 96 return total; | 96 return total; |
| 97 } | 97 } |
| 98 | 98 |
| 99 void BudgetDatabase::AddToCache( | 99 void BudgetDatabase::AddToCache( |
| 100 const GURL& origin, | 100 const GURL& origin, |
| 101 const AddToCacheCallback& callback, | 101 const CacheCallback& callback, |
| 102 bool success, | 102 bool success, |
| 103 std::unique_ptr<budget_service::Budget> budget_proto) { | 103 std::unique_ptr<budget_service::Budget> budget_proto) { |
| 104 // If the database read failed, there's nothing to add to the cache. | 104 // If the database read failed, or there's nothing to add to the cache. |
|
Peter Beverloo
2016/09/08 17:16:06
nit: This sentence feels incomplete now?
harkness
2016/09/13 13:41:28
Done.
| |
| 105 if (!success || !budget_proto) { | 105 if (!success || !budget_proto) { |
| 106 callback.Run(success); | 106 callback.Run(success); |
| 107 return; | 107 return; |
| 108 } | 108 } |
| 109 | 109 |
| 110 // If there were two simultaneous loads, don't overwrite the cache value, | 110 // If there were two simultaneous loads, don't overwrite the cache value, |
| 111 // which might have been updated after the previous load. | 111 // which might have been updated after the previous load. |
| 112 if (IsCached(origin)) { | 112 if (IsCached(origin)) { |
| 113 callback.Run(success); | 113 callback.Run(success); |
| 114 return; | 114 return; |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 165 } | 165 } |
| 166 | 166 |
| 167 DCHECK_EQ(0, total); | 167 DCHECK_EQ(0, total); |
| 168 | 168 |
| 169 callback.Run(blink::mojom::BudgetServiceErrorType::NONE, | 169 callback.Run(blink::mojom::BudgetServiceErrorType::NONE, |
| 170 std::move(predictions)); | 170 std::move(predictions)); |
| 171 } | 171 } |
| 172 | 172 |
| 173 void BudgetDatabase::SpendBudgetAfterSync(const GURL& origin, | 173 void BudgetDatabase::SpendBudgetAfterSync(const GURL& origin, |
| 174 double amount, | 174 double amount, |
| 175 const StoreBudgetCallback& callback, | 175 const SpendBudgetCallback& callback, |
| 176 bool success) { | 176 bool success) { |
| 177 if (!success) { | 177 if (!success) { |
| 178 callback.Run(false /* success */); | 178 callback.Run(blink::mojom::BudgetServiceErrorType::DATABASE_ERROR, |
| 179 false /* success */); | |
| 179 return; | 180 return; |
| 180 } | 181 } |
| 181 | 182 |
| 182 // Get the current SES score, to generate UMA. | 183 // Get the current SES score, to generate UMA. |
| 183 SiteEngagementService* service = SiteEngagementService::Get(profile_); | 184 SiteEngagementService* service = SiteEngagementService::Get(profile_); |
| 184 double score = service->GetScore(origin); | 185 double score = service->GetScore(origin); |
| 185 | 186 |
| 186 // Walk the list of budget chunks to see if the origin has enough budget. | 187 // Walk the list of budget chunks to see if the origin has enough budget. |
| 187 double total = 0; | 188 double total = 0; |
| 188 BudgetInfo& info = budget_map_[origin.spec()]; | 189 BudgetInfo& info = budget_map_[origin.spec()]; |
| 189 for (const BudgetChunk& chunk : info.chunks) | 190 for (const BudgetChunk& chunk : info.chunks) |
| 190 total += chunk.amount; | 191 total += chunk.amount; |
| 191 | 192 |
| 192 if (total < amount) { | 193 if (total < amount) { |
| 193 UMA_HISTOGRAM_COUNTS_100("PushMessaging.SESForNoBudgetOrigin", score); | 194 UMA_HISTOGRAM_COUNTS_100("PushMessaging.SESForNoBudgetOrigin", score); |
| 194 callback.Run(false /* success */); | 195 callback.Run(blink::mojom::BudgetServiceErrorType::NONE, |
| 196 false /* success */); | |
| 195 return; | 197 return; |
| 196 } else if (total < amount * 2) { | 198 } else if (total < amount * 2) { |
| 197 UMA_HISTOGRAM_COUNTS_100("PushMessaging.SESForLowBudgetOrigin", score); | 199 UMA_HISTOGRAM_COUNTS_100("PushMessaging.SESForLowBudgetOrigin", score); |
| 198 } | 200 } |
| 199 | 201 |
| 200 // Walk the chunks and remove enough budget to cover the needed amount. | 202 // Walk the chunks and remove enough budget to cover the needed amount. |
| 201 double bill = amount; | 203 double bill = amount; |
| 202 for (auto iter = info.chunks.begin(); iter != info.chunks.end();) { | 204 for (auto iter = info.chunks.begin(); iter != info.chunks.end();) { |
| 203 if (iter->amount > bill) { | 205 if (iter->amount > bill) { |
| 204 iter->amount -= bill; | 206 iter->amount -= bill; |
| 205 bill = 0; | 207 bill = 0; |
| 206 break; | 208 break; |
| 207 } | 209 } |
| 208 bill -= iter->amount; | 210 bill -= iter->amount; |
| 209 iter = info.chunks.erase(iter); | 211 iter = info.chunks.erase(iter); |
| 210 } | 212 } |
| 211 | 213 |
| 212 // There should have been enough budget to cover the entire bill. | 214 // There should have been enough budget to cover the entire bill. |
| 213 DCHECK_EQ(0, bill); | 215 DCHECK_EQ(0, bill); |
| 214 | 216 |
| 215 // Now that the cache is updated, write the data to the database. | 217 // Now that the cache is updated, write the data to the database. |
| 216 // TODO(harkness): Consider adding a second parameter to the callback so the | 218 WriteCachedValuesToDatabase( |
| 217 // caller can distinguish between not enough budget and a failed database | 219 origin, base::Bind(&BudgetDatabase::SpendBudgetAfterWrite, |
| 218 // write. | 220 weak_ptr_factory_.GetWeakPtr(), callback)); |
| 221 } | |
| 222 | |
| 223 // This converts the bool value which is returned from the database to a Mojo | |
| 224 // error type. | |
| 225 void BudgetDatabase::SpendBudgetAfterWrite(const SpendBudgetCallback& callback, | |
| 226 bool write_successful) { | |
| 219 // TODO(harkness): If the database write fails, the cache will be out of sync | 227 // TODO(harkness): If the database write fails, the cache will be out of sync |
| 220 // with the database. Consider ways to mitigate this. | 228 // with the database. Consider ways to mitigate this. |
| 221 WriteCachedValuesToDatabase(origin, callback); | 229 if (!write_successful) { |
| 230 callback.Run(blink::mojom::BudgetServiceErrorType::DATABASE_ERROR, | |
| 231 false /* success */); | |
| 232 return; | |
| 233 } | |
| 234 callback.Run(blink::mojom::BudgetServiceErrorType::NONE, true /* success */); | |
| 222 } | 235 } |
| 223 | 236 |
| 224 void BudgetDatabase::WriteCachedValuesToDatabase( | 237 void BudgetDatabase::WriteCachedValuesToDatabase( |
| 225 const GURL& origin, | 238 const GURL& origin, |
| 226 const StoreBudgetCallback& callback) { | 239 const StoreBudgetCallback& callback) { |
| 227 // Create the data structures that are passed to the ProtoDatabase. | 240 // Create the data structures that are passed to the ProtoDatabase. |
| 228 std::unique_ptr< | 241 std::unique_ptr< |
| 229 leveldb_proto::ProtoDatabase<budget_service::Budget>::KeyEntryVector> | 242 leveldb_proto::ProtoDatabase<budget_service::Budget>::KeyEntryVector> |
| 230 entries(new leveldb_proto::ProtoDatabase< | 243 entries(new leveldb_proto::ProtoDatabase< |
| 231 budget_service::Budget>::KeyEntryVector()); | 244 budget_service::Budget>::KeyEntryVector()); |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 249 } else { | 262 } else { |
| 250 // If the origin doesn't exist in the cache, this is a remove operation. | 263 // If the origin doesn't exist in the cache, this is a remove operation. |
| 251 keys_to_remove->push_back(origin.spec()); | 264 keys_to_remove->push_back(origin.spec()); |
| 252 } | 265 } |
| 253 | 266 |
| 254 // Send the updates to the database. | 267 // Send the updates to the database. |
| 255 db_->UpdateEntries(std::move(entries), std::move(keys_to_remove), callback); | 268 db_->UpdateEntries(std::move(entries), std::move(keys_to_remove), callback); |
| 256 } | 269 } |
| 257 | 270 |
| 258 void BudgetDatabase::SyncCache(const GURL& origin, | 271 void BudgetDatabase::SyncCache(const GURL& origin, |
| 259 const SyncCacheCallback& callback) { | 272 const CacheCallback& callback) { |
| 260 DCHECK_EQ(origin, origin.GetOrigin()); | 273 DCHECK_EQ(origin, origin.GetOrigin()); |
| 261 | 274 |
| 262 // If the origin isn't already cached, add it to the cache. | 275 // If the origin isn't already cached, add it to the cache. |
| 263 if (!IsCached(origin)) { | 276 if (!IsCached(origin)) { |
| 264 AddToCacheCallback add_callback = | 277 CacheCallback add_callback = |
| 265 base::Bind(&BudgetDatabase::SyncLoadedCache, | 278 base::Bind(&BudgetDatabase::SyncLoadedCache, |
| 266 weak_ptr_factory_.GetWeakPtr(), origin, callback); | 279 weak_ptr_factory_.GetWeakPtr(), origin, callback); |
| 267 db_->GetEntry(origin.spec(), base::Bind(&BudgetDatabase::AddToCache, | 280 db_->GetEntry(origin.spec(), base::Bind(&BudgetDatabase::AddToCache, |
| 268 weak_ptr_factory_.GetWeakPtr(), | 281 weak_ptr_factory_.GetWeakPtr(), |
| 269 origin, add_callback)); | 282 origin, add_callback)); |
| 270 return; | 283 return; |
| 271 } | 284 } |
| 272 SyncLoadedCache(origin, callback, true /* success */); | 285 SyncLoadedCache(origin, callback, true /* success */); |
| 273 } | 286 } |
| 274 | 287 |
| 275 void BudgetDatabase::SyncLoadedCache(const GURL& origin, | 288 void BudgetDatabase::SyncLoadedCache(const GURL& origin, |
| 276 const SyncCacheCallback& callback, | 289 const CacheCallback& callback, |
| 277 bool success) { | 290 bool success) { |
| 278 if (!success) { | 291 if (!success) { |
| 279 callback.Run(false /* success */); | 292 callback.Run(false /* success */); |
| 280 return; | 293 return; |
| 281 } | 294 } |
| 282 | 295 |
| 283 // Now, cleanup any expired budget chunks for the origin. | 296 // Now, cleanup any expired budget chunks for the origin. |
| 284 bool needs_write = CleanupExpiredBudget(origin); | 297 bool needs_write = CleanupExpiredBudget(origin); |
| 285 | 298 |
| 286 // Get the SES score and add engagement budget for the site. | 299 // Get the SES score and add engagement budget for the site. |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 350 clock_->Now() - base::TimeDelta::FromHours(kBudgetDurationInHours)) { | 363 clock_->Now() - base::TimeDelta::FromHours(kBudgetDurationInHours)) { |
| 351 budget_map_.erase(origin.spec()); | 364 budget_map_.erase(origin.spec()); |
| 352 return true; | 365 return true; |
| 353 } | 366 } |
| 354 | 367 |
| 355 // Although some things may have expired, there are some chunks still valid. | 368 // Although some things may have expired, there are some chunks still valid. |
| 356 // Don't write to the DB now, write either when all chunks expire or when the | 369 // Don't write to the DB now, write either when all chunks expire or when the |
| 357 // origin spends some budget. | 370 // origin spends some budget. |
| 358 return false; | 371 return false; |
| 359 } | 372 } |
| OLD | NEW |