OLD | NEW |
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 Loading... |
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 Loading... |
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 int kDefaultSchedulingDelaySec = 1; |
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 int kSchedulingMaxDelaySec = 5 * 60; |
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_(TimeDelta::FromSeconds(kDefaultSchedulingDelaySec)), |
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 Loading... |
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 // Blacklist uploads are scheduled on any request completion, so only schedule |
| 169 // an upload if there is no ongoing request. |
| 170 if (!pending_request_.get()) { |
| 171 ScheduleBlacklistUpload(); |
| 172 } |
| 173 } |
160 | 174 |
161 // Blacklist locally for immediate effect and serve the requestors. | 175 void SuggestionsService::UndoBlacklistURL( |
162 blacklist_store_->BlacklistUrl(candidate_url); | 176 const GURL& url, |
163 ServeFromCache(); | 177 const SuggestionsService::ResponseCallback& callback, |
164 | 178 const base::Closure& fail_callback) { |
165 // Send blacklisting request. Even if this request ends up not being sent | 179 DCHECK(thread_checker_.CalledOnValidThread()); |
166 // because of an ongoing request, a blacklist request is later scheduled. | 180 TimeDelta time_delta; |
167 // TODO(mathp): Currently, this will not send a request if there is already | 181 if (blacklist_store_->GetTimeUntilURLReadyForUpload(url, &time_delta) && |
168 // a request in flight (for suggestions or blacklist). Should we prioritize | 182 time_delta > TimeDelta::FromSeconds(0) && |
169 // blacklist requests since they actually carry a payload? | 183 blacklist_store_->RemoveUrl(url)) { |
170 IssueRequestIfNoneOngoing( | 184 // The URL was not yet candidate for upload to the server and could be |
171 BuildBlacklistRequestURL(blacklist_url_prefix_, candidate_url)); | 185 // removed from the blacklist. |
| 186 waiting_requestors_.push_back(callback); |
| 187 ServeFromCache(); |
| 188 return; |
| 189 } |
| 190 fail_callback.Run(); |
172 } | 191 } |
173 | 192 |
174 // static | 193 // static |
175 bool SuggestionsService::GetBlacklistedUrl(const net::URLFetcher& request, | 194 bool SuggestionsService::GetBlacklistedUrl(const net::URLFetcher& request, |
176 GURL* url) { | 195 GURL* url) { |
177 bool is_blacklist_request = StartsWithASCII(request.GetOriginalURL().spec(), | 196 bool is_blacklist_request = StartsWithASCII(request.GetOriginalURL().spec(), |
178 kSuggestionsBlacklistURLPrefix, | 197 kSuggestionsBlacklistURLPrefix, |
179 true); | 198 true); |
180 if (!is_blacklist_request) return false; | 199 if (!is_blacklist_request) return false; |
181 | 200 |
(...skipping 30 matching lines...) Expand all Loading... |
212 } | 231 } |
213 } | 232 } |
214 | 233 |
215 void SuggestionsService::IssueRequestIfNoneOngoing(const GURL& url) { | 234 void SuggestionsService::IssueRequestIfNoneOngoing(const GURL& url) { |
216 // If there is an ongoing request, let it complete. | 235 // If there is an ongoing request, let it complete. |
217 if (pending_request_.get()) { | 236 if (pending_request_.get()) { |
218 return; | 237 return; |
219 } | 238 } |
220 pending_request_.reset(CreateSuggestionsRequest(url)); | 239 pending_request_.reset(CreateSuggestionsRequest(url)); |
221 pending_request_->Start(); | 240 pending_request_->Start(); |
222 last_request_started_time_ = base::TimeTicks::Now(); | 241 last_request_started_time_ = TimeTicks::Now(); |
223 } | 242 } |
224 | 243 |
225 net::URLFetcher* SuggestionsService::CreateSuggestionsRequest(const GURL& url) { | 244 net::URLFetcher* SuggestionsService::CreateSuggestionsRequest(const GURL& url) { |
226 net::URLFetcher* request = | 245 net::URLFetcher* request = |
227 net::URLFetcher::Create(0, url, net::URLFetcher::GET, this); | 246 net::URLFetcher::Create(0, url, net::URLFetcher::GET, this); |
228 request->SetLoadFlags(net::LOAD_DISABLE_CACHE); | 247 request->SetLoadFlags(net::LOAD_DISABLE_CACHE); |
229 request->SetRequestContext(url_request_context_); | 248 request->SetRequestContext(url_request_context_); |
230 // Add Chrome experiment state to the request headers. | 249 // Add Chrome experiment state to the request headers. |
231 net::HttpRequestHeaders headers; | 250 net::HttpRequestHeaders headers; |
232 variations::VariationsHttpHeaderProvider::GetInstance()->AppendHeaders( | 251 variations::VariationsHttpHeaderProvider::GetInstance()->AppendHeaders( |
(...skipping 11 matching lines...) Expand all Loading... |
244 | 263 |
245 const net::URLRequestStatus& request_status = request->GetStatus(); | 264 const net::URLRequestStatus& request_status = request->GetStatus(); |
246 if (request_status.status() != net::URLRequestStatus::SUCCESS) { | 265 if (request_status.status() != net::URLRequestStatus::SUCCESS) { |
247 // This represents network errors (i.e. the server did not provide a | 266 // This represents network errors (i.e. the server did not provide a |
248 // response). | 267 // response). |
249 UMA_HISTOGRAM_SPARSE_SLOWLY("Suggestions.FailedRequestErrorCode", | 268 UMA_HISTOGRAM_SPARSE_SLOWLY("Suggestions.FailedRequestErrorCode", |
250 -request_status.error()); | 269 -request_status.error()); |
251 DVLOG(1) << "Suggestions server request failed with error: " | 270 DVLOG(1) << "Suggestions server request failed with error: " |
252 << request_status.error() << ": " | 271 << request_status.error() << ": " |
253 << net::ErrorToString(request_status.error()); | 272 << net::ErrorToString(request_status.error()); |
254 ScheduleBlacklistUpload(false); | 273 UpdateBlacklistDelay(false); |
| 274 ScheduleBlacklistUpload(); |
255 return; | 275 return; |
256 } | 276 } |
257 | 277 |
258 const int response_code = request->GetResponseCode(); | 278 const int response_code = request->GetResponseCode(); |
259 UMA_HISTOGRAM_SPARSE_SLOWLY("Suggestions.FetchResponseCode", response_code); | 279 UMA_HISTOGRAM_SPARSE_SLOWLY("Suggestions.FetchResponseCode", response_code); |
260 if (response_code != net::HTTP_OK) { | 280 if (response_code != net::HTTP_OK) { |
261 // A non-200 response code means that server has no (longer) suggestions for | 281 // A non-200 response code means that server has no (longer) suggestions for |
262 // this user. Aggressively clear the cache. | 282 // this user. Aggressively clear the cache. |
263 suggestions_store_->ClearSuggestions(); | 283 suggestions_store_->ClearSuggestions(); |
264 ScheduleBlacklistUpload(false); | 284 UpdateBlacklistDelay(false); |
| 285 ScheduleBlacklistUpload(); |
265 return; | 286 return; |
266 } | 287 } |
267 | 288 |
268 const base::TimeDelta latency = | 289 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); | 290 UMA_HISTOGRAM_MEDIUM_TIMES("Suggestions.FetchSuccessLatency", latency); |
271 | 291 |
272 // Handle a successful blacklisting. | 292 // Handle a successful blacklisting. |
273 GURL blacklisted_url; | 293 GURL blacklisted_url; |
274 if (GetBlacklistedUrl(*source, &blacklisted_url)) { | 294 if (GetBlacklistedUrl(*source, &blacklisted_url)) { |
275 blacklist_store_->RemoveUrl(blacklisted_url); | 295 blacklist_store_->RemoveUrl(blacklisted_url); |
276 } | 296 } |
277 | 297 |
278 std::string suggestions_data; | 298 std::string suggestions_data; |
279 bool success = request->GetResponseAsString(&suggestions_data); | 299 bool success = request->GetResponseAsString(&suggestions_data); |
280 DCHECK(success); | 300 DCHECK(success); |
281 | 301 |
282 // Parse the received suggestions and update the cache, or take proper action | 302 // Parse the received suggestions and update the cache, or take proper action |
283 // in the case of invalid response. | 303 // in the case of invalid response. |
284 SuggestionsProfile suggestions; | 304 SuggestionsProfile suggestions; |
285 if (suggestions_data.empty()) { | 305 if (suggestions_data.empty()) { |
286 LogResponseState(RESPONSE_EMPTY); | 306 LogResponseState(RESPONSE_EMPTY); |
287 suggestions_store_->ClearSuggestions(); | 307 suggestions_store_->ClearSuggestions(); |
288 } else if (suggestions.ParseFromString(suggestions_data)) { | 308 } else if (suggestions.ParseFromString(suggestions_data)) { |
289 LogResponseState(RESPONSE_VALID); | 309 LogResponseState(RESPONSE_VALID); |
290 int64 now_usec = (base::Time::NowFromSystemTime() - base::Time::UnixEpoch()) | 310 int64 now_usec = (base::Time::NowFromSystemTime() - base::Time::UnixEpoch()) |
291 .ToInternalValue(); | 311 .ToInternalValue(); |
292 SetDefaultExpiryTimestamp(&suggestions, now_usec + kDefaultExpiryUsec); | 312 SetDefaultExpiryTimestamp(&suggestions, now_usec + kDefaultExpiryUsec); |
293 suggestions_store_->StoreSuggestions(suggestions); | 313 suggestions_store_->StoreSuggestions(suggestions); |
294 } else { | 314 } else { |
295 LogResponseState(RESPONSE_INVALID); | 315 LogResponseState(RESPONSE_INVALID); |
296 } | 316 } |
297 | 317 |
298 ScheduleBlacklistUpload(true); | 318 UpdateBlacklistDelay(true); |
| 319 ScheduleBlacklistUpload(); |
299 } | 320 } |
300 | 321 |
301 void SuggestionsService::Shutdown() { | 322 void SuggestionsService::Shutdown() { |
302 // Cancel pending request, then serve existing requestors from cache. | 323 // Cancel pending request, then serve existing requestors from cache. |
303 pending_request_.reset(NULL); | 324 pending_request_.reset(NULL); |
304 ServeFromCache(); | 325 ServeFromCache(); |
305 } | 326 } |
306 | 327 |
307 void SuggestionsService::ServeFromCache() { | 328 void SuggestionsService::ServeFromCache() { |
308 SuggestionsProfile suggestions; | 329 SuggestionsProfile suggestions; |
309 // In case of empty cache or error, |suggestions| stays empty. | 330 // In case of empty cache or error, |suggestions| stays empty. |
310 suggestions_store_->LoadSuggestions(&suggestions); | 331 suggestions_store_->LoadSuggestions(&suggestions); |
311 thumbnail_manager_->Initialize(suggestions); | 332 thumbnail_manager_->Initialize(suggestions); |
312 FilterAndServe(&suggestions); | 333 FilterAndServe(&suggestions); |
313 } | 334 } |
314 | 335 |
315 void SuggestionsService::FilterAndServe(SuggestionsProfile* suggestions) { | 336 void SuggestionsService::FilterAndServe(SuggestionsProfile* suggestions) { |
316 blacklist_store_->FilterSuggestions(suggestions); | 337 blacklist_store_->FilterSuggestions(suggestions); |
317 DispatchRequestsAndClear(*suggestions, &waiting_requestors_); | 338 DispatchRequestsAndClear(*suggestions, &waiting_requestors_); |
318 } | 339 } |
319 | 340 |
320 void SuggestionsService::ScheduleBlacklistUpload(bool last_request_successful) { | 341 void SuggestionsService::ScheduleBlacklistUpload() { |
321 DCHECK(thread_checker_.CalledOnValidThread()); | 342 DCHECK(thread_checker_.CalledOnValidThread()); |
322 | 343 TimeDelta time_delta; |
323 UpdateBlacklistDelay(last_request_successful); | 344 if (blacklist_store_->GetTimeUntilReadyForUpload(&time_delta)) { |
324 | 345 // 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 = | 346 base::Closure blacklist_cb = |
329 base::Bind(&SuggestionsService::UploadOneFromBlacklist, | 347 base::Bind(&SuggestionsService::UploadOneFromBlacklist, |
330 weak_ptr_factory_.GetWeakPtr()); | 348 weak_ptr_factory_.GetWeakPtr()); |
331 base::MessageLoopProxy::current()->PostDelayedTask( | 349 base::MessageLoopProxy::current()->PostDelayedTask( |
332 FROM_HERE, blacklist_cb, | 350 FROM_HERE, blacklist_cb, time_delta + scheduling_delay_); |
333 base::TimeDelta::FromSeconds(blacklist_delay_sec_)); | |
334 } | 351 } |
335 } | 352 } |
336 | 353 |
337 void SuggestionsService::UploadOneFromBlacklist() { | 354 void SuggestionsService::UploadOneFromBlacklist() { |
338 DCHECK(thread_checker_.CalledOnValidThread()); | 355 DCHECK(thread_checker_.CalledOnValidThread()); |
339 | 356 |
340 GURL blacklist_url; | 357 GURL blacklist_url; |
341 if (!blacklist_store_->GetFirstUrlFromBlacklist(&blacklist_url)) | 358 if (blacklist_store_->GetCandidateForUpload(&blacklist_url)) { |
342 return; // Local blacklist is empty. | 359 // Issue a blacklisting request. Even if this request ends up not being sent |
| 360 // because of an ongoing request, a blacklist request is later scheduled. |
| 361 IssueRequestIfNoneOngoing( |
| 362 BuildBlacklistRequestURL(blacklist_url_prefix_, blacklist_url)); |
| 363 return; |
| 364 } |
343 | 365 |
344 // Send blacklisting request. Even if this request ends up not being sent | 366 // 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. | 367 // empty. |
346 IssueRequestIfNoneOngoing( | 368 ScheduleBlacklistUpload(); |
347 BuildBlacklistRequestURL(blacklist_url_prefix_, blacklist_url)); | |
348 } | 369 } |
349 | 370 |
350 void SuggestionsService::UpdateBlacklistDelay(bool last_request_successful) { | 371 void SuggestionsService::UpdateBlacklistDelay(bool last_request_successful) { |
351 DCHECK(thread_checker_.CalledOnValidThread()); | 372 DCHECK(thread_checker_.CalledOnValidThread()); |
352 | 373 |
353 if (last_request_successful) { | 374 if (last_request_successful) { |
354 blacklist_delay_sec_ = kBlacklistDefaultDelaySec; | 375 scheduling_delay_ = TimeDelta::FromSeconds(kDefaultSchedulingDelaySec); |
355 } else { | 376 } else { |
356 int candidate_delay = blacklist_delay_sec_ * kBlacklistBackoffMultiplier; | 377 TimeDelta candidate_delay = |
357 if (candidate_delay < kBlacklistMaxDelaySec) | 378 scheduling_delay_ * kSchedulingBackoffMultiplier; |
358 blacklist_delay_sec_ = candidate_delay; | 379 if (candidate_delay < TimeDelta::FromSeconds(kSchedulingMaxDelaySec)) |
| 380 scheduling_delay_ = candidate_delay; |
359 } | 381 } |
360 } | 382 } |
361 | 383 |
362 } // namespace suggestions | 384 } // namespace suggestions |
OLD | NEW |