Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 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/offline_pages/background/pick_request_task.h" | 5 #include "components/offline_pages/background/pick_request_task.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/time/time.h" | 9 #include "base/time/time.h" |
| 10 #include "components/offline_pages/background/device_conditions.h" | 10 #include "components/offline_pages/background/device_conditions.h" |
| 11 #include "components/offline_pages/background/offliner_policy.h" | 11 #include "components/offline_pages/background/offliner_policy.h" |
| 12 #include "components/offline_pages/background/request_coordinator_event_logger.h " | 12 #include "components/offline_pages/background/request_coordinator_event_logger.h " |
| 13 #include "components/offline_pages/background/request_notifier.h" | 13 #include "components/offline_pages/background/request_notifier.h" |
| 14 #include "components/offline_pages/background/request_queue_store.h" | 14 #include "components/offline_pages/background/request_queue_store.h" |
| 15 #include "components/offline_pages/background/save_page_request.h" | 15 #include "components/offline_pages/background/save_page_request.h" |
| 16 | 16 |
| 17 namespace { | 17 namespace { |
| 18 template <typename T> | 18 template <typename T> |
| 19 int signum(T t) { | 19 int signum(T t) { |
| 20 return (T(0) < t) - (t < T(0)); | 20 return (T(0) < t) - (t < T(0)); |
| 21 } | 21 } |
| 22 | 22 |
| 23 #define CALL_MEMBER_FUNCTION(object, ptrToMember) ((object)->*(ptrToMember)) | 23 #define CALL_MEMBER_FUNCTION(object, ptrToMember) ((object)->*(ptrToMember)) |
| 24 } // namespace | 24 } // namespace |
| 25 | 25 |
| 26 namespace offline_pages { | 26 namespace offline_pages { |
| 27 | 27 |
| 28 PickRequestTask::PickRequestTask(RequestQueueStore* store, | 28 PickRequestTask::PickRequestTask(RequestQueueStore* store, |
| 29 OfflinerPolicy* policy, | 29 OfflinerPolicy* policy, |
| 30 RequestNotifier* notifier, | |
| 31 RequestCoordinatorEventLogger* event_logger, | |
| 32 RequestPickedCallback picked_callback, | 30 RequestPickedCallback picked_callback, |
| 33 RequestNotPickedCallback not_picked_callback, | 31 RequestNotPickedCallback not_picked_callback, |
| 34 RequestCountCallback request_count_callback, | 32 RequestCountCallback request_count_callback, |
| 35 DeviceConditions& device_conditions, | 33 DeviceConditions& device_conditions, |
| 36 const std::set<int64_t>& disabled_requests) | 34 const std::set<int64_t>& disabled_requests) |
| 37 : store_(store), | 35 : store_(store), |
| 38 policy_(policy), | 36 policy_(policy), |
| 39 notifier_(notifier), | |
| 40 event_logger_(event_logger), | |
| 41 picked_callback_(picked_callback), | 37 picked_callback_(picked_callback), |
| 42 not_picked_callback_(not_picked_callback), | 38 not_picked_callback_(not_picked_callback), |
| 43 request_count_callback_(request_count_callback), | 39 request_count_callback_(request_count_callback), |
| 44 disabled_requests_(disabled_requests), | 40 disabled_requests_(disabled_requests), |
| 45 weak_ptr_factory_(this) { | 41 weak_ptr_factory_(this) { |
| 46 device_conditions_.reset(new DeviceConditions(device_conditions)); | 42 device_conditions_.reset(new DeviceConditions(device_conditions)); |
| 47 } | 43 } |
| 48 | 44 |
| 49 PickRequestTask::~PickRequestTask() {} | 45 PickRequestTask::~PickRequestTask() {} |
| 50 | 46 |
| 51 void PickRequestTask::Run() { | 47 void PickRequestTask::Run() { |
| 52 // Get all the requests from the queue, we will classify them in the callback. | 48 GetRequests(); |
| 53 store_->GetRequests(base::Bind(&PickRequestTask::ChooseAndPrune, | |
| 54 weak_ptr_factory_.GetWeakPtr())); | |
| 55 } | 49 } |
| 56 | 50 |
| 57 void PickRequestTask::ChooseAndPrune( | 51 void PickRequestTask::GetRequests() { |
| 52 // Get all the requests from the queue, we will classify them in the callback. | |
| 53 store_->GetRequests( | |
| 54 base::Bind(&PickRequestTask::Choose, weak_ptr_factory_.GetWeakPtr())); | |
| 55 } | |
| 56 | |
| 57 void PickRequestTask::Choose( | |
| 58 bool success, | 58 bool success, |
| 59 std::vector<std::unique_ptr<SavePageRequest>> requests) { | 59 std::vector<std::unique_ptr<SavePageRequest>> requests) { |
| 60 // If there is nothing to do, return right away. | 60 // If there is nothing to do, return right away. |
| 61 if (requests.empty()) { | 61 if (requests.empty()) { |
| 62 request_count_callback_.Run(requests.size(), 0); | 62 request_count_callback_.Run(requests.size(), 0); |
| 63 not_picked_callback_.Run(false); | 63 not_picked_callback_.Run(false, false); |
|
dougarnett
2016/12/02 17:20:27
...(false /* non user requests */, false /* cleanu
Pete Williamson
2016/12/05 20:39:45
I did this, but with enums, not comments, WDYT?
dougarnett
2016/12/05 21:27:45
The constants are fine. I have noticed the parm co
Pete Williamson
2016/12/06 00:25:38
I've always preferred named constants to comments.
| |
| 64 TaskComplete(); | 64 TaskComplete(); |
| 65 return; | 65 return; |
| 66 } | 66 } |
| 67 | 67 |
| 68 // Get the expired requests to be removed from the queue, and the valid ones | |
| 69 // from which to pick the next request. | |
| 70 std::vector<std::unique_ptr<SavePageRequest>> valid_requests; | |
| 71 std::vector<int64_t> expired_request_ids; | |
| 72 SplitRequests(std::move(requests), &valid_requests, &expired_request_ids); | |
| 73 | |
| 74 // Continue processing by choosing a request. | |
| 75 ChooseRequestAndCallback(std::move(valid_requests)); | |
| 76 | |
| 77 // Continue processing by handling expired requests, if any. | |
| 78 if (expired_request_ids.size() == 0) { | |
| 79 TaskComplete(); | |
| 80 return; | |
| 81 } | |
| 82 | |
| 83 RemoveStaleRequests(std::move(expired_request_ids)); | |
| 84 } | |
| 85 | |
| 86 void PickRequestTask::ChooseRequestAndCallback( | |
| 87 std::vector<std::unique_ptr<SavePageRequest>> valid_requests) { | |
| 88 // Pick the most deserving request for our conditions. | 68 // Pick the most deserving request for our conditions. |
| 89 const SavePageRequest* picked_request = nullptr; | 69 const SavePageRequest* picked_request = nullptr; |
| 90 | 70 |
| 91 RequestCompareFunction comparator = nullptr; | 71 RequestCompareFunction comparator = nullptr; |
| 92 | 72 |
| 93 // Choose which comparison function to use based on policy. | 73 // Choose which comparison function to use based on policy. |
| 94 if (policy_->RetryCountIsMoreImportantThanRecency()) | 74 if (policy_->RetryCountIsMoreImportantThanRecency()) |
| 95 comparator = &PickRequestTask::RetryCountFirstCompareFunction; | 75 comparator = &PickRequestTask::RetryCountFirstCompareFunction; |
| 96 else | 76 else |
| 97 comparator = &PickRequestTask::RecencyFirstCompareFunction; | 77 comparator = &PickRequestTask::RecencyFirstCompareFunction; |
| 98 | 78 |
| 99 // TODO(petewil): Consider replacing this bool with a better named enum. | 79 // TODO(petewil): Consider replacing this bool with a better named enum. |
| 100 bool non_user_requested_tasks_remaining = false; | 80 bool non_user_requested_tasks_remaining = false; |
| 81 bool cleanup_needed = false; | |
| 101 | 82 |
| 102 size_t available_request_count = 0; | 83 size_t available_request_count = 0; |
| 103 | 84 |
| 104 // Iterate once through the requests, keeping track of best candidate. | 85 // Iterate once through the requests, keeping track of best candidate. |
| 105 for (unsigned i = 0; i < valid_requests.size(); ++i) { | 86 for (unsigned i = 0; i < requests.size(); ++i) { |
| 87 // If the request is expired or has exceeded the retry count, skip it. | |
| 88 if (base::Time::Now() - requests[i]->creation_time() >= | |
|
fgorski
2016/12/02 21:44:04
One more reason to extract a method as suggested e
Pete Williamson
2016/12/05 20:39:45
Done, new class is OfflinerPolicyUtils.
| |
| 89 base::TimeDelta::FromSeconds(kRequestExpirationTimeInSeconds) || | |
| 90 requests[i]->started_attempt_count() >= policy_->GetMaxStartedTries() || | |
| 91 requests[i]->completed_attempt_count() >= | |
| 92 policy_->GetMaxCompletedTries()) { | |
| 93 cleanup_needed = true; | |
| 94 continue; | |
| 95 } | |
| 96 | |
| 106 // If the request is on the disabled list, skip it. | 97 // If the request is on the disabled list, skip it. |
| 107 auto search = disabled_requests_.find(valid_requests[i]->request_id()); | 98 auto search = disabled_requests_.find(requests[i]->request_id()); |
| 108 if (search != disabled_requests_.end()) | 99 if (search != disabled_requests_.end()) |
| 109 continue; | 100 continue; |
| 101 | |
| 110 // If there are non-user-requested tasks remaining, we need to make sure | 102 // If there are non-user-requested tasks remaining, we need to make sure |
| 111 // that they are scheduled after we run out of user requested tasks. Here we | 103 // that they are scheduled after we run out of user requested tasks. Here we |
| 112 // detect if any exist. If we don't find any user-requested tasks, we will | 104 // detect if any exist. If we don't find any user-requested tasks, we will |
| 113 // inform the "not_picked_callback_" that it needs to schedule a task for | 105 // inform the "not_picked_callback_" that it needs to schedule a task for |
| 114 // non-user-requested items, which have different network and power needs. | 106 // non-user-requested items, which have different network and power needs. |
| 115 if (!valid_requests[i]->user_requested()) | 107 if (!requests[i]->user_requested()) |
| 116 non_user_requested_tasks_remaining = true; | 108 non_user_requested_tasks_remaining = true; |
| 117 if (valid_requests[i]->request_state() == | 109 if (requests[i]->request_state() == |
| 118 SavePageRequest::RequestState::AVAILABLE) { | 110 SavePageRequest::RequestState::AVAILABLE) { |
| 119 available_request_count++; | 111 available_request_count++; |
| 120 } | 112 } |
| 121 if (!RequestConditionsSatisfied(valid_requests[i].get())) | 113 if (!RequestConditionsSatisfied(requests[i].get())) |
| 122 continue; | 114 continue; |
| 123 if (IsNewRequestBetter(picked_request, valid_requests[i].get(), comparator)) | 115 if (IsNewRequestBetter(picked_request, requests[i].get(), comparator)) |
| 124 picked_request = valid_requests[i].get(); | 116 picked_request = requests[i].get(); |
| 125 } | 117 } |
| 126 | 118 |
| 127 // Report the request queue counts. | 119 // Report the request queue counts. TODO(reviewers): This used to report only |
| 128 request_count_callback_.Run(valid_requests.size(), available_request_count); | 120 // the count of valid entries, but now we report all entries. Reviewers, is |
|
dougarnett
2016/12/02 23:00:11
Looks fine. Before valid_requests.size() was the t
Pete Williamson
2016/12/05 20:39:45
Acknowledged.
| |
| 121 // that a problem? | |
| 122 request_count_callback_.Run(requests.size(), available_request_count); | |
| 129 | 123 |
| 130 // If we have a best request to try next, get the request coodinator to | 124 // If we have a best request to try next, get the request coodinator to |
| 131 // start it. Otherwise return that we have no candidates. | 125 // start it. Otherwise return that we have no candidates. |
| 132 if (picked_request != nullptr) | 126 if (picked_request != nullptr) |
|
fgorski
2016/12/02 21:44:04
add {} to both clauses, because your else spans mu
Pete Williamson
2016/12/05 20:39:45
Ah, I must have missed seeing it wrap. Fixed.
| |
| 133 picked_callback_.Run(*picked_request); | 127 picked_callback_.Run(*picked_request, cleanup_needed); |
| 134 else | 128 else |
| 135 not_picked_callback_.Run(non_user_requested_tasks_remaining); | 129 not_picked_callback_.Run(non_user_requested_tasks_remaining, |
| 136 } | 130 cleanup_needed); |
| 137 | 131 |
| 138 // Continue the async part of the processing by deleting the expired requests. | |
| 139 // TODO(petewil): Does that really need to be done on the task queue? Hard to | |
| 140 // see how we need to wait for it before starting the next task. OTOH, we'd hate | |
| 141 // to do a second slow DB operation to get entries a second time, and waiting | |
| 142 // until this is done will make sure other gets don't see these old entries. | |
| 143 // Consider moving this to a fresh task type to clean the queue. | |
| 144 void PickRequestTask::RemoveStaleRequests( | |
| 145 std::vector<int64_t> stale_request_ids) { | |
| 146 store_->RemoveRequests(stale_request_ids, | |
| 147 base::Bind(&PickRequestTask::OnRequestsExpired, | |
| 148 weak_ptr_factory_.GetWeakPtr())); | |
| 149 } | |
| 150 | |
| 151 void PickRequestTask::OnRequestsExpired( | |
| 152 std::unique_ptr<UpdateRequestsResult> result) { | |
| 153 RequestNotifier::BackgroundSavePageResult save_page_result( | |
| 154 RequestNotifier::BackgroundSavePageResult::EXPIRED); | |
| 155 for (const auto& request : result->updated_items) { | |
| 156 event_logger_->RecordDroppedSavePageRequest( | |
| 157 request.client_id().name_space, save_page_result, request.request_id()); | |
| 158 notifier_->NotifyCompleted(request, save_page_result); | |
| 159 } | |
| 160 | |
| 161 // The task is now done, return control to the task queue. | |
| 162 TaskComplete(); | 132 TaskComplete(); |
| 163 } | 133 } |
| 164 | 134 |
| 165 void PickRequestTask::SplitRequests( | |
| 166 std::vector<std::unique_ptr<SavePageRequest>> requests, | |
| 167 std::vector<std::unique_ptr<SavePageRequest>>* valid_requests, | |
| 168 std::vector<int64_t>* expired_request_ids) { | |
| 169 for (auto& request : requests) { | |
| 170 if (base::Time::Now() - request->creation_time() >= | |
| 171 base::TimeDelta::FromSeconds(kRequestExpirationTimeInSeconds)) { | |
| 172 expired_request_ids->push_back(request->request_id()); | |
| 173 } else { | |
| 174 valid_requests->push_back(std::move(request)); | |
| 175 } | |
| 176 } | |
| 177 } | |
| 178 | |
| 179 // Filter out requests that don't meet the current conditions. For instance, if | 135 // Filter out requests that don't meet the current conditions. For instance, if |
| 180 // this is a predictive request, and we are not on WiFi, it should be ignored | 136 // this is a predictive request, and we are not on WiFi, it should be ignored |
| 181 // this round. | 137 // this round. |
| 182 bool PickRequestTask::RequestConditionsSatisfied( | 138 bool PickRequestTask::RequestConditionsSatisfied( |
| 183 const SavePageRequest* request) { | 139 const SavePageRequest* request) { |
| 184 // If the user did not request the page directly, make sure we are connected | 140 // If the user did not request the page directly, make sure we are connected |
| 185 // to power and have WiFi and sufficient battery remaining before we take this | 141 // to power and have WiFi and sufficient battery remaining before we take this |
| 186 // request. | 142 // request. |
| 187 if (!device_conditions_->IsPowerConnected() && | 143 if (!device_conditions_->IsPowerConnected() && |
| 188 policy_->PowerRequired(request->user_requested())) { | 144 policy_->PowerRequired(request->user_requested())) { |
| 189 return false; | 145 return false; |
| 190 } | 146 } |
| 191 | 147 |
| 192 if (device_conditions_->GetNetConnectionType() != | 148 if (device_conditions_->GetNetConnectionType() != |
| 193 net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI && | 149 net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI && |
| 194 policy_->UnmeteredNetworkRequired(request->user_requested())) { | 150 policy_->UnmeteredNetworkRequired(request->user_requested())) { |
| 195 return false; | 151 return false; |
| 196 } | 152 } |
| 197 | 153 |
| 198 if (device_conditions_->GetBatteryPercentage() < | 154 if (device_conditions_->GetBatteryPercentage() < |
| 199 policy_->BatteryPercentageRequired(request->user_requested())) { | 155 policy_->BatteryPercentageRequired(request->user_requested())) { |
| 200 return false; | 156 return false; |
| 201 } | 157 } |
| 202 | 158 |
| 203 // If we have already started this page the max number of times, it is not | |
| 204 // eligible to try again. | |
| 205 if (request->started_attempt_count() >= policy_->GetMaxStartedTries()) | |
| 206 return false; | |
| 207 | |
| 208 // If we have already completed trying this page the max number of times, it | |
| 209 // is not eligible to try again. | |
| 210 if (request->completed_attempt_count() >= policy_->GetMaxCompletedTries()) | |
| 211 return false; | |
| 212 | |
| 213 // If the request is paused, do not consider it. | 159 // If the request is paused, do not consider it. |
| 214 if (request->request_state() == SavePageRequest::RequestState::PAUSED) | 160 if (request->request_state() == SavePageRequest::RequestState::PAUSED) |
| 215 return false; | 161 return false; |
| 216 | 162 |
| 217 // If the request is expired, do not consider it. | |
| 218 base::TimeDelta requestAge = base::Time::Now() - request->creation_time(); | |
| 219 if (requestAge > base::TimeDelta::FromSeconds( | |
| 220 policy_->GetRequestExpirationTimeInSeconds())) | |
| 221 return false; | |
| 222 | |
| 223 // If this request is not active yet, return false. | 163 // If this request is not active yet, return false. |
| 224 // TODO(petewil): If the only reason we return nothing to do is that we have | 164 // TODO(petewil): If the only reason we return nothing to do is that we have |
| 225 // inactive requests, we still want to try again later after their activation | 165 // inactive requests, we still want to try again later after their activation |
| 226 // time elapses, we shouldn't take ourselves completely off the scheduler. | 166 // time elapses, we shouldn't take ourselves completely off the scheduler. |
| 227 if (request->activation_time() > base::Time::Now()) | 167 if (request->activation_time() > base::Time::Now()) |
| 228 return false; | 168 return false; |
| 229 | 169 |
| 230 return true; | 170 return true; |
| 231 } | 171 } |
| 232 | 172 |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 305 int result = signum(difference.InMilliseconds()); | 245 int result = signum(difference.InMilliseconds()); |
| 306 | 246 |
| 307 // Flip the direction of comparison if policy prefers fewer retries. | 247 // Flip the direction of comparison if policy prefers fewer retries. |
| 308 if (policy_->ShouldPreferEarlierRequests()) | 248 if (policy_->ShouldPreferEarlierRequests()) |
| 309 result *= -1; | 249 result *= -1; |
| 310 | 250 |
| 311 return result; | 251 return result; |
| 312 } | 252 } |
| 313 | 253 |
| 314 } // namespace offline_pages | 254 } // namespace offline_pages |
| OLD | NEW |