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