OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 "storage/browser/quota/quota_temporary_storage_evictor.h" | 5 #include "storage/browser/quota/quota_temporary_storage_evictor.h" |
6 | 6 |
7 #include <stdint.h> | 7 #include <stdint.h> |
8 | 8 |
9 #include <algorithm> | 9 #include <algorithm> |
10 | 10 |
11 #include "base/auto_reset.h" | |
12 #include "base/bind.h" | 11 #include "base/bind.h" |
13 #include "base/metrics/histogram_macros.h" | 12 #include "base/metrics/histogram_macros.h" |
14 #include "storage/browser/quota/quota_manager.h" | 13 #include "storage/browser/quota/quota_manager.h" |
15 #include "url/gurl.h" | 14 #include "url/gurl.h" |
16 | 15 |
17 #define UMA_HISTOGRAM_MBYTES(name, sample) \ | 16 #define UMA_HISTOGRAM_MBYTES(name, sample) \ |
18 UMA_HISTOGRAM_CUSTOM_COUNTS( \ | 17 UMA_HISTOGRAM_CUSTOM_COUNTS( \ |
19 (name), static_cast<int>((sample) / kMBytes), \ | 18 (name), static_cast<int>((sample) / kMBytes), \ |
20 1, 10 * 1024 * 1024 /* 10TB */, 100) | 19 1, 10 * 1024 * 1024 /* 10TB */, 100) |
21 | 20 |
22 #define UMA_HISTOGRAM_MINUTES(name, sample) \ | 21 #define UMA_HISTOGRAM_MINUTES(name, sample) \ |
23 UMA_HISTOGRAM_CUSTOM_TIMES( \ | 22 UMA_HISTOGRAM_CUSTOM_TIMES( \ |
24 (name), (sample), \ | 23 (name), (sample), \ |
25 base::TimeDelta::FromMinutes(1), \ | 24 base::TimeDelta::FromMinutes(1), \ |
26 base::TimeDelta::FromDays(1), 50) | 25 base::TimeDelta::FromDays(1), 50) |
27 | 26 |
28 namespace { | 27 namespace { |
29 const int64_t kMBytes = 1024 * 1024; | 28 const int64_t kMBytes = 1024 * 1024; |
30 const double kUsageRatioToStartEviction = 0.7; | 29 const double kUsageRatioToStartEviction = 0.7; |
31 const int kThresholdOfErrorsToStopEviction = 5; | 30 const int kThresholdOfErrorsToStopEviction = 5; |
32 const int kHistogramReportIntervalMinutes = 60; | 31 const int kHistogramReportIntervalMinutes = 60; |
33 | 32 const double kMustRemainAvailableRatio = 0.1; |
| 33 const int64_t kDefaultMustRemainAvailableSpace = 1024 * kMBytes; |
34 const double kDiskSpaceShortageAllowanceRatio = 0.5; | 34 const double kDiskSpaceShortageAllowanceRatio = 0.5; |
35 } | 35 } |
36 | 36 |
37 namespace storage { | 37 namespace storage { |
38 | 38 |
| 39 const int QuotaTemporaryStorageEvictor:: |
| 40 kMinAvailableToStartEvictionNotSpecified = -1; |
| 41 |
39 QuotaTemporaryStorageEvictor::EvictionRoundStatistics::EvictionRoundStatistics() | 42 QuotaTemporaryStorageEvictor::EvictionRoundStatistics::EvictionRoundStatistics() |
40 : in_round(false), | 43 : in_round(false), |
41 is_initialized(false), | 44 is_initialized(false), |
42 usage_overage_at_round(-1), | 45 usage_overage_at_round(-1), |
43 diskspace_shortage_at_round(-1), | 46 diskspace_shortage_at_round(-1), |
44 usage_on_beginning_of_round(-1), | 47 usage_on_beginning_of_round(-1), |
45 usage_on_end_of_round(-1), | 48 usage_on_end_of_round(-1), |
46 num_evicted_origins_in_round(0) { | 49 num_evicted_origins_in_round(0) { |
47 } | 50 } |
48 | 51 |
49 QuotaTemporaryStorageEvictor::QuotaTemporaryStorageEvictor( | 52 QuotaTemporaryStorageEvictor::QuotaTemporaryStorageEvictor( |
50 QuotaEvictionHandler* quota_eviction_handler, | 53 QuotaEvictionHandler* quota_eviction_handler, |
51 int64_t interval_ms) | 54 int64_t interval_ms) |
52 : quota_eviction_handler_(quota_eviction_handler), | 55 : min_available_to_start_eviction_( |
| 56 kMinAvailableToStartEvictionNotSpecified), |
| 57 quota_eviction_handler_(quota_eviction_handler), |
53 interval_ms_(interval_ms), | 58 interval_ms_(interval_ms), |
54 timer_disabled_for_testing_(false), | 59 repeated_eviction_(true), |
55 weak_factory_(this) { | 60 weak_factory_(this) { |
56 DCHECK(quota_eviction_handler); | 61 DCHECK(quota_eviction_handler); |
57 } | 62 } |
58 | 63 |
59 QuotaTemporaryStorageEvictor::~QuotaTemporaryStorageEvictor() { | 64 QuotaTemporaryStorageEvictor::~QuotaTemporaryStorageEvictor() { |
60 } | 65 } |
61 | 66 |
62 void QuotaTemporaryStorageEvictor::GetStatistics( | 67 void QuotaTemporaryStorageEvictor::GetStatistics( |
63 std::map<std::string, int64_t>* statistics) { | 68 std::map<std::string, int64_t>* statistics) { |
64 DCHECK(statistics); | 69 DCHECK(statistics); |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
130 time_of_end_of_last_nonskipped_round_ = base::Time::Now(); | 135 time_of_end_of_last_nonskipped_round_ = base::Time::Now(); |
131 } else { | 136 } else { |
132 ++statistics_.num_skipped_eviction_rounds; | 137 ++statistics_.num_skipped_eviction_rounds; |
133 } | 138 } |
134 // Reset stats for next round. | 139 // Reset stats for next round. |
135 round_statistics_ = EvictionRoundStatistics(); | 140 round_statistics_ = EvictionRoundStatistics(); |
136 } | 141 } |
137 | 142 |
138 void QuotaTemporaryStorageEvictor::Start() { | 143 void QuotaTemporaryStorageEvictor::Start() { |
139 DCHECK(CalledOnValidThread()); | 144 DCHECK(CalledOnValidThread()); |
140 | |
141 base::AutoReset<bool> auto_reset(&timer_disabled_for_testing_, false); | |
142 StartEvictionTimerWithDelay(0); | 145 StartEvictionTimerWithDelay(0); |
143 | 146 |
144 if (histogram_timer_.IsRunning()) | 147 if (histogram_timer_.IsRunning()) |
145 return; | 148 return; |
146 | 149 |
147 histogram_timer_.Start( | 150 histogram_timer_.Start( |
148 FROM_HERE, base::TimeDelta::FromMinutes(kHistogramReportIntervalMinutes), | 151 FROM_HERE, base::TimeDelta::FromMinutes(kHistogramReportIntervalMinutes), |
149 this, &QuotaTemporaryStorageEvictor::ReportPerHourHistogram); | 152 this, &QuotaTemporaryStorageEvictor::ReportPerHourHistogram); |
150 } | 153 } |
151 | 154 |
152 void QuotaTemporaryStorageEvictor::StartEvictionTimerWithDelay(int delay_ms) { | 155 void QuotaTemporaryStorageEvictor::StartEvictionTimerWithDelay(int delay_ms) { |
153 if (eviction_timer_.IsRunning() || timer_disabled_for_testing_) | 156 if (eviction_timer_.IsRunning()) |
154 return; | 157 return; |
155 eviction_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(delay_ms), | 158 eviction_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(delay_ms), |
156 this, &QuotaTemporaryStorageEvictor::ConsiderEviction); | 159 this, &QuotaTemporaryStorageEvictor::ConsiderEviction); |
157 } | 160 } |
158 | 161 |
159 void QuotaTemporaryStorageEvictor::ConsiderEviction() { | 162 void QuotaTemporaryStorageEvictor::ConsiderEviction() { |
160 OnEvictionRoundStarted(); | 163 OnEvictionRoundStarted(); |
161 quota_eviction_handler_->GetEvictionRoundInfo( | 164 |
162 base::Bind(&QuotaTemporaryStorageEvictor::OnGotEvictionRoundInfo, | 165 if (min_available_to_start_eviction_ == |
163 weak_factory_.GetWeakPtr())); | 166 kMinAvailableToStartEvictionNotSpecified) { |
| 167 quota_eviction_handler_->AsyncGetVolumeInfo( |
| 168 base::Bind(&QuotaTemporaryStorageEvictor::OnGotVolumeInfo, |
| 169 weak_factory_.GetWeakPtr())); |
| 170 } else { |
| 171 quota_eviction_handler_->GetUsageAndQuotaForEviction( |
| 172 base::Bind(&QuotaTemporaryStorageEvictor::OnGotUsageAndQuotaForEviction, |
| 173 weak_factory_.GetWeakPtr(), |
| 174 min_available_to_start_eviction_)); |
| 175 } |
164 } | 176 } |
165 | 177 |
166 void QuotaTemporaryStorageEvictor::OnGotEvictionRoundInfo( | 178 void QuotaTemporaryStorageEvictor::OnGotVolumeInfo( |
| 179 bool success, uint64_t available_space, uint64_t total_size) { |
| 180 // Compute how much to keep free as a function of total disk size. |
| 181 int64_t must_remain_available_space = success ? |
| 182 static_cast<int64_t>(total_size * kMustRemainAvailableRatio) : |
| 183 kDefaultMustRemainAvailableSpace; |
| 184 |
| 185 quota_eviction_handler_->GetUsageAndQuotaForEviction( |
| 186 base::Bind(&QuotaTemporaryStorageEvictor::OnGotUsageAndQuotaForEviction, |
| 187 weak_factory_.GetWeakPtr(), must_remain_available_space)); |
| 188 } |
| 189 |
| 190 void QuotaTemporaryStorageEvictor::OnGotUsageAndQuotaForEviction( |
| 191 int64_t must_remain_available_space, |
167 QuotaStatusCode status, | 192 QuotaStatusCode status, |
168 const QuotaSettings& settings, | 193 const UsageAndQuota& qau) { |
169 int64_t available_space, | 194 DCHECK(CalledOnValidThread()); |
170 int64_t total_space, | |
171 int64_t current_usage, | |
172 bool current_usage_is_complete) { | |
173 DCHECK_GE(current_usage, 0); | |
174 | 195 |
175 // Note: if there is no storage pressure, |current_usage| | 196 int64_t usage = qau.global_limited_usage; |
176 // may not be fully calculated and may be 0. | 197 DCHECK_GE(usage, 0); |
177 | 198 |
178 if (status != kQuotaStatusOk) | 199 if (status != kQuotaStatusOk) |
179 ++statistics_.num_errors_on_getting_usage_and_quota; | 200 ++statistics_.num_errors_on_getting_usage_and_quota; |
180 | 201 |
181 int64_t usage_overage = std::max( | 202 int64_t usage_overage = std::max( |
182 static_cast<int64_t>(0), | 203 static_cast<int64_t>(0), |
183 current_usage - static_cast<int64_t>(settings.pool_size * | 204 usage - static_cast<int64_t>(qau.quota * kUsageRatioToStartEviction)); |
184 kUsageRatioToStartEviction)); | 205 |
185 int64_t diskspace_shortage = | 206 int64_t diskspace_shortage = std::max( |
186 std::max(static_cast<int64_t>(0), | 207 static_cast<int64_t>(0), |
187 settings.must_remain_available - available_space); | 208 must_remain_available_space - qau.available_disk_space); |
188 DCHECK(current_usage_is_complete || diskspace_shortage == 0); | |
189 | 209 |
190 // If we're using so little that freeing all of it wouldn't help, | 210 // If we're using so little that freeing all of it wouldn't help, |
191 // don't let the low space condition cause us to delete it all. | 211 // don't let the low space condition cause us to delete it all. |
192 if (current_usage < static_cast<int64_t>(diskspace_shortage * | 212 if (usage < static_cast<int64_t>(diskspace_shortage * |
193 kDiskSpaceShortageAllowanceRatio)) { | 213 kDiskSpaceShortageAllowanceRatio)) { |
194 diskspace_shortage = 0; | 214 diskspace_shortage = 0; |
195 } | 215 } |
196 | 216 |
197 if (!round_statistics_.is_initialized) { | 217 if (!round_statistics_.is_initialized) { |
198 round_statistics_.usage_overage_at_round = usage_overage; | 218 round_statistics_.usage_overage_at_round = usage_overage; |
199 round_statistics_.diskspace_shortage_at_round = diskspace_shortage; | 219 round_statistics_.diskspace_shortage_at_round = diskspace_shortage; |
200 round_statistics_.usage_on_beginning_of_round = current_usage; | 220 round_statistics_.usage_on_beginning_of_round = usage; |
201 round_statistics_.is_initialized = true; | 221 round_statistics_.is_initialized = true; |
202 } | 222 } |
203 round_statistics_.usage_on_end_of_round = current_usage; | 223 round_statistics_.usage_on_end_of_round = usage; |
204 | 224 |
205 int64_t amount_to_evict = std::max(usage_overage, diskspace_shortage); | 225 int64_t amount_to_evict = std::max(usage_overage, diskspace_shortage); |
206 if (status == kQuotaStatusOk && amount_to_evict > 0) { | 226 if (status == kQuotaStatusOk && amount_to_evict > 0) { |
207 // Space is getting tight. Get the least recently used origin and continue. | 227 // Space is getting tight. Get the least recently used origin and continue. |
208 // TODO(michaeln): if the reason for eviction is low physical disk space, | 228 // TODO(michaeln): if the reason for eviction is low physical disk space, |
209 // make 'unlimited' origins subject to eviction too. | 229 // make 'unlimited' origins subject to eviction too. |
210 quota_eviction_handler_->GetEvictionOrigin( | 230 quota_eviction_handler_->GetEvictionOrigin( |
211 kStorageTypeTemporary, in_progress_eviction_origins_, | 231 kStorageTypeTemporary, in_progress_eviction_origins_, qau.quota, |
212 settings.pool_size, | |
213 base::Bind(&QuotaTemporaryStorageEvictor::OnGotEvictionOrigin, | 232 base::Bind(&QuotaTemporaryStorageEvictor::OnGotEvictionOrigin, |
214 weak_factory_.GetWeakPtr())); | 233 weak_factory_.GetWeakPtr())); |
215 return; | 234 } else { |
| 235 if (repeated_eviction_) { |
| 236 // No action required, sleep for a while and check again later. |
| 237 if (statistics_.num_errors_on_getting_usage_and_quota < |
| 238 kThresholdOfErrorsToStopEviction) { |
| 239 StartEvictionTimerWithDelay(interval_ms_); |
| 240 } else { |
| 241 // TODO(dmikurube): Try restarting eviction after a while. |
| 242 LOG(WARNING) << "Stopped eviction of temporary storage due to errors " |
| 243 "in GetUsageAndQuotaForEviction."; |
| 244 } |
| 245 } |
| 246 OnEvictionRoundFinished(); |
216 } | 247 } |
217 | 248 |
218 // No action required, sleep for a while and check again later. | 249 // TODO(dmikurube): Add error handling for the case status != kQuotaStatusOk. |
219 if (statistics_.num_errors_on_getting_usage_and_quota < | |
220 kThresholdOfErrorsToStopEviction) { | |
221 StartEvictionTimerWithDelay(interval_ms_); | |
222 } else { | |
223 // TODO(dmikurube): Add error handling for the case status is not OK. | |
224 // TODO(dmikurube): Try restarting eviction after a while. | |
225 LOG(WARNING) << "Stopped eviction of temporary storage due to errors"; | |
226 } | |
227 | |
228 OnEvictionRoundFinished(); | |
229 } | 250 } |
230 | 251 |
231 void QuotaTemporaryStorageEvictor::OnGotEvictionOrigin(const GURL& origin) { | 252 void QuotaTemporaryStorageEvictor::OnGotEvictionOrigin(const GURL& origin) { |
232 DCHECK(CalledOnValidThread()); | 253 DCHECK(CalledOnValidThread()); |
233 | 254 |
234 if (origin.is_empty()) { | 255 if (origin.is_empty()) { |
235 StartEvictionTimerWithDelay(interval_ms_); | 256 if (repeated_eviction_) |
| 257 StartEvictionTimerWithDelay(interval_ms_); |
236 OnEvictionRoundFinished(); | 258 OnEvictionRoundFinished(); |
237 return; | 259 return; |
238 } | 260 } |
239 | 261 |
240 in_progress_eviction_origins_.insert(origin); | 262 in_progress_eviction_origins_.insert(origin); |
241 | 263 |
242 quota_eviction_handler_->EvictOriginData(origin, kStorageTypeTemporary, | 264 quota_eviction_handler_->EvictOriginData(origin, kStorageTypeTemporary, |
243 base::Bind( | 265 base::Bind( |
244 &QuotaTemporaryStorageEvictor::OnEvictionComplete, | 266 &QuotaTemporaryStorageEvictor::OnEvictionComplete, |
245 weak_factory_.GetWeakPtr())); | 267 weak_factory_.GetWeakPtr())); |
246 } | 268 } |
247 | 269 |
248 void QuotaTemporaryStorageEvictor::OnEvictionComplete( | 270 void QuotaTemporaryStorageEvictor::OnEvictionComplete( |
249 QuotaStatusCode status) { | 271 QuotaStatusCode status) { |
250 DCHECK(CalledOnValidThread()); | 272 DCHECK(CalledOnValidThread()); |
251 | 273 |
252 // Just calling ConsiderEviction() or StartEvictionTimerWithDelay() here is | 274 // Just calling ConsiderEviction() or StartEvictionTimerWithDelay() here is |
253 // ok. No need to deal with the case that all of the Delete operations fail | 275 // ok. No need to deal with the case that all of the Delete operations fail |
254 // for a certain origin. It doesn't result in trying to evict the same | 276 // for a certain origin. It doesn't result in trying to evict the same |
255 // origin permanently. The evictor skips origins which had deletion errors | 277 // origin permanently. The evictor skips origins which had deletion errors |
256 // a few times. | 278 // a few times. |
257 | 279 |
258 if (status == kQuotaStatusOk) { | 280 if (status == kQuotaStatusOk) { |
259 ++statistics_.num_evicted_origins; | 281 ++statistics_.num_evicted_origins; |
260 ++round_statistics_.num_evicted_origins_in_round; | 282 ++round_statistics_.num_evicted_origins_in_round; |
261 // We many need to get rid of more space so reconsider immediately. | 283 // We many need to get rid of more space so reconsider immediately. |
262 ConsiderEviction(); | 284 ConsiderEviction(); |
263 } else { | 285 } else { |
264 ++statistics_.num_errors_on_evicting_origin; | 286 ++statistics_.num_errors_on_evicting_origin; |
265 // Sleep for a while and retry again until we see too many errors. | 287 if (repeated_eviction_) { |
266 StartEvictionTimerWithDelay(interval_ms_); | 288 // Sleep for a while and retry again until we see too many errors. |
| 289 StartEvictionTimerWithDelay(interval_ms_); |
| 290 } |
267 OnEvictionRoundFinished(); | 291 OnEvictionRoundFinished(); |
268 } | 292 } |
269 } | 293 } |
270 | 294 |
271 } // namespace storage | 295 } // namespace storage |
OLD | NEW |