Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(807)

Side by Side Diff: net/sdch/sdch_owner.cc

Issue 841883002: Add an eviction mechanism for SDCH dictionaries. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: git cl format Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 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 "net/sdch/sdch_owner.h"
6
7 #include "base/bind.h"
8 #include "base/metrics/histogram_macros.h"
9 #include "base/time/default_clock.h"
10 #include "net/base/sdch_manager.h"
11 #include "net/base/sdch_net_log_params.h"
12
13 namespace {
14
15 enum DictionaryFate {
16 // A Get-Dictionary header wasn't acted on.
17 DICTIONARY_FATE_GET_IGNORED = 1,
18
19 // A fetch was attempted, but failed.
20 // TODO(rdsmith): Actually record this case.
21 DICTIONARY_FATE_FETCH_FAILED = 2,
22
23 // A successful fetch was dropped on the floor, no space.
24 DICTIONARY_FATE_FETCH_IGNORED_NO_SPACE = 3,
25
26 // A successful fetch was refused by the SdchManager.
27 DICTIONARY_FATE_FETCH_MANAGER_REFUSED = 4,
28
29 // A dictionary was successfully added.
30 DICTIONARY_FATE_ADDED = 5,
31
32 // A dictionary was evicted by an incoming dict.
33 DICTIONARY_FATE_EVICT_FOR_DICT = 6,
34
35 // A dictionary was evicted by memory pressure.
36 DICTIONARY_FATE_EVICT_FOR_MEMORY = 7,
37
38 // A dictionary was evicted on destruction.
39 DICTIONARY_FATE_EVICT_FOR_DESTRUCTION = 8,
40
41 DICTIONARY_FATE_MAX = 9
42 };
43
44 void RecordDictionaryFate(enum DictionaryFate fate) {
45 UMA_HISTOGRAM_ENUMERATION("Sdch3.DictionaryFate", fate, DICTIONARY_FATE_MAX);
46 }
47
48 void RecordDictionaryEviction(int use_count, DictionaryFate fate) {
49 DCHECK(fate == DICTIONARY_FATE_EVICT_FOR_DICT ||
50 fate == DICTIONARY_FATE_EVICT_FOR_MEMORY ||
51 fate == DICTIONARY_FATE_EVICT_FOR_DESTRUCTION);
52
53 UMA_HISTOGRAM_COUNTS_100("Sdch3.DictionaryUseCount", use_count);
54 RecordDictionaryFate(fate);
55 }
56
57 } // namespace
58
59 namespace net {
60
61 // Adjust SDCH limits downwards for mobile.
62 #if defined(OS_ANDROID) || defined(OS_IOS)
63 // static
64 const size_t SdchOwner::kMaxTotalDictionarySize = 500 * 1000;
65 #else
66 // static
67 const size_t SdchOwner::kMaxTotalDictionarySize = 20 * 1000 * 1000;
68 #endif
69
70 // Somewhat arbitrary, but we assume a dictionary smaller than
71 // 50K isn't going to do anyone any good. Note that this still doesn't
72 // prevent download and addition unless there is less than this
73 // amount of space available in storage.
74 const size_t SdchOwner::kMinSpaceForDictionaryFetch = 50 * 1000;
75
76 SdchOwner::SdchOwner(net::SdchManager* sdch_manager,
77 net::URLRequestContext* context)
78 : manager_(sdch_manager),
79 fetcher_(context,
80 base::Bind(&SdchOwner::OnDictionaryFetched,
81 // Because |fetcher_| is owned by SdchOwner, the
82 // SdchOwner object will be available for the lifetime
83 // of |fetcher_|.
84 base::Unretained(this))),
85 total_dictionary_bytes_(0),
86 clock_(new base::DefaultClock),
87 max_total_dictionary_size_(kMaxTotalDictionarySize),
88 min_space_for_dictionary_fetch_(kMinSpaceForDictionaryFetch),
89 memory_pressure_listener_(
90 base::Bind(&SdchOwner::OnMemoryPressure,
91 // Because |memory_pressure_listener_| is owned by
92 // SdchOwner, the SdchOwner object will be available
93 // for the lifetime // of |memory_pressure_listener_|.
Bence 2015/01/08 14:37:17 Remove extra // from middle of line.
Randy Smith (Not in Mondays) 2015/01/08 15:48:23 Ooops. Done.
94 base::Unretained(this))) {
95 manager_->AddObserver(this);
96 }
97
98 SdchOwner::~SdchOwner() {
99 for (auto it = local_dictionary_info_.begin();
100 it != local_dictionary_info_.end(); ++it) {
101 RecordDictionaryEviction(it->second.use_count,
102 DICTIONARY_FATE_EVICT_FOR_DESTRUCTION);
103 }
104 manager_->RemoveObserver(this);
105 }
106
107 void SdchOwner::SetMaxTotalDictionarySize(size_t max_total_dictionary_size) {
108 max_total_dictionary_size_ = max_total_dictionary_size;
109 }
110
111 void SdchOwner::SetMinSpaceForDictionaryFetch(
112 size_t min_space_for_dictionary_fetch) {
113 min_space_for_dictionary_fetch_ = min_space_for_dictionary_fetch;
114 }
115
116 void SdchOwner::OnDictionaryFetched(const std::string& dictionary_text,
117 const GURL& dictionary_url,
118 const net::BoundNetLog& net_log) {
119 struct DictionaryItem {
120 base::Time last_used;
121 std::string server_hash;
122 int use_count;
123 size_t dictionary_size;
124
125 DictionaryItem() : use_count(0), dictionary_size(0) {}
126 DictionaryItem(const base::Time& last_used,
127 const std::string& server_hash,
128 int use_count,
129 size_t dictionary_size)
130 : last_used(last_used),
131 server_hash(server_hash),
132 use_count(use_count),
133 dictionary_size(dictionary_size) {}
134 DictionaryItem(const DictionaryItem& rhs) {
Bence 2015/01/08 14:37:17 Copy constructor is generated when needed, why do
Randy Smith (Not in Mondays) 2015/01/08 15:48:23 So I interpreted http://google-styleguide.googleco
Bence 2015/01/08 15:56:38 Excellent.
135 last_used = rhs.last_used;
136 server_hash = rhs.server_hash;
137 use_count = rhs.use_count;
138 dictionary_size = rhs.dictionary_size;
139 }
140 DictionaryItem& operator=(const DictionaryItem& rhs) {
141 last_used = rhs.last_used;
142 server_hash = rhs.server_hash;
143 use_count = rhs.use_count;
144 dictionary_size = rhs.dictionary_size;
145 return *this;
146 }
147 bool operator<(const DictionaryItem& rhs) const {
148 return last_used < rhs.last_used;
149 }
150 };
151
152 std::vector<DictionaryItem> stale_dictionary_list;
153 size_t recoverable_bytes = 0;
154 base::Time stale_boundary(clock_->Now() - base::TimeDelta::FromDays(1));
155 for (auto used_it = local_dictionary_info_.begin();
156 used_it != local_dictionary_info_.end(); ++used_it) {
157 if (used_it->second.last_used < stale_boundary) {
158 stale_dictionary_list.push_back(
159 DictionaryItem(used_it->second.last_used, used_it->first,
160 used_it->second.use_count, used_it->second.size));
161 recoverable_bytes += used_it->second.size;
162 }
163 }
164
165 if (total_dictionary_bytes_ + dictionary_text.size() - recoverable_bytes >
166 max_total_dictionary_size_) {
167 RecordDictionaryFate(DICTIONARY_FATE_FETCH_IGNORED_NO_SPACE);
168 net::SdchManager::SdchErrorRecovery(SDCH_DICTIONARY_NO_ROOM);
169 net_log.AddEvent(net::NetLog::TYPE_SDCH_DICTIONARY_ERROR,
170 base::Bind(&net::NetLogSdchDictionaryFetchProblemCallback,
171 SDCH_DICTIONARY_NO_ROOM, dictionary_url, true));
172 return;
173 }
174
175 // Evict from oldest to youngest until we have space.
176 std::sort(stale_dictionary_list.begin(), stale_dictionary_list.end());
177 size_t avail_bytes = max_total_dictionary_size_ - total_dictionary_bytes_;
178 auto stale_it = stale_dictionary_list.begin();
179 while (avail_bytes < dictionary_text.size() &&
180 stale_it != stale_dictionary_list.end()) {
181 manager_->RemoveSdchDictionary(stale_it->server_hash);
182 RecordDictionaryEviction(stale_it->use_count,
183 DICTIONARY_FATE_EVICT_FOR_DICT);
184 local_dictionary_info_.erase(stale_it->server_hash);
185 avail_bytes += stale_it->dictionary_size;
186 ++stale_it;
187 }
188 DCHECK(avail_bytes >= dictionary_text.size());
189
190 std::string server_hash;
191 net::SdchProblemCode rv = manager_->AddSdchDictionary(
192 dictionary_text, dictionary_url, &server_hash);
193 if (rv != net::SDCH_OK) {
194 RecordDictionaryFate(DICTIONARY_FATE_FETCH_MANAGER_REFUSED);
195 net::SdchManager::SdchErrorRecovery(rv);
196 net_log.AddEvent(net::NetLog::TYPE_SDCH_DICTIONARY_ERROR,
197 base::Bind(&net::NetLogSdchDictionaryFetchProblemCallback,
198 rv, dictionary_url, true));
199 return;
200 }
201
202 RecordDictionaryFate(DICTIONARY_FATE_ADDED);
203
204 DCHECK(local_dictionary_info_.end() ==
205 local_dictionary_info_.find(server_hash));
206 total_dictionary_bytes_ += dictionary_text.size();
207 local_dictionary_info_[server_hash] = DictionaryInfo(
208 // Set the time last used to something to avoid thrashing, but not recent,
209 // to avoid taking too much time/space with useless dictionaries/one-off
210 // visits to web sites.
211 clock_->Now() - base::TimeDelta::FromHours(23), dictionary_text.size());
212 }
213
214 void SdchOwner::OnDictionaryUsed(SdchManager* manager,
215 const std::string& server_hash) {
216 auto it = local_dictionary_info_.find(server_hash);
217 DCHECK(local_dictionary_info_.end() != it);
218
219 it->second.last_used = clock_->Now();
220 it->second.use_count++;
221 }
222
223 void SdchOwner::OnGetDictionary(net::SdchManager* manager,
224 const GURL& request_url,
225 const GURL& dictionary_url) {
226 base::Time stale_boundary(clock_->Now() - base::TimeDelta::FromDays(1));
227 size_t avail_bytes = 0;
228 for (auto it = local_dictionary_info_.begin();
229 it != local_dictionary_info_.end(); ++it) {
230 if (it->second.last_used < stale_boundary)
231 avail_bytes += it->second.size;
232 }
233
234 // Don't initiate the fetch if we wouldn't be able to store any
235 // reasonable dictionary.
236 // TODO(rdsmith): Maybe do a HEAD request to figure out how much
237 // storage we'd actually need?
238 if (max_total_dictionary_size_ < (total_dictionary_bytes_ - avail_bytes +
239 min_space_for_dictionary_fetch_)) {
240 RecordDictionaryFate(DICTIONARY_FATE_GET_IGNORED);
241 // TODO(rdsmith): Log a net-internals error. This requires
242 // SdchManager to forward the URLRequest that detected the
243 // Get-Dictionary header to its observers, which is tricky
244 // because SdchManager is layered underneath URLRequest.
245 return;
246 }
247
248 fetcher_.Schedule(dictionary_url);
249 }
250
251 void SdchOwner::OnClearDictionaries(net::SdchManager* manager) {
252 total_dictionary_bytes_ = 0;
253 local_dictionary_info_.clear();
254 fetcher_.Cancel();
255 }
256
257 void SdchOwner::SetClockForTesting(scoped_ptr<base::Clock> clock) {
258 clock_ = clock.Pass();
259 }
260
261 void SdchOwner::OnMemoryPressure(
262 base::MemoryPressureListener::MemoryPressureLevel level) {
263 DCHECK_NE(base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, level);
264
265 for (auto it = local_dictionary_info_.begin();
266 it != local_dictionary_info_.end(); ++it) {
267 RecordDictionaryEviction(it->second.use_count,
268 DICTIONARY_FATE_EVICT_FOR_MEMORY);
269 }
270
271 // TODO(rdsmith): Make a distinction between moderate and critical
272 // memory pressure.
273 manager_->ClearData();
274 }
275
276 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698