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
3 // found in the LICENSE file.
5 #include "webkit/browser/quota/quota_temporary_storage_evictor.h"
10 #include "base/metrics/histogram.h"
12 #include "webkit/browser/quota/quota_manager.h"
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)
19 #define UMA_HISTOGRAM_MINUTES(name, sample) \
20 UMA_HISTOGRAM_CUSTOM_TIMES( \
22 base::TimeDelta::FromMinutes(1), \
23 base::TimeDelta::FromDays(1), 50)
26 const int64 kMBytes
= 1024 * 1024;
27 const double kUsageRatioToStartEviction
= 0.7;
28 const int kThresholdOfErrorsToStopEviction
= 5;
29 const int kHistogramReportIntervalMinutes
= 60;
34 const int QuotaTemporaryStorageEvictor::
35 kMinAvailableDiskSpaceToStartEvictionNotSpecified
= -1;
37 QuotaTemporaryStorageEvictor::EvictionRoundStatistics::EvictionRoundStatistics()
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) {
47 QuotaTemporaryStorageEvictor::QuotaTemporaryStorageEvictor(
48 QuotaEvictionHandler
* quota_eviction_handler
,
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),
56 DCHECK(quota_eviction_handler
);
59 QuotaTemporaryStorageEvictor::~QuotaTemporaryStorageEvictor() {
62 void QuotaTemporaryStorageEvictor::GetStatistics(
63 std::map
<std::string
, int64
>* statistics
) {
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
;
78 void QuotaTemporaryStorageEvictor::ReportPerRoundHistogram() {
79 DCHECK(round_statistics_
.in_round
);
80 DCHECK(round_statistics_
.is_initialized
);
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
);
99 void QuotaTemporaryStorageEvictor::ReportPerHourHistogram() {
100 Statistics
stats_in_hour(statistics_
);
101 stats_in_hour
.subtract_assign(previous_statistics_
);
102 previous_statistics_
= statistics_
;
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
);
116 void QuotaTemporaryStorageEvictor::OnEvictionRoundStarted() {
117 if (round_statistics_
.in_round
)
119 round_statistics_
.in_round
= true;
120 round_statistics_
.start_time
= base::Time::Now();
121 ++statistics_
.num_eviction_rounds
;
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();
130 ++statistics_
.num_skipped_eviction_rounds
;
132 // Reset stats for next round.
133 round_statistics_
= EvictionRoundStatistics();
136 void QuotaTemporaryStorageEvictor::Start() {
137 DCHECK(CalledOnValidThread());
138 StartEvictionTimerWithDelay(0);
140 if (histogram_timer_
.IsRunning())
143 histogram_timer_
.Start(
144 FROM_HERE
, base::TimeDelta::FromMinutes(kHistogramReportIntervalMinutes
),
145 this, &QuotaTemporaryStorageEvictor::ReportPerHourHistogram
);
148 void QuotaTemporaryStorageEvictor::StartEvictionTimerWithDelay(int delay_ms
) {
149 if (eviction_timer_
.IsRunning())
151 eviction_timer_
.Start(FROM_HERE
, base::TimeDelta::FromMilliseconds(delay_ms
),
152 this, &QuotaTemporaryStorageEvictor::ConsiderEviction
);
155 void QuotaTemporaryStorageEvictor::ConsiderEviction() {
156 OnEvictionRoundStarted();
158 // Get usage and disk space, then continue.
159 quota_eviction_handler_
->GetUsageAndQuotaForEviction(
160 base::Bind(&QuotaTemporaryStorageEvictor::OnGotUsageAndQuotaForEviction
,
161 weak_factory_
.GetWeakPtr()));
164 void QuotaTemporaryStorageEvictor::OnGotUsageAndQuotaForEviction(
165 QuotaStatusCode status
,
166 const UsageAndQuota
& qau
) {
167 DCHECK(CalledOnValidThread());
169 int64 usage
= qau
.global_limited_usage
;
172 if (status
!= kQuotaStatusOk
)
173 ++statistics_
.num_errors_on_getting_usage_and_quota
;
175 int64 usage_overage
= std::max(
176 static_cast<int64
>(0),
177 usage
- static_cast<int64
>(qau
.quota
* kUsageRatioToStartEviction
));
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
);
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;
191 round_statistics_
.usage_on_end_of_round
= usage
;
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()));
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_
);
209 // TODO(dmikurube): Try restarting eviction after a while.
210 LOG(WARNING
) << "Stopped eviction of temporary storage due to errors "
211 "in GetUsageAndQuotaForEviction.";
214 OnEvictionRoundFinished();
217 // TODO(dmikurube): Add error handling for the case status != kQuotaStatusOk.
220 void QuotaTemporaryStorageEvictor::OnGotLRUOrigin(const GURL
& origin
) {
221 DCHECK(CalledOnValidThread());
223 if (origin
.is_empty()) {
224 if (repeated_eviction_
)
225 StartEvictionTimerWithDelay(interval_ms_
);
226 OnEvictionRoundFinished();
230 quota_eviction_handler_
->EvictOriginData(origin
, kStorageTypeTemporary
,
232 &QuotaTemporaryStorageEvictor::OnEvictionComplete
,
233 weak_factory_
.GetWeakPtr()));
236 void QuotaTemporaryStorageEvictor::OnEvictionComplete(
237 QuotaStatusCode status
) {
238 DCHECK(CalledOnValidThread());
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
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.
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_
);
257 OnEvictionRoundFinished();
261 } // namespace storage