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

Side by Side Diff: components/offline_pages/background/request_coordinator.cc

Issue 2489443002: Move all components/offline_pages/ files into component/offline_pages/core (Closed)
Patch Set: rebase Created 4 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
(Empty)
1 // Copyright 2016 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 "components/offline_pages/background/request_coordinator.h"
6
7 #include <limits>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/logging.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/rand_util.h"
15 #include "base/sys_info.h"
16 #include "base/time/time.h"
17 #include "components/offline_pages/background/offliner_factory.h"
18 #include "components/offline_pages/background/offliner_policy.h"
19 #include "components/offline_pages/background/save_page_request.h"
20 #include "components/offline_pages/client_policy_controller.h"
21 #include "components/offline_pages/offline_page_feature.h"
22 #include "components/offline_pages/offline_page_item.h"
23 #include "components/offline_pages/offline_page_model.h"
24
25 namespace offline_pages {
26
27 namespace {
28 const bool kUserRequest = true;
29 const bool kStartOfProcessing = true;
30 const int kMinDurationSeconds = 1;
31 const int kMaxDurationSeconds = 7 * 24 * 60 * 60; // 7 days
32 const int kDurationBuckets = 50;
33 const int kDisabledTaskRecheckSeconds = 5;
34
35 // TODO(dougarnett): Move to util location and share with model impl.
36 std::string AddHistogramSuffix(const ClientId& client_id,
37 const char* histogram_name) {
38 if (client_id.name_space.empty()) {
39 NOTREACHED();
40 return histogram_name;
41 }
42 std::string adjusted_histogram_name(histogram_name);
43 adjusted_histogram_name += "." + client_id.name_space;
44 return adjusted_histogram_name;
45 }
46
47 // Records the final request status UMA for an offlining request. This should
48 // only be called once per Offliner::LoadAndSave request.
49 void RecordOfflinerResultUMA(const ClientId& client_id,
50 const base::Time& request_creation_time,
51 Offliner::RequestStatus request_status) {
52 // The histogram below is an expansion of the UMA_HISTOGRAM_ENUMERATION
53 // macro adapted to allow for a dynamically suffixed histogram name.
54 // Note: The factory creates and owns the histogram.
55 base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
56 AddHistogramSuffix(client_id,
57 "OfflinePages.Background.OfflinerRequestStatus"),
58 1, static_cast<int>(Offliner::RequestStatus::STATUS_COUNT),
59 static_cast<int>(Offliner::RequestStatus::STATUS_COUNT) + 1,
60 base::HistogramBase::kUmaTargetedHistogramFlag);
61 histogram->Add(static_cast<int>(request_status));
62
63 // For successful requests also record time from request to save.
64 if (request_status == Offliner::RequestStatus::SAVED) {
65 // Using regular histogram (with dynamic suffix) rather than time-oriented
66 // one to record samples in seconds rather than milliseconds.
67 base::HistogramBase* histogram = base::Histogram::FactoryGet(
68 AddHistogramSuffix(client_id, "OfflinePages.Background.TimeToSaved"),
69 kMinDurationSeconds, kMaxDurationSeconds, kDurationBuckets,
70 base::HistogramBase::kUmaTargetedHistogramFlag);
71 base::TimeDelta duration = base::Time::Now() - request_creation_time;
72 histogram->Add(duration.InSeconds());
73 }
74 }
75
76 void RecordStartTimeUMA(const SavePageRequest& request) {
77 std::string histogram_name("OfflinePages.Background.TimeToStart");
78 if (base::SysInfo::IsLowEndDevice()) {
79 histogram_name += ".Svelte";
80 }
81
82 // The histogram below is an expansion of the UMA_HISTOGRAM_CUSTOM_TIMES
83 // macro adapted to allow for a dynamically suffixed histogram name.
84 // Note: The factory creates and owns the histogram.
85 base::HistogramBase* histogram = base::Histogram::FactoryTimeGet(
86 AddHistogramSuffix(request.client_id(), histogram_name.c_str()),
87 base::TimeDelta::FromMilliseconds(100), base::TimeDelta::FromDays(7), 50,
88 base::HistogramBase::kUmaTargetedHistogramFlag);
89 base::TimeDelta duration = base::Time::Now() - request.creation_time();
90 histogram->AddTime(duration);
91 }
92
93 void RecordCancelTimeUMA(const SavePageRequest& canceled_request) {
94 // Using regular histogram (with dynamic suffix) rather than time-oriented
95 // one to record samples in seconds rather than milliseconds.
96 base::HistogramBase* histogram = base::Histogram::FactoryGet(
97 AddHistogramSuffix(canceled_request.client_id(),
98 "OfflinePages.Background.TimeToCanceled"),
99 kMinDurationSeconds, kMaxDurationSeconds, kDurationBuckets,
100 base::HistogramBase::kUmaTargetedHistogramFlag);
101 base::TimeDelta duration =
102 base::Time::Now() - canceled_request.creation_time();
103 histogram->Add(duration.InSeconds());
104 }
105
106 // Records the number of started attempts for completed requests (whether
107 // successful or not).
108 void RecordAttemptCount(const SavePageRequest& request,
109 RequestNotifier::BackgroundSavePageResult status) {
110 if (status == RequestNotifier::BackgroundSavePageResult::SUCCESS) {
111 // TODO(dougarnett): Also record UMA for completed attempts here.
112 UMA_HISTOGRAM_CUSTOM_COUNTS(
113 "OfflinePages.Background.RequestSuccess.StartedAttemptCount",
114 request.started_attempt_count(), 1, 10, 11);
115 } else {
116 UMA_HISTOGRAM_CUSTOM_COUNTS(
117 "OfflinePages.Background.RequestFailure.StartedAttemptCount",
118 request.started_attempt_count(), 1, 10, 11);
119 }
120 }
121
122 // Record the network quality at request creation time per namespace.
123 void RecordSavePageLaterNetworkQuality(
124 const ClientId& client_id,
125 const net::EffectiveConnectionType effective_connection) {
126 // The histogram below is an expansion of the UMA_HISTOGRAM_ENUMERATION
127 // macro adapted to allow for a dynamically suffixed histogram name.
128 // Note: The factory creates and owns the histogram.
129 base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
130 AddHistogramSuffix(
131 client_id,
132 "OfflinePages.Background.EffectiveConnectionType.SavePageLater"),
133 1, net::EFFECTIVE_CONNECTION_TYPE_LAST - 1,
134 net::EFFECTIVE_CONNECTION_TYPE_LAST,
135 base::HistogramBase::kUmaTargetedHistogramFlag);
136 histogram->Add(effective_connection);
137 }
138
139 // This should use the same algorithm as we use for OfflinePageItem, so the IDs
140 // are similar.
141 int64_t GenerateOfflineId() {
142 return base::RandGenerator(std::numeric_limits<int64_t>::max()) + 1;
143 }
144
145 // In case we start processing from SavePageLater, we need a callback, but there
146 // is nothing for it to do.
147 void EmptySchedulerCallback(bool started) {}
148
149 // Returns whether |result| is a successful result for a single request.
150 bool IsSingleSuccessResult(const UpdateRequestsResult* result) {
151 return result->store_state == StoreState::LOADED &&
152 result->item_statuses.size() == 1 &&
153 result->item_statuses.at(0).second == ItemActionStatus::SUCCESS;
154 }
155
156 } // namespace
157
158 RequestCoordinator::RequestCoordinator(
159 std::unique_ptr<OfflinerPolicy> policy,
160 std::unique_ptr<OfflinerFactory> factory,
161 std::unique_ptr<RequestQueue> queue,
162 std::unique_ptr<Scheduler> scheduler,
163 net::NetworkQualityEstimator::NetworkQualityProvider*
164 network_quality_estimator)
165 : is_low_end_device_(base::SysInfo::IsLowEndDevice()),
166 is_busy_(false),
167 is_starting_(false),
168 processing_state_(ProcessingWindowState::STOPPED),
169 use_test_connection_type_(false),
170 test_connection_type_(),
171 offliner_(nullptr),
172 policy_(std::move(policy)),
173 factory_(std::move(factory)),
174 queue_(std::move(queue)),
175 scheduler_(std::move(scheduler)),
176 policy_controller_(new ClientPolicyController()),
177 network_quality_estimator_(network_quality_estimator),
178 active_request_(nullptr),
179 last_offlining_status_(Offliner::RequestStatus::UNKNOWN),
180 scheduler_callback_(base::Bind(&EmptySchedulerCallback)),
181 immediate_schedule_callback_(base::Bind(&EmptySchedulerCallback)),
182 weak_ptr_factory_(this) {
183 DCHECK(policy_ != nullptr);
184 std::unique_ptr<CleanupTaskFactory> cleanup_factory(
185 new CleanupTaskFactory(policy_.get(), this, &event_logger_));
186 queue_->SetCleanupFactory(std::move(cleanup_factory));
187 // Do a cleanup at startup time.
188 queue_->CleanupRequestQueue();
189 }
190
191 RequestCoordinator::~RequestCoordinator() {}
192
193 int64_t RequestCoordinator::SavePageLater(const GURL& url,
194 const ClientId& client_id,
195 bool user_requested,
196 RequestAvailability availability) {
197 DVLOG(2) << "URL is " << url << " " << __func__;
198
199 if (!OfflinePageModel::CanSaveURL(url)) {
200 DVLOG(1) << "Not able to save page for requested url: " << url;
201 return 0L;
202 }
203
204 int64_t id = GenerateOfflineId();
205
206 // Build a SavePageRequest.
207 offline_pages::SavePageRequest request(id, url, client_id, base::Time::Now(),
208 user_requested);
209
210 // If the download manager is not done with the request, put it on the
211 // disabled list.
212 if (availability == RequestAvailability::DISABLED_FOR_OFFLINER)
213 disabled_requests_.insert(id);
214
215 // Put the request on the request queue.
216 queue_->AddRequest(request,
217 base::Bind(&RequestCoordinator::AddRequestResultCallback,
218 weak_ptr_factory_.GetWeakPtr(), availability));
219
220 // Record the network quality when this request is made.
221 if (network_quality_estimator_) {
222 RecordSavePageLaterNetworkQuality(
223 client_id, network_quality_estimator_->GetEffectiveConnectionType());
224 }
225
226 return id;
227 }
228 void RequestCoordinator::GetAllRequests(const GetRequestsCallback& callback) {
229 // Get all matching requests from the request queue, send them to our
230 // callback. We bind the namespace and callback to the front of the callback
231 // param set.
232 queue_->GetRequests(base::Bind(&RequestCoordinator::GetQueuedRequestsCallback,
233 weak_ptr_factory_.GetWeakPtr(), callback));
234 }
235
236 void RequestCoordinator::GetQueuedRequestsCallback(
237 const GetRequestsCallback& callback,
238 GetRequestsResult result,
239 std::vector<std::unique_ptr<SavePageRequest>> requests) {
240 callback.Run(std::move(requests));
241 }
242
243 void RequestCoordinator::StopPrerendering(Offliner::RequestStatus stop_status) {
244 if (offliner_ && is_busy_) {
245 DCHECK(active_request_.get());
246 offliner_->Cancel();
247
248 if (stop_status == Offliner::RequestStatus::REQUEST_COORDINATOR_TIMED_OUT) {
249 // Consider watchdog timeout a completed attempt.
250 SavePageRequest request(*active_request_.get());
251 UpdateRequestForCompletedAttempt(request,
252 Offliner::REQUEST_COORDINATOR_TIMED_OUT);
253 } else {
254 // Otherwise consider this stop an aborted attempt.
255 UpdateRequestForAbortedAttempt(*active_request_.get());
256 }
257 }
258
259 // Stopping offliner means it will not call callback so set last status.
260 last_offlining_status_ = stop_status;
261
262 if (active_request_) {
263 event_logger_.RecordOfflinerResult(active_request_->client_id().name_space,
264 last_offlining_status_,
265 active_request_->request_id());
266 RecordOfflinerResultUMA(active_request_->client_id(),
267 active_request_->creation_time(),
268 last_offlining_status_);
269 is_busy_ = false;
270 active_request_.reset();
271 }
272 }
273
274 void RequestCoordinator::GetRequestsForSchedulingCallback(
275 GetRequestsResult result,
276 std::vector<std::unique_ptr<SavePageRequest>> requests) {
277 bool user_requested = false;
278
279 // Examine all requests, if we find a user requested one, we will use the less
280 // restrictive conditions for user_requested requests. Otherwise we will use
281 // the more restrictive non-user-requested conditions.
282 for (const auto& request : requests) {
283 if (request->user_requested()) {
284 user_requested = true;
285 break;
286 }
287 }
288
289 // In the get callback, determine the least restrictive, and call
290 // GetTriggerConditions based on that.
291 scheduler_->Schedule(GetTriggerConditions(user_requested));
292 }
293
294 bool RequestCoordinator::CancelActiveRequestIfItMatches(
295 const std::vector<int64_t>& request_ids) {
296 // If we have a request in progress and need to cancel it, call the
297 // pre-renderer to cancel. TODO Make sure we remove any page created by the
298 // prerenderer if it doesn't get the cancel in time.
299 if (active_request_ != nullptr) {
300 if (request_ids.end() != std::find(request_ids.begin(), request_ids.end(),
301 active_request_->request_id())) {
302 StopPrerendering(Offliner::RequestStatus::REQUEST_COORDINATOR_CANCELED);
303 active_request_.reset(nullptr);
304 return true;
305 }
306 }
307
308 return false;
309 }
310
311 void RequestCoordinator::UpdateRequestForAbortedAttempt(
312 const SavePageRequest& request) {
313 if (request.started_attempt_count() >= policy_->GetMaxStartedTries()) {
314 const RequestNotifier::BackgroundSavePageResult result(
315 RequestNotifier::BackgroundSavePageResult::START_COUNT_EXCEEDED);
316 event_logger_.RecordDroppedSavePageRequest(request.client_id().name_space,
317 result, request.request_id());
318 RemoveAttemptedRequest(request, result);
319 } else {
320 MarkAttemptAborted(request.request_id(), request.client_id().name_space);
321 }
322 }
323
324 void RequestCoordinator::RemoveAttemptedRequest(
325 const SavePageRequest& request,
326 RequestNotifier::BackgroundSavePageResult result) {
327 std::vector<int64_t> remove_requests;
328 remove_requests.push_back(request.request_id());
329 queue_->RemoveRequests(remove_requests,
330 base::Bind(&RequestCoordinator::HandleRemovedRequests,
331 weak_ptr_factory_.GetWeakPtr(), result));
332 RecordAttemptCount(request, result);
333 }
334
335 void RequestCoordinator::MarkAttemptAborted(int64_t request_id,
336 const std::string& name_space) {
337 queue_->MarkAttemptAborted(
338 request_id,
339 base::Bind(&RequestCoordinator::MarkAttemptDone,
340 weak_ptr_factory_.GetWeakPtr(), request_id, name_space));
341 }
342
343 void RequestCoordinator::MarkAttemptDone(
344 int64_t request_id,
345 const std::string& name_space,
346 std::unique_ptr<UpdateRequestsResult> result) {
347 // If the request succeeded, notify observer. If it failed, we can't really
348 // do much, so just log it.
349 if (IsSingleSuccessResult(result.get())) {
350 NotifyChanged(result->updated_items.at(0));
351 } else {
352 DVLOG(1) << "Failed to mark attempt: " << request_id;
353 UpdateRequestResult request_result =
354 result->store_state != StoreState::LOADED
355 ? UpdateRequestResult::STORE_FAILURE
356 : UpdateRequestResult::REQUEST_DOES_NOT_EXIST;
357 event_logger_.RecordUpdateRequestFailed(name_space, request_result);
358 }
359 }
360
361 void RequestCoordinator::RemoveRequests(
362 const std::vector<int64_t>& request_ids,
363 const RemoveRequestsCallback& callback) {
364 bool canceled = CancelActiveRequestIfItMatches(request_ids);
365 queue_->RemoveRequests(
366 request_ids,
367 base::Bind(&RequestCoordinator::HandleRemovedRequestsAndCallback,
368 weak_ptr_factory_.GetWeakPtr(), callback,
369 RequestNotifier::BackgroundSavePageResult::REMOVED));
370
371 // Record the network quality when this request is made.
372 if (network_quality_estimator_) {
373 UMA_HISTOGRAM_ENUMERATION(
374 "OfflinePages.Background.EffectiveConnectionType.RemoveRequests",
375 network_quality_estimator_->GetEffectiveConnectionType(),
376 net::EFFECTIVE_CONNECTION_TYPE_LAST);
377 }
378
379 if (canceled)
380 TryNextRequest(!kStartOfProcessing);
381 }
382
383 void RequestCoordinator::PauseRequests(
384 const std::vector<int64_t>& request_ids) {
385 bool canceled = CancelActiveRequestIfItMatches(request_ids);
386 queue_->ChangeRequestsState(
387 request_ids, SavePageRequest::RequestState::PAUSED,
388 base::Bind(&RequestCoordinator::UpdateMultipleRequestsCallback,
389 weak_ptr_factory_.GetWeakPtr()));
390
391 // Record the network quality when this request is made.
392 if (network_quality_estimator_) {
393 UMA_HISTOGRAM_ENUMERATION(
394 "OfflinePages.Background.EffectiveConnectionType.PauseRequests",
395 network_quality_estimator_->GetEffectiveConnectionType(),
396 net::EFFECTIVE_CONNECTION_TYPE_LAST);
397 }
398
399 if (canceled)
400 TryNextRequest(!kStartOfProcessing);
401 }
402
403 void RequestCoordinator::ResumeRequests(
404 const std::vector<int64_t>& request_ids) {
405 queue_->ChangeRequestsState(
406 request_ids, SavePageRequest::RequestState::AVAILABLE,
407 base::Bind(&RequestCoordinator::UpdateMultipleRequestsCallback,
408 weak_ptr_factory_.GetWeakPtr()));
409
410 // Record the network quality when this request is made.
411 if (network_quality_estimator_) {
412 UMA_HISTOGRAM_ENUMERATION(
413 "OfflinePages.Background.EffectiveConnectionType.ResumeRequests",
414 network_quality_estimator_->GetEffectiveConnectionType(),
415 net::EFFECTIVE_CONNECTION_TYPE_LAST);
416 }
417
418 // Schedule a task, in case there is not one scheduled.
419 ScheduleAsNeeded();
420 }
421
422 net::NetworkChangeNotifier::ConnectionType
423 RequestCoordinator::GetConnectionType() {
424 // If we have a connection type set for test, use that.
425 if (use_test_connection_type_)
426 return test_connection_type_;
427
428 return net::NetworkChangeNotifier::GetConnectionType();
429 }
430
431 void RequestCoordinator::AddRequestResultCallback(
432 RequestAvailability availability,
433 AddRequestResult result,
434 const SavePageRequest& request) {
435 NotifyAdded(request);
436 // Inform the scheduler that we have an outstanding task.
437 scheduler_->Schedule(GetTriggerConditions(kUserRequest));
438
439 if (availability == RequestAvailability::DISABLED_FOR_OFFLINER) {
440 // Mark attempt started (presuming it is disabled for background offliner
441 // because foreground offlining is happening).
442 queue_->MarkAttemptStarted(
443 request.request_id(),
444 base::Bind(&RequestCoordinator::MarkAttemptDone,
445 weak_ptr_factory_.GetWeakPtr(), request.request_id(),
446 request.client_id().name_space));
447 } else if (request.user_requested()) {
448 StartImmediatelyIfConnected();
449 }
450 }
451
452 void RequestCoordinator::UpdateMultipleRequestsCallback(
453 std::unique_ptr<UpdateRequestsResult> result) {
454 for (const auto& request : result->updated_items)
455 NotifyChanged(request);
456
457 bool available_user_request = false;
458 for (const auto& request : result->updated_items) {
459 if (!available_user_request && request.user_requested() &&
460 request.request_state() == SavePageRequest::RequestState::AVAILABLE) {
461 available_user_request = true;
462 }
463 }
464
465 if (available_user_request)
466 StartImmediatelyIfConnected();
467 }
468
469 void RequestCoordinator::HandleRemovedRequestsAndCallback(
470 const RemoveRequestsCallback& callback,
471 RequestNotifier::BackgroundSavePageResult status,
472 std::unique_ptr<UpdateRequestsResult> result) {
473 // TODO(dougarnett): Define status code for user/api cancel and use here
474 // to determine whether to record cancel time UMA.
475 for (const auto& request : result->updated_items)
476 RecordCancelTimeUMA(request);
477 callback.Run(result->item_statuses);
478 HandleRemovedRequests(status, std::move(result));
479 }
480
481 void RequestCoordinator::HandleRemovedRequests(
482 RequestNotifier::BackgroundSavePageResult status,
483 std::unique_ptr<UpdateRequestsResult> result) {
484 for (const auto& request : result->updated_items)
485 NotifyCompleted(request, status);
486 }
487
488 void RequestCoordinator::ScheduleAsNeeded() {
489 // Get all requests from queue (there is no filtering mechanism).
490 queue_->GetRequests(
491 base::Bind(&RequestCoordinator::GetRequestsForSchedulingCallback,
492 weak_ptr_factory_.GetWeakPtr()));
493 }
494
495 void RequestCoordinator::StopProcessing(
496 Offliner::RequestStatus stop_status) {
497 processing_state_ = ProcessingWindowState::STOPPED;
498 StopPrerendering(stop_status);
499
500 // Let the scheduler know we are done processing.
501 scheduler_callback_.Run(true);
502 }
503
504 void RequestCoordinator::HandleWatchdogTimeout() {
505 Offliner::RequestStatus watchdog_status =
506 Offliner::REQUEST_COORDINATOR_TIMED_OUT;
507 StopPrerendering(watchdog_status);
508 TryNextRequest(!kStartOfProcessing);
509 }
510
511 // Returns true if the caller should expect a callback, false otherwise. For
512 // instance, this would return false if a request is already in progress.
513 bool RequestCoordinator::StartProcessing(
514 const DeviceConditions& device_conditions,
515 const base::Callback<void(bool)>& callback) {
516 DVLOG(2) << "Scheduled " << __func__;
517 return StartProcessingInternal(ProcessingWindowState::SCHEDULED_WINDOW,
518 device_conditions, callback);
519 }
520
521 bool RequestCoordinator::StartProcessingInternal(
522 const ProcessingWindowState processing_state,
523 const DeviceConditions& device_conditions,
524 const base::Callback<void(bool)>& callback) {
525 current_conditions_.reset(new DeviceConditions(device_conditions));
526 if (is_starting_ || is_busy_)
527 return false;
528 processing_state_ = processing_state;
529 scheduler_callback_ = callback;
530
531 // Mark the time at which we started processing so we can check our time
532 // budget.
533 operation_start_time_ = base::Time::Now();
534
535 TryNextRequest(kStartOfProcessing);
536
537 return true;
538 }
539
540 void RequestCoordinator::StartImmediatelyIfConnected() {
541 OfflinerImmediateStartStatus immediate_start_status = TryImmediateStart();
542 UMA_HISTOGRAM_ENUMERATION(
543 "OfflinePages.Background.ImmediateStartStatus", immediate_start_status,
544 RequestCoordinator::OfflinerImmediateStartStatus::STATUS_COUNT);
545 }
546
547 RequestCoordinator::OfflinerImmediateStartStatus
548 RequestCoordinator::TryImmediateStart() {
549 DVLOG(2) << "Immediate " << __func__;
550 // Make sure not already busy processing.
551 if (is_busy_)
552 return OfflinerImmediateStartStatus::BUSY;
553
554 // Make sure we are not on svelte device to start immediately.
555 if (is_low_end_device_ &&
556 !offline_pages::IsOfflinePagesSvelteConcurrentLoadingEnabled()) {
557 DVLOG(2) << "low end device, returning";
558 // Let the scheduler know we are done processing and failed due to svelte.
559 immediate_schedule_callback_.Run(false);
560 return OfflinerImmediateStartStatus::NOT_STARTED_ON_SVELTE;
561 }
562
563 if (GetConnectionType() ==
564 net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE) {
565 RequestConnectedEventForStarting();
566 return OfflinerImmediateStartStatus::NO_CONNECTION;
567 } else {
568 // Clear any pending connected event request since we have connection
569 // and will start processing.
570 ClearConnectedEventRequest();
571 }
572
573 // Start processing with manufactured conservative battery conditions
574 // (i.e., assume no battery).
575 // TODO(dougarnett): Obtain actual battery conditions (from Android/Java).
576
577 DeviceConditions device_conditions(false, 0, GetConnectionType());
578 if (StartProcessingInternal(ProcessingWindowState::IMMEDIATE_WINDOW,
579 device_conditions, immediate_schedule_callback_))
580 return OfflinerImmediateStartStatus::STARTED;
581 else
582 return OfflinerImmediateStartStatus::NOT_ACCEPTED;
583 }
584
585 void RequestCoordinator::RequestConnectedEventForStarting() {
586 connection_notifier_.reset(new ConnectionNotifier(
587 base::Bind(&RequestCoordinator::HandleConnectedEventForStarting,
588 weak_ptr_factory_.GetWeakPtr())));
589 }
590
591 void RequestCoordinator::ClearConnectedEventRequest() {
592 connection_notifier_.reset(nullptr);
593 }
594
595 void RequestCoordinator::HandleConnectedEventForStarting() {
596 ClearConnectedEventRequest();
597 StartImmediatelyIfConnected();
598 }
599
600 void RequestCoordinator::TryNextRequest(bool is_start_of_processing) {
601 is_starting_ = true;
602 base::TimeDelta processing_time_budget;
603 if (processing_state_ == ProcessingWindowState::SCHEDULED_WINDOW) {
604 processing_time_budget = base::TimeDelta::FromSeconds(
605 policy_->GetProcessingTimeBudgetWhenBackgroundScheduledInSeconds());
606 } else {
607 DCHECK(processing_state_ == ProcessingWindowState::IMMEDIATE_WINDOW);
608 processing_time_budget = base::TimeDelta::FromSeconds(
609 policy_->GetProcessingTimeBudgetForImmediateLoadInSeconds());
610 }
611
612 // Determine connection type. If just starting processing, the best source is
613 // from the current device conditions (they are fresh and motivated starting
614 // processing whereas NetworkChangeNotifier may lag reality).
615 net::NetworkChangeNotifier::ConnectionType connection_type;
616 if (is_start_of_processing)
617 connection_type = current_conditions_->GetNetConnectionType();
618 else
619 connection_type = GetConnectionType();
620
621 // If there is no network or no time left in the budget, return to the
622 // scheduler. We do not remove the pending scheduler task that was set
623 // up earlier in case we run out of time, so the background scheduler
624 // will return to us at the next opportunity to run background tasks.
625 if (connection_type ==
626 net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE ||
627 (base::Time::Now() - operation_start_time_) > processing_time_budget) {
628 is_starting_ = false;
629
630 // If we were doing immediate processing, try to start it again
631 // when we get connected.
632 if (processing_state_ == ProcessingWindowState::IMMEDIATE_WINDOW)
633 RequestConnectedEventForStarting();
634
635 // Let the scheduler know we are done processing.
636 // TODO: Make sure the scheduler callback is valid before running it.
637 scheduler_callback_.Run(true);
638 DVLOG(2) << " out of time, giving up. " << __func__;
639
640 return;
641 }
642
643 // Ask request queue to make a new PickRequestTask object, then put it on the
644 // task queue.
645 queue_->PickNextRequest(
646 policy_.get(), base::Bind(&RequestCoordinator::RequestPicked,
647 weak_ptr_factory_.GetWeakPtr()),
648 base::Bind(&RequestCoordinator::RequestNotPicked,
649 weak_ptr_factory_.GetWeakPtr()),
650 base::Bind(&RequestCoordinator::RequestCounts,
651 weak_ptr_factory_.GetWeakPtr(), is_start_of_processing),
652 *current_conditions_.get(), disabled_requests_);
653 // TODO(petewil): Verify current_conditions has a good value on all calling
654 // paths. It is really more of a "last known conditions" than "current
655 // conditions". Consider having a call to Java to check the current
656 // conditions.
657 }
658
659 // Called by the request picker when a request has been picked.
660 void RequestCoordinator::RequestPicked(const SavePageRequest& request,
661 bool cleanup_needed) {
662 DVLOG(2) << request.url() << " " << __func__;
663 is_starting_ = false;
664
665 // Make sure we were not stopped while picking.
666 if (processing_state_ != ProcessingWindowState::STOPPED) {
667 // Send the request on to the offliner.
668 SendRequestToOffliner(request);
669 }
670
671 // Schedule a queue cleanup if needed.
672 if (cleanup_needed)
673 queue_->CleanupRequestQueue();
674 }
675
676 void RequestCoordinator::RequestNotPicked(
677 bool non_user_requested_tasks_remaining,
678 bool cleanup_needed) {
679 DVLOG(2) << __func__;
680 is_starting_ = false;
681
682 // Clear the outstanding "safety" task in the scheduler.
683 scheduler_->Unschedule();
684
685 // If disabled tasks remain, post a new safety task for 5 sec from now.
686 if (disabled_requests_.size() > 0) {
687 scheduler_->BackupSchedule(GetTriggerConditions(kUserRequest),
688 kDisabledTaskRecheckSeconds);
689 } else if (non_user_requested_tasks_remaining) {
690 // If we don't have any of those, check for non-user-requested tasks.
691 scheduler_->Schedule(GetTriggerConditions(!kUserRequest));
692 }
693
694 // Schedule a queue cleanup if needed.
695 if (cleanup_needed)
696 queue_->CleanupRequestQueue();
697
698 // Let the scheduler know we are done processing.
699 scheduler_callback_.Run(true);
700 }
701
702 void RequestCoordinator::RequestCounts(bool is_start_of_processing,
703 size_t total_requests,
704 size_t available_requests) {
705 // Only capture request counts for the start of processing (not for
706 // continued processing in the same window).
707 if (!is_start_of_processing)
708 return;
709
710 if (processing_state_ == ProcessingWindowState::SCHEDULED_WINDOW) {
711 if (is_low_end_device_) {
712 UMA_HISTOGRAM_COUNTS_1000(
713 "OfflinePages.Background.ScheduledStart.AvailableRequestCount."
714 "Svelte",
715 available_requests);
716 UMA_HISTOGRAM_COUNTS_1000(
717 "OfflinePages.Background.ScheduledStart.UnavailableRequestCount."
718 "Svelte",
719 total_requests - available_requests);
720 } else {
721 UMA_HISTOGRAM_COUNTS_1000(
722 "OfflinePages.Background.ScheduledStart.AvailableRequestCount",
723 available_requests);
724 UMA_HISTOGRAM_COUNTS_1000(
725 "OfflinePages.Background.ScheduledStart.UnavailableRequestCount",
726 total_requests - available_requests);
727 }
728 } else if (processing_state_ == ProcessingWindowState::IMMEDIATE_WINDOW) {
729 if (is_low_end_device_) {
730 UMA_HISTOGRAM_COUNTS_1000(
731 "OfflinePages.Background.ImmediateStart.AvailableRequestCount."
732 "Svelte",
733 available_requests);
734 UMA_HISTOGRAM_COUNTS_1000(
735 "OfflinePages.Background.ImmediateStart.UnavailableRequestCount."
736 "Svelte",
737 total_requests - available_requests);
738 } else {
739 UMA_HISTOGRAM_COUNTS_1000(
740 "OfflinePages.Background.ImmediateStart.AvailableRequestCount",
741 available_requests);
742 UMA_HISTOGRAM_COUNTS_1000(
743 "OfflinePages.Background.ImmediateStart.UnavailableRequestCount",
744 total_requests - available_requests);
745 }
746 }
747 }
748
749 void RequestCoordinator::SendRequestToOffliner(const SavePageRequest& request) {
750 // Check that offlining didn't get cancelled while performing some async
751 // steps.
752 if (processing_state_ == ProcessingWindowState::STOPPED)
753 return;
754
755 GetOffliner();
756 if (!offliner_) {
757 DVLOG(0) << "Unable to create Offliner. "
758 << "Cannot background offline page.";
759 return;
760 }
761
762 DCHECK(!is_busy_);
763 is_busy_ = true;
764
765 // Record start time if this is first attempt.
766 if (request.started_attempt_count() == 0) {
767 RecordStartTimeUMA(request);
768 }
769
770 // Mark attempt started in the database and start offliner when completed.
771 queue_->MarkAttemptStarted(
772 request.request_id(),
773 base::Bind(&RequestCoordinator::StartOffliner,
774 weak_ptr_factory_.GetWeakPtr(), request.request_id(),
775 request.client_id().name_space));
776 }
777
778 void RequestCoordinator::StartOffliner(
779 int64_t request_id,
780 const std::string& client_namespace,
781 std::unique_ptr<UpdateRequestsResult> update_result) {
782 if (update_result->store_state != StoreState::LOADED ||
783 update_result->item_statuses.size() != 1 ||
784 update_result->item_statuses.at(0).first != request_id ||
785 update_result->item_statuses.at(0).second != ItemActionStatus::SUCCESS) {
786 is_busy_ = false;
787 // TODO(fgorski): what is the best result? Do we create a new status?
788 StopProcessing(Offliner::PRERENDERING_NOT_STARTED);
789 DVLOG(1) << "Failed to mark attempt started: " << request_id;
790 UpdateRequestResult request_result =
791 update_result->store_state != StoreState::LOADED
792 ? UpdateRequestResult::STORE_FAILURE
793 : UpdateRequestResult::REQUEST_DOES_NOT_EXIST;
794 event_logger_.RecordUpdateRequestFailed(client_namespace, request_result);
795 return;
796 }
797
798 // TODO(fgorski): Switch to request_id only, so that this value is not written
799 // back to the store.
800 active_request_.reset(
801 new SavePageRequest(update_result->updated_items.at(0)));
802
803 // Start the load and save process in the offliner (Async).
804 if (offliner_->LoadAndSave(
805 update_result->updated_items.at(0),
806 base::Bind(&RequestCoordinator::OfflinerDoneCallback,
807 weak_ptr_factory_.GetWeakPtr()))) {
808 base::TimeDelta timeout;
809 if (processing_state_ == ProcessingWindowState::SCHEDULED_WINDOW) {
810 timeout = base::TimeDelta::FromSeconds(
811 policy_->GetSinglePageTimeLimitWhenBackgroundScheduledInSeconds());
812 } else {
813 DCHECK(processing_state_ == ProcessingWindowState::IMMEDIATE_WINDOW);
814 timeout = base::TimeDelta::FromSeconds(
815 policy_->GetSinglePageTimeLimitForImmediateLoadInSeconds());
816 }
817
818 // Inform observer of active request.
819 NotifyChanged(*active_request_.get());
820
821 // Start a watchdog timer to catch pre-renders running too long
822 watchdog_timer_.Start(FROM_HERE, timeout, this,
823 &RequestCoordinator::HandleWatchdogTimeout);
824 } else {
825 is_busy_ = false;
826 DVLOG(0) << "Unable to start LoadAndSave";
827 StopProcessing(Offliner::PRERENDERING_NOT_STARTED);
828
829 // We need to undo the MarkAttemptStarted that brought us to this
830 // method since we didn't success in starting after all.
831 MarkAttemptAborted(request_id, client_namespace);
832 }
833 }
834
835 void RequestCoordinator::OfflinerDoneCallback(const SavePageRequest& request,
836 Offliner::RequestStatus status) {
837 DVLOG(2) << "offliner finished, saved: "
838 << (status == Offliner::RequestStatus::SAVED)
839 << ", status: " << static_cast<int>(status) << ", " << __func__;
840 DCHECK_NE(status, Offliner::RequestStatus::UNKNOWN);
841 DCHECK_NE(status, Offliner::RequestStatus::LOADED);
842 event_logger_.RecordOfflinerResult(request.client_id().name_space, status,
843 request.request_id());
844 last_offlining_status_ = status;
845 RecordOfflinerResultUMA(request.client_id(), request.creation_time(),
846 last_offlining_status_);
847 watchdog_timer_.Stop();
848 is_busy_ = false;
849 active_request_.reset(nullptr);
850
851 UpdateRequestForCompletedAttempt(request, status);
852 if (ShouldTryNextRequest(status))
853 TryNextRequest(!kStartOfProcessing);
854 else
855 scheduler_callback_.Run(true);
856 }
857
858 void RequestCoordinator::UpdateRequestForCompletedAttempt(
859 const SavePageRequest& request,
860 Offliner::RequestStatus status) {
861 if (status == Offliner::RequestStatus::FOREGROUND_CANCELED ||
862 status == Offliner::RequestStatus::PRERENDERING_CANCELED) {
863 // Update the request for the canceled attempt.
864 // TODO(dougarnett): See if we can conclusively identify other attempt
865 // aborted cases to treat this way (eg, for Render Process Killed).
866 UpdateRequestForAbortedAttempt(request);
867 } else if (status == Offliner::RequestStatus::SAVED) {
868 // Remove the request from the queue if it succeeded.
869 RemoveAttemptedRequest(request,
870 RequestNotifier::BackgroundSavePageResult::SUCCESS);
871 } else if (status == Offliner::RequestStatus::PRERENDERING_FAILED_NO_RETRY) {
872 RemoveAttemptedRequest(
873 request, RequestNotifier::BackgroundSavePageResult::PRERENDER_FAILURE);
874 } else if (request.completed_attempt_count() + 1 >=
875 policy_->GetMaxCompletedTries()) {
876 // Remove from the request queue if we exceeded max retries. The +1
877 // represents the request that just completed. Since we call
878 // MarkAttemptCompleted within the if branches, the completed_attempt_count
879 // has not yet been updated when we are checking the if condition.
880 const RequestNotifier::BackgroundSavePageResult result(
881 RequestNotifier::BackgroundSavePageResult::RETRY_COUNT_EXCEEDED);
882 event_logger_.RecordDroppedSavePageRequest(request.client_id().name_space,
883 result, request.request_id());
884 RemoveAttemptedRequest(request, result);
885 } else {
886 // If we failed, but are not over the limit, update the request in the
887 // queue.
888 queue_->MarkAttemptCompleted(
889 request.request_id(),
890 base::Bind(&RequestCoordinator::MarkAttemptDone,
891 weak_ptr_factory_.GetWeakPtr(), request.request_id(),
892 request.client_id().name_space));
893 }
894 }
895
896 bool RequestCoordinator::ShouldTryNextRequest(
897 Offliner::RequestStatus previous_request_status) {
898 switch (previous_request_status) {
899 case Offliner::RequestStatus::SAVED:
900 case Offliner::RequestStatus::SAVE_FAILED:
901 case Offliner::RequestStatus::REQUEST_COORDINATOR_CANCELED:
902 case Offliner::RequestStatus::REQUEST_COORDINATOR_TIMED_OUT:
903 case Offliner::RequestStatus::PRERENDERING_FAILED:
904 case Offliner::RequestStatus::PRERENDERING_FAILED_NO_RETRY:
905 return true;
906 case Offliner::RequestStatus::FOREGROUND_CANCELED:
907 case Offliner::RequestStatus::PRERENDERING_CANCELED:
908 case Offliner::RequestStatus::PRERENDERING_FAILED_NO_NEXT:
909 // No further processing in this service window.
910 return false;
911 default:
912 // Make explicit choice about new status codes that actually reach here.
913 // Their default is no further processing in this service window.
914 NOTREACHED();
915 return false;
916 }
917 }
918
919 void RequestCoordinator::EnableForOffliner(int64_t request_id,
920 const ClientId& client_id) {
921 // Since the recent tab helper might call multiple times, ignore subsequent
922 // calls for a particular request_id.
923 if (disabled_requests_.find(request_id) == disabled_requests_.end())
924 return;
925
926 // Clear from disabled list.
927 disabled_requests_.erase(request_id);
928
929 // Mark the request as now in available state.
930 MarkAttemptAborted(request_id, client_id.name_space);
931
932 // If we are not busy, start processing right away.
933 StartImmediatelyIfConnected();
934 }
935
936 void RequestCoordinator::MarkRequestCompleted(int64_t request_id) {
937 // Since the recent tab helper might call multiple times, ignore subsequent
938 // calls for a particular request_id.
939 if (disabled_requests_.find(request_id) == disabled_requests_.end())
940 return;
941
942 // Clear from disabled list.
943 disabled_requests_.erase(request_id);
944
945 // Remove the request, but send out SUCCEEDED instead of removed.
946 // Note: since it had been disabled, it will not have been active in a
947 // background offliner, so it is not appropriate to TryNextRequest here.
948 std::vector<int64_t> request_ids { request_id };
949 queue_->RemoveRequests(
950 request_ids,
951 base::Bind(&RequestCoordinator::HandleRemovedRequests,
952 weak_ptr_factory_.GetWeakPtr(),
953 RequestNotifier::BackgroundSavePageResult::SUCCESS));
954 }
955
956 const Scheduler::TriggerConditions RequestCoordinator::GetTriggerConditions(
957 const bool user_requested) {
958 return Scheduler::TriggerConditions(
959 policy_->PowerRequired(user_requested),
960 policy_->BatteryPercentageRequired(user_requested),
961 policy_->UnmeteredNetworkRequired(user_requested));
962 }
963
964 void RequestCoordinator::AddObserver(Observer* observer) {
965 DCHECK(observer);
966 observers_.AddObserver(observer);
967 }
968
969 void RequestCoordinator::RemoveObserver(Observer* observer) {
970 observers_.RemoveObserver(observer);
971 }
972
973 void RequestCoordinator::NotifyAdded(const SavePageRequest& request) {
974 for (Observer& observer : observers_)
975 observer.OnAdded(request);
976 }
977
978 void RequestCoordinator::NotifyCompleted(
979 const SavePageRequest& request,
980 RequestNotifier::BackgroundSavePageResult status) {
981 for (Observer& observer : observers_)
982 observer.OnCompleted(request, status);
983 }
984
985 void RequestCoordinator::NotifyChanged(const SavePageRequest& request) {
986 for (Observer& observer : observers_)
987 observer.OnChanged(request);
988 }
989
990 void RequestCoordinator::GetOffliner() {
991 if (!offliner_) {
992 offliner_ = factory_->GetOffliner(policy_.get());
993 }
994 }
995
996 ClientPolicyController* RequestCoordinator::GetPolicyController() {
997 return policy_controller_.get();
998 }
999
1000 void RequestCoordinator::Shutdown() {
1001 network_quality_estimator_ = nullptr;
1002 }
1003
1004 } // namespace offline_pages
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698