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

Side by Side Diff: components/suggestions/suggestions_service.cc

Issue 766053010: SuggestionsService undo blacklist functionality. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: GetTimeUntilReadyForUpload should not return negative values Created 6 years 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
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 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 "components/suggestions/suggestions_service.h" 5 #include "components/suggestions/suggestions_service.h"
6 6
7 #include <string> 7 #include <string>
8 8
9 #include "base/memory/scoped_ptr.h" 9 #include "base/memory/scoped_ptr.h"
10 #include "base/message_loop/message_loop_proxy.h" 10 #include "base/message_loop/message_loop_proxy.h"
(...skipping 12 matching lines...) Expand all
23 #include "net/base/net_errors.h" 23 #include "net/base/net_errors.h"
24 #include "net/base/url_util.h" 24 #include "net/base/url_util.h"
25 #include "net/http/http_response_headers.h" 25 #include "net/http/http_response_headers.h"
26 #include "net/http/http_status_code.h" 26 #include "net/http/http_status_code.h"
27 #include "net/http/http_util.h" 27 #include "net/http/http_util.h"
28 #include "net/url_request/url_fetcher.h" 28 #include "net/url_request/url_fetcher.h"
29 #include "net/url_request/url_request_status.h" 29 #include "net/url_request/url_request_status.h"
30 #include "url/gurl.h" 30 #include "url/gurl.h"
31 31
32 using base::CancelableClosure; 32 using base::CancelableClosure;
33 using base::TimeDelta;
34 using base::TimeTicks;
33 35
34 namespace suggestions { 36 namespace suggestions {
35 37
36 namespace { 38 namespace {
37 39
38 // Used to UMA log the state of the last response from the server. 40 // Used to UMA log the state of the last response from the server.
39 enum SuggestionsResponseState { 41 enum SuggestionsResponseState {
40 RESPONSE_EMPTY, 42 RESPONSE_EMPTY,
41 RESPONSE_INVALID, 43 RESPONSE_INVALID,
42 RESPONSE_VALID, 44 RESPONSE_VALID,
(...skipping 24 matching lines...) Expand all
67 const SuggestionsProfile& suggestions, 69 const SuggestionsProfile& suggestions,
68 std::vector<SuggestionsService::ResponseCallback>* requestors) { 70 std::vector<SuggestionsService::ResponseCallback>* requestors) {
69 std::vector<SuggestionsService::ResponseCallback> temp_requestors; 71 std::vector<SuggestionsService::ResponseCallback> temp_requestors;
70 temp_requestors.swap(*requestors); 72 temp_requestors.swap(*requestors);
71 std::vector<SuggestionsService::ResponseCallback>::iterator it; 73 std::vector<SuggestionsService::ResponseCallback>::iterator it;
72 for (it = temp_requestors.begin(); it != temp_requestors.end(); ++it) { 74 for (it = temp_requestors.begin(); it != temp_requestors.end(); ++it) {
73 if (!it->is_null()) it->Run(suggestions); 75 if (!it->is_null()) it->Run(suggestions);
74 } 76 }
75 } 77 }
76 78
77 // Default delay used when scheduling a blacklist request. 79 // Default delay used when scheduling a request.
78 const int kBlacklistDefaultDelaySec = 1; 80 const TimeDelta kDefaultSchedulingDelay = TimeDelta::FromSeconds(1);
Mathieu 2014/12/04 18:53:32 consider having a const int and rather initialize
manzagop (departed) 2014/12/05 15:13:22 Done. Went with local variables.
79 81
80 // Multiplier on the delay used when scheduling a blacklist request, in case the 82 // Multiplier on the delay used when re-scheduling a failed request.
81 // last observed request was unsuccessful. 83 const int kSchedulingBackoffMultiplier = 2;
82 const int kBlacklistBackoffMultiplier = 2;
83 84
84 // Maximum valid delay for scheduling a request. Candidate delays larger than 85 // Maximum valid delay for scheduling a request. Candidate delays larger than
85 // this are rejected. This means the maximum backoff is at least 300 / 2, i.e. 86 // this are rejected. This means the maximum backoff is at least 5 / 2 minutes.
86 // 2.5 minutes. 87 const TimeDelta kSchedulingMaxDelaySec = TimeDelta::FromMinutes(5);
87 const int kBlacklistMaxDelaySec = 300; // 5 minutes
88 88
89 } // namespace 89 } // namespace
90 90
91 const char kSuggestionsFieldTrialName[] = "ChromeSuggestions"; 91 const char kSuggestionsFieldTrialName[] = "ChromeSuggestions";
92 const char kSuggestionsFieldTrialControlParam[] = "control"; 92 const char kSuggestionsFieldTrialControlParam[] = "control";
93 const char kSuggestionsFieldTrialStateEnabled[] = "enabled"; 93 const char kSuggestionsFieldTrialStateEnabled[] = "enabled";
94 94
95 // TODO(mathp): Put this in TemplateURL. 95 // TODO(mathp): Put this in TemplateURL.
96 const char kSuggestionsURL[] = "https://www.google.com/chromesuggestions?t=2"; 96 const char kSuggestionsURL[] = "https://www.google.com/chromesuggestions?t=2";
97 const char kSuggestionsBlacklistURLPrefix[] = 97 const char kSuggestionsBlacklistURLPrefix[] =
98 "https://www.google.com/chromesuggestions/blacklist?t=2&url="; 98 "https://www.google.com/chromesuggestions/blacklist?t=2&url=";
99 const char kSuggestionsBlacklistURLParam[] = "url"; 99 const char kSuggestionsBlacklistURLParam[] = "url";
100 100
101 // The default expiry timeout is 72 hours. 101 // The default expiry timeout is 72 hours.
102 const int64 kDefaultExpiryUsec = 72 * base::Time::kMicrosecondsPerHour; 102 const int64 kDefaultExpiryUsec = 72 * base::Time::kMicrosecondsPerHour;
103 103
104 SuggestionsService::SuggestionsService( 104 SuggestionsService::SuggestionsService(
105 net::URLRequestContextGetter* url_request_context, 105 net::URLRequestContextGetter* url_request_context,
106 scoped_ptr<SuggestionsStore> suggestions_store, 106 scoped_ptr<SuggestionsStore> suggestions_store,
107 scoped_ptr<ImageManager> thumbnail_manager, 107 scoped_ptr<ImageManager> thumbnail_manager,
108 scoped_ptr<BlacklistStore> blacklist_store) 108 scoped_ptr<BlacklistStore> blacklist_store)
109 : url_request_context_(url_request_context), 109 : url_request_context_(url_request_context),
110 suggestions_store_(suggestions_store.Pass()), 110 suggestions_store_(suggestions_store.Pass()),
111 thumbnail_manager_(thumbnail_manager.Pass()), 111 thumbnail_manager_(thumbnail_manager.Pass()),
112 blacklist_store_(blacklist_store.Pass()), 112 blacklist_store_(blacklist_store.Pass()),
113 blacklist_delay_sec_(kBlacklistDefaultDelaySec), 113 scheduling_delay_(kDefaultSchedulingDelay),
114 suggestions_url_(kSuggestionsURL), 114 suggestions_url_(kSuggestionsURL),
115 blacklist_url_prefix_(kSuggestionsBlacklistURLPrefix), 115 blacklist_url_prefix_(kSuggestionsBlacklistURLPrefix),
116 weak_ptr_factory_(this) {} 116 weak_ptr_factory_(this) {}
117 117
118 SuggestionsService::~SuggestionsService() {} 118 SuggestionsService::~SuggestionsService() {}
119 119
120 // static 120 // static
121 bool SuggestionsService::IsControlGroup() { 121 bool SuggestionsService::IsControlGroup() {
122 return GetExperimentParam(kSuggestionsFieldTrialControlParam) == 122 return GetExperimentParam(kSuggestionsFieldTrialControlParam) ==
123 kSuggestionsFieldTrialStateEnabled; 123 kSuggestionsFieldTrialStateEnabled;
(...skipping 23 matching lines...) Expand all
147 } 147 }
148 148
149 void SuggestionsService::GetPageThumbnail( 149 void SuggestionsService::GetPageThumbnail(
150 const GURL& url, 150 const GURL& url,
151 base::Callback<void(const GURL&, const SkBitmap*)> callback) { 151 base::Callback<void(const GURL&, const SkBitmap*)> callback) {
152 thumbnail_manager_->GetImageForURL(url, callback); 152 thumbnail_manager_->GetImageForURL(url, callback);
153 } 153 }
154 154
155 void SuggestionsService::BlacklistURL( 155 void SuggestionsService::BlacklistURL(
156 const GURL& candidate_url, 156 const GURL& candidate_url,
157 const SuggestionsService::ResponseCallback& callback) { 157 const SuggestionsService::ResponseCallback& callback,
158 const base::Closure& fail_callback) {
158 DCHECK(thread_checker_.CalledOnValidThread()); 159 DCHECK(thread_checker_.CalledOnValidThread());
160
161 if (!blacklist_store_->BlacklistUrl(candidate_url)) {
162 fail_callback.Run();
163 return;
164 }
165
159 waiting_requestors_.push_back(callback); 166 waiting_requestors_.push_back(callback);
167 ServeFromCache();
168 if (!pending_request_.get()) {
Mathieu 2014/12/04 18:53:33 put comment above here, mentioning why we only sch
manzagop (departed) 2014/12/05 15:13:22 Done.
169 ScheduleBlacklistUpload();
170 }
171 }
160 172
161 // Blacklist locally for immediate effect and serve the requestors. 173 void SuggestionsService::UndoBlacklistURL(
162 blacklist_store_->BlacklistUrl(candidate_url); 174 const GURL& url,
163 ServeFromCache(); 175 const SuggestionsService::ResponseCallback& callback,
164 176 const base::Closure& fail_callback) {
165 // Send blacklisting request. Even if this request ends up not being sent 177 DCHECK(thread_checker_.CalledOnValidThread());
166 // because of an ongoing request, a blacklist request is later scheduled. 178 TimeDelta time_delta;
167 // TODO(mathp): Currently, this will not send a request if there is already 179 if (blacklist_store_->GetTimeUntilReadyForUpload(url, &time_delta) &&
168 // a request in flight (for suggestions or blacklist). Should we prioritize 180 time_delta > TimeDelta::FromSeconds(0) &&
169 // blacklist requests since they actually carry a payload? 181 blacklist_store_->RemoveUrl(url)) {
170 IssueRequestIfNoneOngoing( 182 // The URL was in the blacklist without being ready for upload and was
Mathieu 2014/12/04 18:53:32 // The URL was not yet uploaded to the server and
manzagop (departed) 2014/12/05 15:13:22 Changed it, though not quite to what you propose b
171 BuildBlacklistRequestURL(blacklist_url_prefix_, candidate_url)); 183 // successfully removed.
184 waiting_requestors_.push_back(callback);
185 ServeFromCache();
186 return;
187 }
188 fail_callback.Run();
172 } 189 }
173 190
174 // static 191 // static
175 bool SuggestionsService::GetBlacklistedUrl(const net::URLFetcher& request, 192 bool SuggestionsService::GetBlacklistedUrl(const net::URLFetcher& request,
176 GURL* url) { 193 GURL* url) {
177 bool is_blacklist_request = StartsWithASCII(request.GetOriginalURL().spec(), 194 bool is_blacklist_request = StartsWithASCII(request.GetOriginalURL().spec(),
178 kSuggestionsBlacklistURLPrefix, 195 kSuggestionsBlacklistURLPrefix,
179 true); 196 true);
180 if (!is_blacklist_request) return false; 197 if (!is_blacklist_request) return false;
181 198
(...skipping 30 matching lines...) Expand all
212 } 229 }
213 } 230 }
214 231
215 void SuggestionsService::IssueRequestIfNoneOngoing(const GURL& url) { 232 void SuggestionsService::IssueRequestIfNoneOngoing(const GURL& url) {
216 // If there is an ongoing request, let it complete. 233 // If there is an ongoing request, let it complete.
217 if (pending_request_.get()) { 234 if (pending_request_.get()) {
218 return; 235 return;
219 } 236 }
220 pending_request_.reset(CreateSuggestionsRequest(url)); 237 pending_request_.reset(CreateSuggestionsRequest(url));
221 pending_request_->Start(); 238 pending_request_->Start();
222 last_request_started_time_ = base::TimeTicks::Now(); 239 last_request_started_time_ = TimeTicks::Now();
223 } 240 }
224 241
225 net::URLFetcher* SuggestionsService::CreateSuggestionsRequest(const GURL& url) { 242 net::URLFetcher* SuggestionsService::CreateSuggestionsRequest(const GURL& url) {
226 net::URLFetcher* request = 243 net::URLFetcher* request =
227 net::URLFetcher::Create(0, url, net::URLFetcher::GET, this); 244 net::URLFetcher::Create(0, url, net::URLFetcher::GET, this);
228 request->SetLoadFlags(net::LOAD_DISABLE_CACHE); 245 request->SetLoadFlags(net::LOAD_DISABLE_CACHE);
229 request->SetRequestContext(url_request_context_); 246 request->SetRequestContext(url_request_context_);
230 // Add Chrome experiment state to the request headers. 247 // Add Chrome experiment state to the request headers.
231 net::HttpRequestHeaders headers; 248 net::HttpRequestHeaders headers;
232 variations::VariationsHttpHeaderProvider::GetInstance()->AppendHeaders( 249 variations::VariationsHttpHeaderProvider::GetInstance()->AppendHeaders(
(...skipping 11 matching lines...) Expand all
244 261
245 const net::URLRequestStatus& request_status = request->GetStatus(); 262 const net::URLRequestStatus& request_status = request->GetStatus();
246 if (request_status.status() != net::URLRequestStatus::SUCCESS) { 263 if (request_status.status() != net::URLRequestStatus::SUCCESS) {
247 // This represents network errors (i.e. the server did not provide a 264 // This represents network errors (i.e. the server did not provide a
248 // response). 265 // response).
249 UMA_HISTOGRAM_SPARSE_SLOWLY("Suggestions.FailedRequestErrorCode", 266 UMA_HISTOGRAM_SPARSE_SLOWLY("Suggestions.FailedRequestErrorCode",
250 -request_status.error()); 267 -request_status.error());
251 DVLOG(1) << "Suggestions server request failed with error: " 268 DVLOG(1) << "Suggestions server request failed with error: "
252 << request_status.error() << ": " 269 << request_status.error() << ": "
253 << net::ErrorToString(request_status.error()); 270 << net::ErrorToString(request_status.error());
254 ScheduleBlacklistUpload(false); 271 UpdateBlacklistDelay(false);
272 ScheduleBlacklistUpload();
255 return; 273 return;
256 } 274 }
257 275
258 const int response_code = request->GetResponseCode(); 276 const int response_code = request->GetResponseCode();
259 UMA_HISTOGRAM_SPARSE_SLOWLY("Suggestions.FetchResponseCode", response_code); 277 UMA_HISTOGRAM_SPARSE_SLOWLY("Suggestions.FetchResponseCode", response_code);
260 if (response_code != net::HTTP_OK) { 278 if (response_code != net::HTTP_OK) {
261 // A non-200 response code means that server has no (longer) suggestions for 279 // A non-200 response code means that server has no (longer) suggestions for
262 // this user. Aggressively clear the cache. 280 // this user. Aggressively clear the cache.
263 suggestions_store_->ClearSuggestions(); 281 suggestions_store_->ClearSuggestions();
264 ScheduleBlacklistUpload(false); 282 UpdateBlacklistDelay(false);
283 ScheduleBlacklistUpload();
265 return; 284 return;
266 } 285 }
267 286
268 const base::TimeDelta latency = 287 const TimeDelta latency = TimeTicks::Now() - last_request_started_time_;
269 base::TimeTicks::Now() - last_request_started_time_;
270 UMA_HISTOGRAM_MEDIUM_TIMES("Suggestions.FetchSuccessLatency", latency); 288 UMA_HISTOGRAM_MEDIUM_TIMES("Suggestions.FetchSuccessLatency", latency);
271 289
272 // Handle a successful blacklisting. 290 // Handle a successful blacklisting.
273 GURL blacklisted_url; 291 GURL blacklisted_url;
274 if (GetBlacklistedUrl(*source, &blacklisted_url)) { 292 if (GetBlacklistedUrl(*source, &blacklisted_url)) {
275 blacklist_store_->RemoveUrl(blacklisted_url); 293 blacklist_store_->RemoveUrl(blacklisted_url);
276 } 294 }
277 295
278 std::string suggestions_data; 296 std::string suggestions_data;
279 bool success = request->GetResponseAsString(&suggestions_data); 297 bool success = request->GetResponseAsString(&suggestions_data);
280 DCHECK(success); 298 DCHECK(success);
281 299
282 // Parse the received suggestions and update the cache, or take proper action 300 // Parse the received suggestions and update the cache, or take proper action
283 // in the case of invalid response. 301 // in the case of invalid response.
284 SuggestionsProfile suggestions; 302 SuggestionsProfile suggestions;
285 if (suggestions_data.empty()) { 303 if (suggestions_data.empty()) {
286 LogResponseState(RESPONSE_EMPTY); 304 LogResponseState(RESPONSE_EMPTY);
287 suggestions_store_->ClearSuggestions(); 305 suggestions_store_->ClearSuggestions();
288 } else if (suggestions.ParseFromString(suggestions_data)) { 306 } else if (suggestions.ParseFromString(suggestions_data)) {
289 LogResponseState(RESPONSE_VALID); 307 LogResponseState(RESPONSE_VALID);
290 int64 now_usec = (base::Time::NowFromSystemTime() - base::Time::UnixEpoch()) 308 int64 now_usec = (base::Time::NowFromSystemTime() - base::Time::UnixEpoch())
291 .ToInternalValue(); 309 .ToInternalValue();
292 SetDefaultExpiryTimestamp(&suggestions, now_usec + kDefaultExpiryUsec); 310 SetDefaultExpiryTimestamp(&suggestions, now_usec + kDefaultExpiryUsec);
293 suggestions_store_->StoreSuggestions(suggestions); 311 suggestions_store_->StoreSuggestions(suggestions);
294 } else { 312 } else {
295 LogResponseState(RESPONSE_INVALID); 313 LogResponseState(RESPONSE_INVALID);
296 } 314 }
297 315
298 ScheduleBlacklistUpload(true); 316 UpdateBlacklistDelay(true);
317 ScheduleBlacklistUpload();
299 } 318 }
300 319
301 void SuggestionsService::Shutdown() { 320 void SuggestionsService::Shutdown() {
302 // Cancel pending request, then serve existing requestors from cache. 321 // Cancel pending request, then serve existing requestors from cache.
303 pending_request_.reset(NULL); 322 pending_request_.reset(NULL);
304 ServeFromCache(); 323 ServeFromCache();
305 } 324 }
306 325
307 void SuggestionsService::ServeFromCache() { 326 void SuggestionsService::ServeFromCache() {
308 SuggestionsProfile suggestions; 327 SuggestionsProfile suggestions;
309 // In case of empty cache or error, |suggestions| stays empty. 328 // In case of empty cache or error, |suggestions| stays empty.
310 suggestions_store_->LoadSuggestions(&suggestions); 329 suggestions_store_->LoadSuggestions(&suggestions);
311 thumbnail_manager_->Initialize(suggestions); 330 thumbnail_manager_->Initialize(suggestions);
312 FilterAndServe(&suggestions); 331 FilterAndServe(&suggestions);
313 } 332 }
314 333
315 void SuggestionsService::FilterAndServe(SuggestionsProfile* suggestions) { 334 void SuggestionsService::FilterAndServe(SuggestionsProfile* suggestions) {
316 blacklist_store_->FilterSuggestions(suggestions); 335 blacklist_store_->FilterSuggestions(suggestions);
317 DispatchRequestsAndClear(*suggestions, &waiting_requestors_); 336 DispatchRequestsAndClear(*suggestions, &waiting_requestors_);
318 } 337 }
319 338
320 void SuggestionsService::ScheduleBlacklistUpload(bool last_request_successful) { 339 void SuggestionsService::ScheduleBlacklistUpload() {
321 DCHECK(thread_checker_.CalledOnValidThread()); 340 DCHECK(thread_checker_.CalledOnValidThread());
322 341 TimeDelta time_delta;
323 UpdateBlacklistDelay(last_request_successful); 342 if (blacklist_store_->GetTimeUntilReadyForUpload(&time_delta)) {
324 343 // Blacklist cache is not empty: schedule.
325 // Schedule a blacklist upload task.
326 GURL blacklist_url;
327 if (blacklist_store_->GetFirstUrlFromBlacklist(&blacklist_url)) {
328 base::Closure blacklist_cb = 344 base::Closure blacklist_cb =
329 base::Bind(&SuggestionsService::UploadOneFromBlacklist, 345 base::Bind(&SuggestionsService::UploadOneFromBlacklist,
330 weak_ptr_factory_.GetWeakPtr()); 346 weak_ptr_factory_.GetWeakPtr());
331 base::MessageLoopProxy::current()->PostDelayedTask( 347 base::MessageLoopProxy::current()->PostDelayedTask(
332 FROM_HERE, blacklist_cb, 348 FROM_HERE, blacklist_cb, time_delta + scheduling_delay_);
333 base::TimeDelta::FromSeconds(blacklist_delay_sec_));
334 } 349 }
335 } 350 }
336 351
337 void SuggestionsService::UploadOneFromBlacklist() { 352 void SuggestionsService::UploadOneFromBlacklist() {
338 DCHECK(thread_checker_.CalledOnValidThread()); 353 DCHECK(thread_checker_.CalledOnValidThread());
339 354
340 GURL blacklist_url; 355 GURL blacklist_url;
341 if (!blacklist_store_->GetFirstUrlFromBlacklist(&blacklist_url)) 356 if (blacklist_store_->GetCandidateForUpload(&blacklist_url)) {
342 return; // Local blacklist is empty. 357 // Issue a blacklisting request. Even if this request ends up not being sent
358 // because of an ongoing request, a blacklist request is later scheduled.
359 IssueRequestIfNoneOngoing(
360 BuildBlacklistRequestURL(blacklist_url_prefix_, blacklist_url));
361 return;
362 }
343 363
344 // Send blacklisting request. Even if this request ends up not being sent 364 // Even though there's no candidate for upload, the blacklist might not be
345 // because of an ongoing request, a blacklist request is later scheduled. 365 // empty.
346 IssueRequestIfNoneOngoing( 366 ScheduleBlacklistUpload();
347 BuildBlacklistRequestURL(blacklist_url_prefix_, blacklist_url));
348 } 367 }
349 368
350 void SuggestionsService::UpdateBlacklistDelay(bool last_request_successful) { 369 void SuggestionsService::UpdateBlacklistDelay(bool last_request_successful) {
351 DCHECK(thread_checker_.CalledOnValidThread()); 370 DCHECK(thread_checker_.CalledOnValidThread());
352 371
353 if (last_request_successful) { 372 if (last_request_successful) {
354 blacklist_delay_sec_ = kBlacklistDefaultDelaySec; 373 scheduling_delay_ = kDefaultSchedulingDelay;
355 } else { 374 } else {
356 int candidate_delay = blacklist_delay_sec_ * kBlacklistBackoffMultiplier; 375 TimeDelta candidate_delay =
357 if (candidate_delay < kBlacklistMaxDelaySec) 376 scheduling_delay_ * kSchedulingBackoffMultiplier;
358 blacklist_delay_sec_ = candidate_delay; 377 if (candidate_delay < kSchedulingMaxDelaySec)
378 scheduling_delay_ = candidate_delay;
359 } 379 }
360 } 380 }
361 381
362 } // namespace suggestions 382 } // namespace suggestions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698