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