| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "webkit/quota/quota_temporary_storage_evictor.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/metrics/histogram.h" | |
| 11 #include "googleurl/src/gurl.h" | |
| 12 #include "webkit/quota/quota_manager.h" | |
| 13 | |
| 14 #define UMA_HISTOGRAM_MBYTES(name, sample) \ | |
| 15 UMA_HISTOGRAM_CUSTOM_COUNTS( \ | |
| 16 (name), static_cast<int>((sample) / kMBytes), \ | |
| 17 1, 10 * 1024 * 1024 /* 10TB */, 100) | |
| 18 | |
| 19 #define UMA_HISTOGRAM_MINUTES(name, sample) \ | |
| 20 UMA_HISTOGRAM_CUSTOM_TIMES( \ | |
| 21 (name), (sample), \ | |
| 22 base::TimeDelta::FromMinutes(1), \ | |
| 23 base::TimeDelta::FromDays(1), 50) | |
| 24 | |
| 25 namespace { | |
| 26 const int64 kMBytes = 1024 * 1024; | |
| 27 const double kUsageRatioToStartEviction = 0.7; | |
| 28 const int kThresholdOfErrorsToStopEviction = 5; | |
| 29 const int kHistogramReportIntervalMinutes = 60; | |
| 30 } | |
| 31 | |
| 32 namespace quota { | |
| 33 | |
| 34 const int QuotaTemporaryStorageEvictor:: | |
| 35 kMinAvailableDiskSpaceToStartEvictionNotSpecified = -1; | |
| 36 | |
| 37 QuotaTemporaryStorageEvictor::EvictionRoundStatistics::EvictionRoundStatistics() | |
| 38 : in_round(false), | |
| 39 is_initialized(false), | |
| 40 usage_overage_at_round(-1), | |
| 41 diskspace_shortage_at_round(-1), | |
| 42 usage_on_beginning_of_round(-1), | |
| 43 usage_on_end_of_round(-1), | |
| 44 num_evicted_origins_in_round(0) { | |
| 45 } | |
| 46 | |
| 47 QuotaTemporaryStorageEvictor::QuotaTemporaryStorageEvictor( | |
| 48 QuotaEvictionHandler* quota_eviction_handler, | |
| 49 int64 interval_ms) | |
| 50 : min_available_disk_space_to_start_eviction_( | |
| 51 kMinAvailableDiskSpaceToStartEvictionNotSpecified), | |
| 52 quota_eviction_handler_(quota_eviction_handler), | |
| 53 interval_ms_(interval_ms), | |
| 54 repeated_eviction_(true), | |
| 55 weak_factory_(this) { | |
| 56 DCHECK(quota_eviction_handler); | |
| 57 } | |
| 58 | |
| 59 QuotaTemporaryStorageEvictor::~QuotaTemporaryStorageEvictor() { | |
| 60 } | |
| 61 | |
| 62 void QuotaTemporaryStorageEvictor::GetStatistics( | |
| 63 std::map<std::string, int64>* statistics) { | |
| 64 DCHECK(statistics); | |
| 65 | |
| 66 (*statistics)["errors-on-evicting-origin"] = | |
| 67 statistics_.num_errors_on_evicting_origin; | |
| 68 (*statistics)["errors-on-getting-usage-and-quota"] = | |
| 69 statistics_.num_errors_on_getting_usage_and_quota; | |
| 70 (*statistics)["evicted-origins"] = | |
| 71 statistics_.num_evicted_origins; | |
| 72 (*statistics)["eviction-rounds"] = | |
| 73 statistics_.num_eviction_rounds; | |
| 74 (*statistics)["skipped-eviction-rounds"] = | |
| 75 statistics_.num_skipped_eviction_rounds; | |
| 76 } | |
| 77 | |
| 78 void QuotaTemporaryStorageEvictor::ReportPerRoundHistogram() { | |
| 79 DCHECK(round_statistics_.in_round); | |
| 80 DCHECK(round_statistics_.is_initialized); | |
| 81 | |
| 82 base::Time now = base::Time::Now(); | |
| 83 UMA_HISTOGRAM_TIMES("Quota.TimeSpentToAEvictionRound", | |
| 84 now - round_statistics_.start_time); | |
| 85 if (!time_of_end_of_last_round_.is_null()) | |
| 86 UMA_HISTOGRAM_MINUTES("Quota.TimeDeltaOfEvictionRounds", | |
| 87 now - time_of_end_of_last_round_); | |
| 88 UMA_HISTOGRAM_MBYTES("Quota.UsageOverageOfTemporaryGlobalStorage", | |
| 89 round_statistics_.usage_overage_at_round); | |
| 90 UMA_HISTOGRAM_MBYTES("Quota.DiskspaceShortage", | |
| 91 round_statistics_.diskspace_shortage_at_round); | |
| 92 UMA_HISTOGRAM_MBYTES("Quota.EvictedBytesPerRound", | |
| 93 round_statistics_.usage_on_beginning_of_round - | |
| 94 round_statistics_.usage_on_end_of_round); | |
| 95 UMA_HISTOGRAM_COUNTS("Quota.NumberOfEvictedOriginsPerRound", | |
| 96 round_statistics_.num_evicted_origins_in_round); | |
| 97 } | |
| 98 | |
| 99 void QuotaTemporaryStorageEvictor::ReportPerHourHistogram() { | |
| 100 Statistics stats_in_hour(statistics_); | |
| 101 stats_in_hour.subtract_assign(previous_statistics_); | |
| 102 previous_statistics_ = statistics_; | |
| 103 | |
| 104 UMA_HISTOGRAM_COUNTS("Quota.ErrorsOnEvictingOriginPerHour", | |
| 105 stats_in_hour.num_errors_on_evicting_origin); | |
| 106 UMA_HISTOGRAM_COUNTS("Quota.ErrorsOnGettingUsageAndQuotaPerHour", | |
| 107 stats_in_hour.num_errors_on_getting_usage_and_quota); | |
| 108 UMA_HISTOGRAM_COUNTS("Quota.EvictedOriginsPerHour", | |
| 109 stats_in_hour.num_evicted_origins); | |
| 110 UMA_HISTOGRAM_COUNTS("Quota.EvictionRoundsPerHour", | |
| 111 stats_in_hour.num_eviction_rounds); | |
| 112 UMA_HISTOGRAM_COUNTS("Quota.SkippedEvictionRoundsPerHour", | |
| 113 stats_in_hour.num_skipped_eviction_rounds); | |
| 114 } | |
| 115 | |
| 116 void QuotaTemporaryStorageEvictor::OnEvictionRoundStarted() { | |
| 117 if (round_statistics_.in_round) | |
| 118 return; | |
| 119 round_statistics_.in_round = true; | |
| 120 round_statistics_.start_time = base::Time::Now(); | |
| 121 ++statistics_.num_eviction_rounds; | |
| 122 } | |
| 123 | |
| 124 void QuotaTemporaryStorageEvictor::OnEvictionRoundFinished() { | |
| 125 // Check if skipped round | |
| 126 if (round_statistics_.num_evicted_origins_in_round) { | |
| 127 ReportPerRoundHistogram(); | |
| 128 time_of_end_of_last_nonskipped_round_ = base::Time::Now(); | |
| 129 } else { | |
| 130 ++statistics_.num_skipped_eviction_rounds; | |
| 131 } | |
| 132 // Reset stats for next round. | |
| 133 round_statistics_ = EvictionRoundStatistics(); | |
| 134 } | |
| 135 | |
| 136 void QuotaTemporaryStorageEvictor::Start() { | |
| 137 DCHECK(CalledOnValidThread()); | |
| 138 StartEvictionTimerWithDelay(0); | |
| 139 | |
| 140 if (histogram_timer_.IsRunning()) | |
| 141 return; | |
| 142 | |
| 143 histogram_timer_.Start( | |
| 144 FROM_HERE, base::TimeDelta::FromMinutes(kHistogramReportIntervalMinutes), | |
| 145 this, &QuotaTemporaryStorageEvictor::ReportPerHourHistogram); | |
| 146 } | |
| 147 | |
| 148 void QuotaTemporaryStorageEvictor::StartEvictionTimerWithDelay(int delay_ms) { | |
| 149 if (eviction_timer_.IsRunning()) | |
| 150 return; | |
| 151 eviction_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(delay_ms), | |
| 152 this, &QuotaTemporaryStorageEvictor::ConsiderEviction); | |
| 153 } | |
| 154 | |
| 155 void QuotaTemporaryStorageEvictor::ConsiderEviction() { | |
| 156 OnEvictionRoundStarted(); | |
| 157 | |
| 158 // Get usage and disk space, then continue. | |
| 159 quota_eviction_handler_->GetUsageAndQuotaForEviction( | |
| 160 base::Bind(&QuotaTemporaryStorageEvictor::OnGotUsageAndQuotaForEviction, | |
| 161 weak_factory_.GetWeakPtr())); | |
| 162 } | |
| 163 | |
| 164 void QuotaTemporaryStorageEvictor::OnGotUsageAndQuotaForEviction( | |
| 165 QuotaStatusCode status, | |
| 166 const UsageAndQuota& qau) { | |
| 167 DCHECK(CalledOnValidThread()); | |
| 168 | |
| 169 int64 usage = qau.global_limited_usage; | |
| 170 DCHECK_GE(usage, 0); | |
| 171 | |
| 172 if (status != kQuotaStatusOk) | |
| 173 ++statistics_.num_errors_on_getting_usage_and_quota; | |
| 174 | |
| 175 int64 usage_overage = std::max( | |
| 176 static_cast<int64>(0), | |
| 177 usage - static_cast<int64>(qau.quota * kUsageRatioToStartEviction)); | |
| 178 | |
| 179 // min_available_disk_space_to_start_eviction_ might be < 0 if no value | |
| 180 // is explicitly configured yet. | |
| 181 int64 diskspace_shortage = std::max( | |
| 182 static_cast<int64>(0), | |
| 183 min_available_disk_space_to_start_eviction_ - qau.available_disk_space); | |
| 184 | |
| 185 if (!round_statistics_.is_initialized) { | |
| 186 round_statistics_.usage_overage_at_round = usage_overage; | |
| 187 round_statistics_.diskspace_shortage_at_round = diskspace_shortage; | |
| 188 round_statistics_.usage_on_beginning_of_round = usage; | |
| 189 round_statistics_.is_initialized = true; | |
| 190 } | |
| 191 round_statistics_.usage_on_end_of_round = usage; | |
| 192 | |
| 193 int64 amount_to_evict = std::max(usage_overage, diskspace_shortage); | |
| 194 if (status == kQuotaStatusOk && amount_to_evict > 0) { | |
| 195 // Space is getting tight. Get the least recently used origin and continue. | |
| 196 // TODO(michaeln): if the reason for eviction is low physical disk space, | |
| 197 // make 'unlimited' origins subject to eviction too. | |
| 198 quota_eviction_handler_->GetLRUOrigin( | |
| 199 kStorageTypeTemporary, | |
| 200 base::Bind(&QuotaTemporaryStorageEvictor::OnGotLRUOrigin, | |
| 201 weak_factory_.GetWeakPtr())); | |
| 202 } else { | |
| 203 if (repeated_eviction_) { | |
| 204 // No action required, sleep for a while and check again later. | |
| 205 if (statistics_.num_errors_on_getting_usage_and_quota < | |
| 206 kThresholdOfErrorsToStopEviction) { | |
| 207 StartEvictionTimerWithDelay(interval_ms_); | |
| 208 } else { | |
| 209 // TODO(dmikurube): Try restarting eviction after a while. | |
| 210 LOG(WARNING) << "Stopped eviction of temporary storage due to errors " | |
| 211 "in GetUsageAndQuotaForEviction."; | |
| 212 } | |
| 213 } | |
| 214 OnEvictionRoundFinished(); | |
| 215 } | |
| 216 | |
| 217 // TODO(dmikurube): Add error handling for the case status != kQuotaStatusOk. | |
| 218 } | |
| 219 | |
| 220 void QuotaTemporaryStorageEvictor::OnGotLRUOrigin(const GURL& origin) { | |
| 221 DCHECK(CalledOnValidThread()); | |
| 222 | |
| 223 if (origin.is_empty()) { | |
| 224 if (repeated_eviction_) | |
| 225 StartEvictionTimerWithDelay(interval_ms_); | |
| 226 OnEvictionRoundFinished(); | |
| 227 return; | |
| 228 } | |
| 229 | |
| 230 quota_eviction_handler_->EvictOriginData(origin, kStorageTypeTemporary, | |
| 231 base::Bind( | |
| 232 &QuotaTemporaryStorageEvictor::OnEvictionComplete, | |
| 233 weak_factory_.GetWeakPtr())); | |
| 234 } | |
| 235 | |
| 236 void QuotaTemporaryStorageEvictor::OnEvictionComplete( | |
| 237 QuotaStatusCode status) { | |
| 238 DCHECK(CalledOnValidThread()); | |
| 239 | |
| 240 // Just calling ConsiderEviction() or StartEvictionTimerWithDelay() here is | |
| 241 // ok. No need to deal with the case that all of the Delete operations fail | |
| 242 // for a certain origin. It doesn't result in trying to evict the same | |
| 243 // origin permanently. The evictor skips origins which had deletion errors | |
| 244 // a few times. | |
| 245 | |
| 246 if (status == kQuotaStatusOk) { | |
| 247 ++statistics_.num_evicted_origins; | |
| 248 ++round_statistics_.num_evicted_origins_in_round; | |
| 249 // We many need to get rid of more space so reconsider immediately. | |
| 250 ConsiderEviction(); | |
| 251 } else { | |
| 252 ++statistics_.num_errors_on_evicting_origin; | |
| 253 if (repeated_eviction_) { | |
| 254 // Sleep for a while and retry again until we see too many errors. | |
| 255 StartEvictionTimerWithDelay(interval_ms_); | |
| 256 } | |
| 257 OnEvictionRoundFinished(); | |
| 258 } | |
| 259 } | |
| 260 | |
| 261 } // namespace quota | |
| OLD | NEW |