| 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/containers/adapters.h" | 7 #include "base/containers/adapters.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" |
| 12 #include "chrome/browser/engagement/site_engagement_service.h" |
| 13 #include "chrome/browser/profiles/profile.h" |
| 11 #include "components/leveldb_proto/proto_database_impl.h" | 14 #include "components/leveldb_proto/proto_database_impl.h" |
| 12 #include "content/public/browser/browser_thread.h" | 15 #include "content/public/browser/browser_thread.h" |
| 13 #include "url/gurl.h" | 16 #include "url/gurl.h" |
| 14 | 17 |
| 15 using content::BrowserThread; | 18 using content::BrowserThread; |
| 16 | 19 |
| 17 namespace { | 20 namespace { |
| 18 | 21 |
| 19 // 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. |
| 20 // They will be LevelDB.*.BudgetManager. Changes here should be synchronized | 23 // They will be LevelDB.*.BudgetManager. Changes here should be synchronized |
| 21 // with histograms.xml. | 24 // with histograms.xml. |
| 22 const char kDatabaseUMAName[] = "BudgetManager"; | 25 const char kDatabaseUMAName[] = "BudgetManager"; |
| 23 | 26 |
| 24 // 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. |
| 25 // This is 3 days = 72 hours. | 28 // This is 3 days = 72 hours. |
| 26 constexpr double kBudgetDurationInHours = 72; | 29 constexpr double kBudgetDurationInHours = 72; |
| 27 | 30 |
| 28 } // namespace | 31 } // namespace |
| 29 | 32 |
| 30 BudgetDatabase::BudgetInfo::BudgetInfo() {} | 33 BudgetDatabase::BudgetInfo::BudgetInfo() {} |
| 31 | 34 |
| 32 BudgetDatabase::BudgetInfo::BudgetInfo(const BudgetInfo&& other) | 35 BudgetDatabase::BudgetInfo::BudgetInfo(const BudgetInfo&& other) |
| 33 : last_engagement_award(other.last_engagement_award) { | 36 : last_engagement_award(other.last_engagement_award) { |
| 34 chunks = std::move(other.chunks); | 37 chunks = std::move(other.chunks); |
| 35 } | 38 } |
| 36 | 39 |
| 37 BudgetDatabase::BudgetInfo::~BudgetInfo() {} | 40 BudgetDatabase::BudgetInfo::~BudgetInfo() {} |
| 38 | 41 |
| 39 BudgetDatabase::BudgetDatabase( | 42 BudgetDatabase::BudgetDatabase( |
| 43 Profile* profile, |
| 40 const base::FilePath& database_dir, | 44 const base::FilePath& database_dir, |
| 41 const scoped_refptr<base::SequencedTaskRunner>& task_runner) | 45 const scoped_refptr<base::SequencedTaskRunner>& task_runner) |
| 42 : db_(new leveldb_proto::ProtoDatabaseImpl<budget_service::Budget>( | 46 : profile_(profile), |
| 47 db_(new leveldb_proto::ProtoDatabaseImpl<budget_service::Budget>( |
| 43 task_runner)), | 48 task_runner)), |
| 44 clock_(base::WrapUnique(new base::DefaultClock)), | 49 clock_(base::WrapUnique(new base::DefaultClock)), |
| 45 weak_ptr_factory_(this) { | 50 weak_ptr_factory_(this) { |
| 46 db_->Init(kDatabaseUMAName, database_dir, | 51 db_->Init(kDatabaseUMAName, database_dir, |
| 47 base::Bind(&BudgetDatabase::OnDatabaseInit, | 52 base::Bind(&BudgetDatabase::OnDatabaseInit, |
| 48 weak_ptr_factory_.GetWeakPtr())); | 53 weak_ptr_factory_.GetWeakPtr())); |
| 49 } | 54 } |
| 50 | 55 |
| 51 BudgetDatabase::~BudgetDatabase() {} | 56 BudgetDatabase::~BudgetDatabase() {} |
| 52 | 57 |
| 53 void BudgetDatabase::GetBudgetDetails( | 58 void BudgetDatabase::GetBudgetDetails( |
| 54 const GURL& origin, | 59 const GURL& origin, |
| 55 const GetBudgetDetailsCallback& callback) { | 60 const GetBudgetDetailsCallback& callback) { |
| 56 DCHECK_EQ(origin.GetOrigin(), origin); | 61 DCHECK_EQ(origin.GetOrigin(), origin); |
| 57 | 62 |
| 58 // If this origin is already in the cache, immediately return the data. | 63 SyncCache(origin, |
| 59 if (IsCached(origin)) { | 64 base::Bind(&BudgetDatabase::GetBudgetAfterSync, |
| 60 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 65 weak_ptr_factory_.GetWeakPtr(), origin, callback)); |
| 61 base::Bind(&BudgetDatabase::DidGetBudget, | |
| 62 weak_ptr_factory_.GetWeakPtr(), origin, | |
| 63 callback, true /* success */)); | |
| 64 return; | |
| 65 } | |
| 66 | |
| 67 // Otherwise, query for the data, add it to the cache, then return the result. | |
| 68 AddToCacheCallback cache_callback = | |
| 69 base::Bind(&BudgetDatabase::DidGetBudget, weak_ptr_factory_.GetWeakPtr(), | |
| 70 origin, callback); | |
| 71 db_->GetEntry(origin.spec(), base::Bind(&BudgetDatabase::AddToCache, | |
| 72 weak_ptr_factory_.GetWeakPtr(), | |
| 73 origin, cache_callback)); | |
| 74 } | |
| 75 | |
| 76 void BudgetDatabase::AddBudget(const GURL& origin, | |
| 77 double amount, | |
| 78 const StoreBudgetCallback& callback) { | |
| 79 DCHECK_EQ(origin.GetOrigin(), origin); | |
| 80 | |
| 81 // Add a new chunk of budget for the origin at the default expiration time. | |
| 82 base::Time expiration = | |
| 83 clock_->Now() + base::TimeDelta::FromHours(kBudgetDurationInHours); | |
| 84 budget_map_[origin.spec()].chunks.emplace_back(amount, expiration); | |
| 85 | |
| 86 // Now that the cache is updated, write the data to the database. | |
| 87 WriteCachedValuesToDatabase(origin, callback); | |
| 88 } | |
| 89 | |
| 90 void BudgetDatabase::AddEngagementBudget(const GURL& origin, | |
| 91 double score, | |
| 92 const StoreBudgetCallback& callback) { | |
| 93 DCHECK_EQ(origin.GetOrigin(), origin); | |
| 94 | |
| 95 // By default we award the "full" award. Then that ratio is decreased if | |
| 96 // there have been other awards recently. | |
| 97 double ratio = 1.0; | |
| 98 | |
| 99 // Calculate how much budget should be awarded. If the origin is not cached, | |
| 100 // then we award a full amount. | |
| 101 if (IsCached(origin)) { | |
| 102 base::TimeDelta elapsed = | |
| 103 clock_->Now() - budget_map_[origin.spec()].last_engagement_award; | |
| 104 int elapsed_hours = elapsed.InHours(); | |
| 105 if (elapsed_hours == 0) { | |
| 106 // Don't give engagement awards for periods less than an hour. | |
| 107 callback.Run(true); | |
| 108 return; | |
| 109 } | |
| 110 if (elapsed_hours < kBudgetDurationInHours) | |
| 111 ratio = elapsed_hours / kBudgetDurationInHours; | |
| 112 } | |
| 113 | |
| 114 // Update the last_engagement_award to the current time. If the origin wasn't | |
| 115 // already in the map, this adds a new entry for it. | |
| 116 budget_map_[origin.spec()].last_engagement_award = clock_->Now(); | |
| 117 | |
| 118 // Pass to the base AddBudget to update the cache and write to the database. | |
| 119 AddBudget(origin, score * ratio, callback); | |
| 120 } | 66 } |
| 121 | 67 |
| 122 void BudgetDatabase::SpendBudget(const GURL& origin, | 68 void BudgetDatabase::SpendBudget(const GURL& origin, |
| 123 double amount, | 69 double amount, |
| 124 const StoreBudgetCallback& callback) { | 70 const StoreBudgetCallback& callback) { |
| 125 DCHECK_EQ(origin.GetOrigin(), origin); | 71 SyncCache(origin, base::Bind(&BudgetDatabase::SpendBudgetAfterSync, |
| 72 weak_ptr_factory_.GetWeakPtr(), origin, amount, |
| 73 callback)); |
| 74 } |
| 126 | 75 |
| 127 // First, cleanup any expired budget chunks for the origin. | 76 void BudgetDatabase::SetClockForTesting(std::unique_ptr<base::Clock> clock) { |
| 128 CleanupExpiredBudget(origin); | 77 clock_ = std::move(clock); |
| 129 | |
| 130 if (!IsCached(origin)) { | |
| 131 callback.Run(false); | |
| 132 return; | |
| 133 } | |
| 134 | |
| 135 // Walk the list of budget chunks to see if the origin has enough budget. | |
| 136 double total = 0; | |
| 137 BudgetInfo& info = budget_map_[origin.spec()]; | |
| 138 for (const BudgetChunk& chunk : info.chunks) | |
| 139 total += chunk.amount; | |
| 140 | |
| 141 if (total < amount) { | |
| 142 callback.Run(false); | |
| 143 return; | |
| 144 } | |
| 145 | |
| 146 // Walk the chunks and remove enough budget to cover the needed amount. | |
| 147 double bill = amount; | |
| 148 for (auto iter = info.chunks.begin(); iter != info.chunks.end();) { | |
| 149 if (iter->amount > bill) { | |
| 150 iter->amount -= bill; | |
| 151 bill = 0; | |
| 152 ++iter; | |
| 153 break; | |
| 154 } | |
| 155 bill -= iter->amount; | |
| 156 iter = info.chunks.erase(iter); | |
| 157 } | |
| 158 | |
| 159 // There should have been enough budget to cover the entire bill. | |
| 160 DCHECK_EQ(0, bill); | |
| 161 | |
| 162 // Now that the cache is updated, write the data to the database. | |
| 163 // TODO(harkness): Consider adding a second parameter to the callback so the | |
| 164 // caller can distinguish between not enough budget and a failed database | |
| 165 // write. | |
| 166 // TODO(harkness): If the database write fails, the cache will be out of sync | |
| 167 // with the database. Consider ways to mitigate this. | |
| 168 WriteCachedValuesToDatabase(origin, callback); | |
| 169 } | 78 } |
| 170 | 79 |
| 171 void BudgetDatabase::OnDatabaseInit(bool success) { | 80 void BudgetDatabase::OnDatabaseInit(bool success) { |
| 172 // TODO(harkness): Consider caching the budget database now? | 81 // TODO(harkness): Consider caching the budget database now? |
| 173 } | 82 } |
| 174 | 83 |
| 175 bool BudgetDatabase::IsCached(const GURL& origin) const { | 84 bool BudgetDatabase::IsCached(const GURL& origin) const { |
| 176 return budget_map_.find(origin.spec()) != budget_map_.end(); | 85 return budget_map_.find(origin.spec()) != budget_map_.end(); |
| 177 } | 86 } |
| 178 | 87 |
| 179 void BudgetDatabase::AddToCache( | 88 void BudgetDatabase::AddToCache( |
| 180 const GURL& origin, | 89 const GURL& origin, |
| 181 const AddToCacheCallback& callback, | 90 const AddToCacheCallback& callback, |
| 182 bool success, | 91 bool success, |
| 183 std::unique_ptr<budget_service::Budget> budget_proto) { | 92 std::unique_ptr<budget_service::Budget> budget_proto) { |
| 184 // If the database read failed, there's nothing to add to the cache. | 93 // If the database read failed, there's nothing to add to the cache. |
| 185 if (!success || !budget_proto) { | 94 if (!success || !budget_proto) { |
| 186 callback.Run(success); | 95 callback.Run(success); |
| 187 return; | 96 return; |
| 188 } | 97 } |
| 189 | 98 |
| 99 // If there were two simultaneous loads, don't overwrite the cache value, |
| 100 // which might have been updated after the previous load. |
| 101 if (IsCached(origin)) { |
| 102 callback.Run(success); |
| 103 return; |
| 104 } |
| 105 |
| 190 // Add the data to the cache, converting from the proto format to an STL | 106 // Add the data to the cache, converting from the proto format to an STL |
| 191 // format which is better for removing things from the list. | 107 // format which is better for removing things from the list. |
| 192 BudgetInfo& info = budget_map_[origin.spec()]; | 108 BudgetInfo& info = budget_map_[origin.spec()]; |
| 193 for (const auto& chunk : budget_proto->budget()) { | 109 for (const auto& chunk : budget_proto->budget()) { |
| 194 info.chunks.emplace_back(chunk.amount(), | 110 info.chunks.emplace_back(chunk.amount(), |
| 195 base::Time::FromInternalValue(chunk.expiration())); | 111 base::Time::FromInternalValue(chunk.expiration())); |
| 196 } | 112 } |
| 197 | 113 |
| 198 info.last_engagement_award = | 114 info.last_engagement_award = |
| 199 base::Time::FromInternalValue(budget_proto->engagement_last_updated()); | 115 base::Time::FromInternalValue(budget_proto->engagement_last_updated()); |
| 200 | 116 |
| 201 callback.Run(success); | 117 callback.Run(success); |
| 202 } | 118 } |
| 203 | 119 |
| 204 void BudgetDatabase::DidGetBudget(const GURL& origin, | 120 void BudgetDatabase::GetBudgetAfterSync( |
| 205 const GetBudgetDetailsCallback& callback, | 121 const GURL& origin, |
| 206 bool success) { | 122 const GetBudgetDetailsCallback& callback, |
| 123 bool success) { |
| 207 // If the database wasn't able to read the information, return the | 124 // If the database wasn't able to read the information, return the |
| 208 // failure and an empty BudgetPrediction. | 125 // failure and an empty BudgetPrediction. |
| 209 if (!success) { | 126 if (!success) { |
| 210 callback.Run(success, BudgetPrediction()); | 127 callback.Run(success, BudgetPrediction()); |
| 211 return; | 128 return; |
| 212 } | 129 } |
| 213 | 130 |
| 214 // First, cleanup any expired budget chunks for the origin. | |
| 215 CleanupExpiredBudget(origin); | |
| 216 | |
| 217 // Now, build up the BudgetExpection. This is different from the format | 131 // Now, build up the BudgetExpection. This is different from the format |
| 218 // in which the cache stores the data. The cache stores chunks of budget and | 132 // in which the cache stores the data. The cache stores chunks of budget and |
| 219 // when that budget expires. The BudgetPrediction describes a set of times | 133 // when that budget expires. The BudgetPrediction describes a set of times |
| 220 // and the budget at those times. | 134 // and the budget at those times. |
| 221 BudgetPrediction prediction; | 135 BudgetPrediction prediction; |
| 222 double total = 0; | 136 double total = 0; |
| 223 | 137 |
| 224 if (IsCached(origin)) { | 138 // Starting with the chunks that expire the farthest in the future, build up |
| 225 // Starting with the chunks that expire the farthest in the future, build up | 139 // the budget predictions for those future times. |
| 226 // the budget predictions for those future times. | 140 const BudgetChunks& chunks = budget_map_[origin.spec()].chunks; |
| 227 const BudgetChunks& chunks = budget_map_[origin.spec()].chunks; | 141 for (const auto& chunk : base::Reversed(chunks)) { |
| 228 for (const auto& chunk : base::Reversed(chunks)) { | 142 prediction.emplace_front(total, chunk.expiration); |
| 229 prediction.emplace_front(total, chunk.expiration); | 143 total += chunk.amount; |
| 230 total += chunk.amount; | |
| 231 } | |
| 232 } | 144 } |
| 233 | 145 |
| 234 // Always add one entry at the front of the list for the total budget now. | 146 // Always add one entry at the front of the list for the total budget now. |
| 235 prediction.emplace_front(total, clock_->Now()); | 147 prediction.emplace_front(total, clock_->Now()); |
| 236 | 148 |
| 237 callback.Run(true /* success */, prediction); | 149 callback.Run(true /* success */, prediction); |
| 238 } | 150 } |
| 239 | 151 |
| 240 void BudgetDatabase::SetClockForTesting(std::unique_ptr<base::Clock> clock) { | 152 void BudgetDatabase::SpendBudgetAfterSync(const GURL& origin, |
| 241 clock_ = std::move(clock); | 153 double amount, |
| 154 const StoreBudgetCallback& callback, |
| 155 bool success) { |
| 156 if (!success) { |
| 157 callback.Run(false /* success */); |
| 158 return; |
| 159 } |
| 160 |
| 161 // Walk the list of budget chunks to see if the origin has enough budget. |
| 162 double total = 0; |
| 163 BudgetInfo& info = budget_map_[origin.spec()]; |
| 164 for (const BudgetChunk& chunk : info.chunks) |
| 165 total += chunk.amount; |
| 166 |
| 167 if (total < amount) { |
| 168 callback.Run(false /* success */); |
| 169 return; |
| 170 } |
| 171 |
| 172 // Walk the chunks and remove enough budget to cover the needed amount. |
| 173 double bill = amount; |
| 174 for (auto iter = info.chunks.begin(); iter != info.chunks.end();) { |
| 175 if (iter->amount > bill) { |
| 176 iter->amount -= bill; |
| 177 bill = 0; |
| 178 break; |
| 179 } |
| 180 bill -= iter->amount; |
| 181 iter = info.chunks.erase(iter); |
| 182 } |
| 183 |
| 184 // There should have been enough budget to cover the entire bill. |
| 185 DCHECK_EQ(0, bill); |
| 186 |
| 187 // Now that the cache is updated, write the data to the database. |
| 188 // TODO(harkness): Consider adding a second parameter to the callback so the |
| 189 // caller can distinguish between not enough budget and a failed database |
| 190 // write. |
| 191 // TODO(harkness): If the database write fails, the cache will be out of sync |
| 192 // with the database. Consider ways to mitigate this. |
| 193 WriteCachedValuesToDatabase(origin, callback); |
| 242 } | 194 } |
| 243 | 195 |
| 244 void BudgetDatabase::WriteCachedValuesToDatabase( | 196 void BudgetDatabase::WriteCachedValuesToDatabase( |
| 245 const GURL& origin, | 197 const GURL& origin, |
| 246 const StoreBudgetCallback& callback) { | 198 const StoreBudgetCallback& callback) { |
| 247 // First, cleanup any expired budget chunks for the origin. | |
| 248 CleanupExpiredBudget(origin); | |
| 249 | |
| 250 // Create the data structures that are passed to the ProtoDatabase. | 199 // Create the data structures that are passed to the ProtoDatabase. |
| 251 std::unique_ptr< | 200 std::unique_ptr< |
| 252 leveldb_proto::ProtoDatabase<budget_service::Budget>::KeyEntryVector> | 201 leveldb_proto::ProtoDatabase<budget_service::Budget>::KeyEntryVector> |
| 253 entries(new leveldb_proto::ProtoDatabase< | 202 entries(new leveldb_proto::ProtoDatabase< |
| 254 budget_service::Budget>::KeyEntryVector()); | 203 budget_service::Budget>::KeyEntryVector()); |
| 255 std::unique_ptr<std::vector<std::string>> keys_to_remove( | 204 std::unique_ptr<std::vector<std::string>> keys_to_remove( |
| 256 new std::vector<std::string>()); | 205 new std::vector<std::string>()); |
| 257 | 206 |
| 258 // Each operation can either update the existing budget or remove the origin's | 207 // Each operation can either update the existing budget or remove the origin's |
| 259 // budget information. | 208 // budget information. |
| (...skipping 11 matching lines...) Expand all Loading... |
| 271 entries->push_back(std::make_pair(origin.spec(), budget)); | 220 entries->push_back(std::make_pair(origin.spec(), budget)); |
| 272 } else { | 221 } else { |
| 273 // If the origin doesn't exist in the cache, this is a remove operation. | 222 // If the origin doesn't exist in the cache, this is a remove operation. |
| 274 keys_to_remove->push_back(origin.spec()); | 223 keys_to_remove->push_back(origin.spec()); |
| 275 } | 224 } |
| 276 | 225 |
| 277 // Send the updates to the database. | 226 // Send the updates to the database. |
| 278 db_->UpdateEntries(std::move(entries), std::move(keys_to_remove), callback); | 227 db_->UpdateEntries(std::move(entries), std::move(keys_to_remove), callback); |
| 279 } | 228 } |
| 280 | 229 |
| 281 void BudgetDatabase::CleanupExpiredBudget(const GURL& origin) { | 230 void BudgetDatabase::SyncCache(const GURL& origin, |
| 231 const SyncCacheCallback& callback) { |
| 232 DCHECK_EQ(origin, origin.GetOrigin()); |
| 233 |
| 234 // If the origin isn't already cached, add it to the cache. |
| 235 if (!IsCached(origin)) { |
| 236 AddToCacheCallback add_callback = |
| 237 base::Bind(&BudgetDatabase::SyncLoadedCache, |
| 238 weak_ptr_factory_.GetWeakPtr(), origin, callback); |
| 239 db_->GetEntry(origin.spec(), base::Bind(&BudgetDatabase::AddToCache, |
| 240 weak_ptr_factory_.GetWeakPtr(), |
| 241 origin, add_callback)); |
| 242 return; |
| 243 } |
| 244 SyncLoadedCache(origin, callback, true /* success */); |
| 245 } |
| 246 |
| 247 void BudgetDatabase::SyncLoadedCache(const GURL& origin, |
| 248 const SyncCacheCallback& callback, |
| 249 bool success) { |
| 250 if (!success) { |
| 251 callback.Run(false /* success */); |
| 252 return; |
| 253 } |
| 254 |
| 255 // Get the SES score and add engagement budget for the site. |
| 256 AddEngagementBudget(origin); |
| 257 |
| 258 // Now, cleanup any expired budget chunks for the origin. |
| 259 bool needs_write = CleanupExpiredBudget(origin); |
| 260 |
| 261 if (needs_write) |
| 262 WriteCachedValuesToDatabase(origin, callback); |
| 263 else |
| 264 callback.Run(success); |
| 265 } |
| 266 |
| 267 void BudgetDatabase::AddEngagementBudget(const GURL& origin) { |
| 268 // Get the current SES score, which we'll use to set a new budget. |
| 269 SiteEngagementService* service = SiteEngagementService::Get(profile_); |
| 270 double score = service->GetScore(origin); |
| 271 |
| 272 // By default we award the "full" award. Then that ratio is decreased if |
| 273 // there have been other awards recently. |
| 274 double ratio = 1.0; |
| 275 |
| 276 // Calculate how much budget should be awarded. If there is no entry in the |
| 277 // cache then we award a full amount. |
| 278 if (IsCached(origin)) { |
| 279 base::TimeDelta elapsed = |
| 280 clock_->Now() - budget_map_[origin.spec()].last_engagement_award; |
| 281 int elapsed_hours = elapsed.InHours(); |
| 282 // Don't give engagement awards for periods less than an hour. |
| 283 if (elapsed_hours < 1) |
| 284 return; |
| 285 if (elapsed_hours < kBudgetDurationInHours) |
| 286 ratio = elapsed_hours / kBudgetDurationInHours; |
| 287 } |
| 288 |
| 289 // 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. |
| 291 budget_map_[origin.spec()].last_engagement_award = clock_->Now(); |
| 292 |
| 293 // Add a new chunk of budget for the origin at the default expiration time. |
| 294 base::Time expiration = |
| 295 clock_->Now() + base::TimeDelta::FromHours(kBudgetDurationInHours); |
| 296 budget_map_[origin.spec()].chunks.emplace_back(ratio * score, expiration); |
| 297 } |
| 298 |
| 299 // Cleans up budget in the cache. Relies on the caller eventually writing the |
| 300 // cache back to the database. |
| 301 bool BudgetDatabase::CleanupExpiredBudget(const GURL& origin) { |
| 282 if (!IsCached(origin)) | 302 if (!IsCached(origin)) |
| 283 return; | 303 return false; |
| 284 | 304 |
| 285 base::Time now = clock_->Now(); | 305 base::Time now = clock_->Now(); |
| 286 | |
| 287 BudgetChunks& chunks = budget_map_[origin.spec()].chunks; | 306 BudgetChunks& chunks = budget_map_[origin.spec()].chunks; |
| 288 auto cleanup_iter = chunks.begin(); | 307 auto cleanup_iter = chunks.begin(); |
| 289 | 308 |
| 290 // This relies on the list of chunks being in timestamp order. | 309 // This relies on the list of chunks being in timestamp order. |
| 291 while (cleanup_iter != chunks.end() && cleanup_iter->expiration <= now) | 310 while (cleanup_iter != chunks.end() && cleanup_iter->expiration <= now) |
| 292 cleanup_iter = chunks.erase(cleanup_iter); | 311 cleanup_iter = chunks.erase(cleanup_iter); |
| 293 | 312 |
| 294 // If the entire budget is empty now AND there have been no engagements | 313 // If the entire budget is empty now AND there have been no engagements |
| 295 // in the last kBudgetDurationInHours hours, remove this from the cache. | 314 // in the last kBudgetDurationInHours hours, remove this from the cache. |
| 296 if (chunks.empty() && | 315 if (chunks.empty() && |
| 297 budget_map_[origin.spec()].last_engagement_award < | 316 budget_map_[origin.spec()].last_engagement_award < |
| 298 clock_->Now() - base::TimeDelta::FromHours(kBudgetDurationInHours)) | 317 clock_->Now() - base::TimeDelta::FromHours(kBudgetDurationInHours)) { |
| 299 budget_map_.erase(origin.spec()); | 318 budget_map_.erase(origin.spec()); |
| 319 return true; |
| 320 } |
| 321 |
| 322 // 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 |
| 324 // origin spends some budget. |
| 325 return false; |
| 300 } | 326 } |
| OLD | NEW |