Upstream version 10.38.222.0
[platform/framework/web/crosswalk.git] / src / webkit / browser / quota / quota_temporary_storage_evictor.cc
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.
4
5 #include "webkit/browser/quota/quota_temporary_storage_evictor.h"
6
7 #include <algorithm>
8
9 #include "base/bind.h"
10 #include "base/metrics/histogram.h"
11 #include "url/gurl.h"
12 #include "webkit/browser/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