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

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

Issue 2473553004: Request Picker task (Closed)
Patch Set: Created 4 years, 1 month 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 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/request_picker.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"
10 #include "components/offline_pages/background/device_conditions.h"
11 #include "components/offline_pages/background/offliner_policy.h"
12 #include "components/offline_pages/background/request_coordinator_event_logger.h "
13 #include "components/offline_pages/background/request_notifier.h"
14 #include "components/offline_pages/background/request_queue.h"
9 #include "components/offline_pages/background/save_page_request.h" 15 #include "components/offline_pages/background/save_page_request.h"
10 16
11 namespace { 17 namespace {
12 template <typename T> 18 template <typename T>
13 int signum(T t) { 19 int signum(T t) {
14 return (T(0) < t) - (t < T(0)); 20 return (T(0) < t) - (t < T(0));
15 } 21 }
16 22
17 #define CALL_MEMBER_FUNCTION(object, ptrToMember) ((object)->*(ptrToMember)) 23 #define CALL_MEMBER_FUNCTION(object, ptrToMember) ((object)->*(ptrToMember))
18 } // namespace 24 } // namespace
19 25
20 namespace offline_pages { 26 namespace offline_pages {
21 27
22 RequestPicker::RequestPicker(RequestQueue* requestQueue, 28 PickRequestTask::PickRequestTask(RequestQueue* request_queue,
23 OfflinerPolicy* policy, 29 OfflinerPolicy* policy,
24 RequestNotifier* notifier, 30 RequestNotifier* notifier,
25 RequestCoordinatorEventLogger* event_logger) 31 RequestCoordinatorEventLogger* event_logger,
26 : queue_(requestQueue), 32 RequestPickedCallback picked_callback,
33 RequestNotPickedCallback not_picked_callback,
34 DeviceConditions* device_conditions,
35 const std::set<int64_t>& disabled_requests)
36 : request_queue_(request_queue),
27 policy_(policy), 37 policy_(policy),
28 notifier_(notifier), 38 notifier_(notifier),
29 event_logger_(event_logger), 39 event_logger_(event_logger),
30 fewer_retries_better_(false), 40 picked_callback_(picked_callback),
31 earlier_requests_better_(false), 41 not_picked_callback_(not_picked_callback),
42 device_conditions_(device_conditions),
43 disabled_requests_(disabled_requests),
32 weak_ptr_factory_(this) {} 44 weak_ptr_factory_(this) {}
33 45
34 RequestPicker::~RequestPicker() {} 46 PickRequestTask::~PickRequestTask() {}
35 47
36 // Entry point for the async operation to choose the next request. 48 void PickRequestTask::Run() {
37 void RequestPicker::ChooseNextRequest( 49 // Get all the requests from the queue, we will classify them in the callback.
38 RequestCoordinator::RequestPickedCallback picked_callback, 50 request_queue_->GetRequests(base::Bind(&PickRequestTask::HandleGetResults,
39 RequestCoordinator::RequestNotPickedCallback not_picked_callback, 51 weak_ptr_factory_.GetWeakPtr()));
40 DeviceConditions* device_conditions,
41 const std::set<int64_t>& disabled_requests) {
42 picked_callback_ = picked_callback;
43 not_picked_callback_ = not_picked_callback;
44 fewer_retries_better_ = policy_->ShouldPreferUntriedRequests();
45 earlier_requests_better_ = policy_->ShouldPreferEarlierRequests();
46 current_conditions_.reset(new DeviceConditions(*device_conditions));
47 // Get all requests from queue (there is no filtering mechanism).
48 queue_->GetRequests(base::Bind(&RequestPicker::GetRequestResultCallback,
49 weak_ptr_factory_.GetWeakPtr(),
50 disabled_requests));
51 } 52 }
52 53
53 // When we get contents from the queue, use them to pick the next 54 void PickRequestTask::HandleGetResults(
54 // request to operate on (if any). 55 QueueResults::GetRequestsResult,
55 void RequestPicker::GetRequestResultCallback(
56 const std::set<int64_t>& disabled_requests,
57 RequestQueue::GetRequestsResult,
58 std::vector<std::unique_ptr<SavePageRequest>> requests) { 56 std::vector<std::unique_ptr<SavePageRequest>> requests) {
59 // If there is nothing to do, return right away. 57 // If there is nothing to do, return right away.
60 if (requests.size() == 0) { 58 if (requests.size() == 0) {
61 not_picked_callback_.Run(false); 59 not_picked_callback_.Run(false);
62 return; 60 return;
63 } 61 }
64 62
65 // Get the expired requests to be removed from the queue, and the valid ones 63 // Get the expired requests to be removed from the queue, and the valid ones
66 // from which to pick the next request. 64 // from which to pick the next request.
67 std::vector<std::unique_ptr<SavePageRequest>> valid_requests; 65 std::vector<std::unique_ptr<SavePageRequest>> valid_requests;
68 std::vector<std::unique_ptr<SavePageRequest>> expired_requests; 66 std::vector<std::unique_ptr<SavePageRequest>> expired_requests;
69 SplitRequests(std::move(requests), &valid_requests, &expired_requests); 67 SplitRequests(std::move(requests), &valid_requests, &expired_requests);
70 std::vector<int64_t> expired_request_ids; 68 std::vector<int64_t> expired_request_ids;
71 for (const auto& request : expired_requests) 69 for (const auto& request : expired_requests)
72 expired_request_ids.push_back(request->request_id()); 70 expired_request_ids.push_back(request->request_id());
73 71
74 queue_->RemoveRequests(expired_request_ids, 72 // Continue processing by choosing a request.
75 base::Bind(&RequestPicker::OnRequestExpired, 73 ChooseRequest(std::move(valid_requests));
dougarnett 2016/11/03 17:01:22 Consider ChooseRequestAndCallback() Or mentioning
Pete Williamson 2016/11/03 19:26:29 Done.
76 weak_ptr_factory_.GetWeakPtr()));
77 74
75 // Continue Async Processing.
76 RemoveExpiredEntries(std::move(expired_requests));
dougarnett 2016/11/03 17:01:22 Generalize to remove other stale requests too? May
Pete Williamson 2016/11/03 19:26:29 Renamed to "RemoveStaleRequests"
dougarnett 2016/11/03 19:56:51 Still some more work though to check started_attem
Pete Williamson 2016/11/04 18:53:37 I'm not sure it is better to clean them here. We
dougarnett 2016/11/04 19:28:26 Yes, I want you to remove them here. If they are d
dougarnett 2016/11/07 17:09:07 Btw, I'm happy to do the follow-up here but I am n
Pete Williamson 2016/11/08 00:36:45 I agree that it needs to be done (in the light of
77 }
78
79 void PickRequestTask::ChooseRequest(
80 std::vector<std::unique_ptr<SavePageRequest>> valid_requests) {
78 // Pick the most deserving request for our conditions. 81 // Pick the most deserving request for our conditions.
79 const SavePageRequest* picked_request = nullptr; 82 const SavePageRequest* picked_request = nullptr;
80 83
81 RequestCompareFunction comparator = nullptr; 84 RequestCompareFunction comparator = nullptr;
82 85
83 // Choose which comparison function to use based on policy. 86 // Choose which comparison function to use based on policy.
84 if (policy_->RetryCountIsMoreImportantThanRecency()) 87 if (policy_->RetryCountIsMoreImportantThanRecency())
85 comparator = &RequestPicker::RetryCountFirstCompareFunction; 88 comparator = &PickRequestTask::RetryCountFirstCompareFunction;
86 else 89 else
87 comparator = &RequestPicker::RecencyFirstCompareFunction; 90 comparator = &PickRequestTask::RecencyFirstCompareFunction;
88 91
89 // Iterate once through the requests, keeping track of best candidate. 92 // Iterate once through the requests, keeping track of best candidate.
90 bool non_user_requested_tasks_remaining = false; 93 bool non_user_requested_tasks_remaining = false;
91 for (unsigned i = 0; i < valid_requests.size(); ++i) { 94 for (unsigned i = 0; i < valid_requests.size(); ++i) {
92 // If the request is on the disabled list, skip it. 95 // If the request is on the disabled list, skip it.
93 auto search = disabled_requests.find(valid_requests[i]->request_id()); 96 auto search = disabled_requests_.find(valid_requests[i]->request_id());
94 if (search != disabled_requests.end()) { 97 if (search != disabled_requests_.end()) {
95 continue; 98 continue;
96 } 99 }
97 if (!valid_requests[i]->user_requested()) 100 if (!valid_requests[i]->user_requested())
98 non_user_requested_tasks_remaining = true; 101 non_user_requested_tasks_remaining = true;
99 if (!RequestConditionsSatisfied(valid_requests[i].get())) 102 if (!RequestConditionsSatisfied(valid_requests[i].get()))
100 continue; 103 continue;
101 if (IsNewRequestBetter(picked_request, valid_requests[i].get(), comparator)) 104 if (IsNewRequestBetter(picked_request, valid_requests[i].get(), comparator))
102 picked_request = valid_requests[i].get(); 105 picked_request = valid_requests[i].get();
103 } 106 }
104 107
105 // If we have a best request to try next, get the request coodinator to 108 // If we have a best request to try next, get the request coodinator to
106 // start it. Otherwise return that we have no candidates. 109 // start it. Otherwise return that we have no candidates.
107 if (picked_request != nullptr) { 110 if (picked_request != nullptr) {
108 picked_callback_.Run(*picked_request); 111 picked_callback_.Run(*picked_request);
109 } else { 112 } else {
110 not_picked_callback_.Run(non_user_requested_tasks_remaining); 113 not_picked_callback_.Run(non_user_requested_tasks_remaining);
111 } 114 }
115
116 // The task is now done, return control to the task queue.
117 TaskComplete();
118 }
119
120 // Continue the async part of the processing by deleting the expired requests.
121 // TODO(petewil): Does that really need to be done on the task queue? Hard to
122 // see how we need to wait for it before starting the next task. OTOH, we'd hate
123 // to do a second slow DB operation to get entries a second time, and waiting
124 // until this is done will make sure other gets don't see these old entries.
125 void PickRequestTask::RemoveExpiredEntries(
126 std::vector<std::unique_ptr<SavePageRequest>> expired_requests) {
127 std::vector<int64_t> expired_request_ids;
128 for (const auto& request : expired_requests)
129 expired_request_ids.push_back(request->request_id());
130
131 request_queue_->RemoveRequests(expired_request_ids,
132 base::Bind(&PickRequestTask::OnRequestsExpired,
133 weak_ptr_factory_.GetWeakPtr()));
134 }
135
136 void PickRequestTask::OnRequestsExpired(
137 std::unique_ptr<QueueResults::UpdateRequestsResult> result) {
138 RequestNotifier::BackgroundSavePageResult save_page_result(
139 RequestNotifier::BackgroundSavePageResult::EXPIRED);
140 for (const auto& request : result->updated_items) {
141 event_logger_->RecordDroppedSavePageRequest(
142 request.client_id().name_space, save_page_result, request.request_id());
143 notifier_->NotifyCompleted(request, save_page_result);
144 }
145
146 // Now all our async operations are complete. Return processing to the task
147 // queue.
148 TaskComplete();
149 }
150
151 void PickRequestTask::SplitRequests(
152 std::vector<std::unique_ptr<SavePageRequest>> requests,
153 std::vector<std::unique_ptr<SavePageRequest>>* valid_requests,
154 std::vector<std::unique_ptr<SavePageRequest>>* expired_requests) {
155 for (auto& request : requests) {
156 if (base::Time::Now() - request->creation_time() >=
157 base::TimeDelta::FromSeconds(kRequestExpirationTimeInSeconds)) {
158 expired_requests->push_back(std::move(request));
159 } else {
160 valid_requests->push_back(std::move(request));
161 }
162 }
112 } 163 }
113 164
114 // Filter out requests that don't meet the current conditions. For instance, if 165 // Filter out requests that don't meet the current conditions. For instance, if
115 // this is a predictive request, and we are not on WiFi, it should be ignored 166 // this is a predictive request, and we are not on WiFi, it should be ignored
116 // this round. 167 // this round.
117 bool RequestPicker::RequestConditionsSatisfied(const SavePageRequest* request) { 168 bool PickRequestTask::RequestConditionsSatisfied(
169 const SavePageRequest* request) {
118 // If the user did not request the page directly, make sure we are connected 170 // If the user did not request the page directly, make sure we are connected
119 // to power and have WiFi and sufficient battery remaining before we take this 171 // to power and have WiFi and sufficient battery remaining before we take this
120 // request. 172 // request.
121 173
122 if (!current_conditions_->IsPowerConnected() && 174 if (!device_conditions_->IsPowerConnected() &&
123 policy_->PowerRequired(request->user_requested())) { 175 policy_->PowerRequired(request->user_requested())) {
124 return false; 176 return false;
125 } 177 }
126 178
127 if (current_conditions_->GetNetConnectionType() != 179 if (device_conditions_->GetNetConnectionType() !=
128 net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI && 180 net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI &&
129 policy_->UnmeteredNetworkRequired(request->user_requested())) { 181 policy_->UnmeteredNetworkRequired(request->user_requested())) {
130 return false; 182 return false;
131 } 183 }
132 184
133 if (current_conditions_->GetBatteryPercentage() < 185 if (device_conditions_->GetBatteryPercentage() <
134 policy_->BatteryPercentageRequired(request->user_requested())) { 186 policy_->BatteryPercentageRequired(request->user_requested())) {
135 return false; 187 return false;
136 } 188 }
137 189
138 // If we have already started this page the max number of times, it is not 190 // If we have already started this page the max number of times, it is not
139 // eligible to try again. 191 // eligible to try again.
140 if (request->started_attempt_count() >= policy_->GetMaxStartedTries()) 192 if (request->started_attempt_count() >= policy_->GetMaxStartedTries())
141 return false; 193 return false;
142 194
143 // If we have already completed trying this page the max number of times, it 195 // If we have already completed trying this page the max number of times, it
144 // is not eligible to try again. 196 // is not eligible to try again.
145 if (request->completed_attempt_count() >= policy_->GetMaxCompletedTries()) 197 if (request->completed_attempt_count() >= policy_->GetMaxCompletedTries())
146 return false; 198 return false;
147 199
148 // If the request is paused, do not consider it. 200 // If the request is paused, do not consider it.
149 if (request->request_state() == SavePageRequest::RequestState::PAUSED) 201 if (request->request_state() == SavePageRequest::RequestState::PAUSED)
150 return false; 202 return false;
151 203
152 // If the request is expired, do not consider it. 204 // If the request is expired, do not consider it.
153 base::TimeDelta requestAge = base::Time::Now() - request->creation_time(); 205 base::TimeDelta requestAge = base::Time::Now() - request->creation_time();
154 if (requestAge > 206 if (requestAge > base::TimeDelta::FromSeconds(
155 base::TimeDelta::FromSeconds( 207 policy_->GetRequestExpirationTimeInSeconds()))
156 policy_->GetRequestExpirationTimeInSeconds()))
157 return false; 208 return false;
158 209
159 // If this request is not active yet, return false. 210 // If this request is not active yet, return false.
160 // TODO(petewil): If the only reason we return nothing to do is that we have 211 // TODO(petewil): If the only reason we return nothing to do is that we have
161 // inactive requests, we still want to try again later after their activation 212 // inactive requests, we still want to try again later after their activation
162 // time elapses, we shouldn't take ourselves completely off the scheduler. 213 // time elapses, we shouldn't take ourselves completely off the scheduler.
163 if (request->activation_time() > base::Time::Now()) 214 if (request->activation_time() > base::Time::Now())
164 return false; 215 return false;
165 216
166 return true; 217 return true;
167 } 218 }
168 219
169 // Look at policies to decide which requests to prefer. 220 // Look at policies to decide which requests to prefer.
170 bool RequestPicker::IsNewRequestBetter(const SavePageRequest* oldRequest, 221 bool PickRequestTask::IsNewRequestBetter(const SavePageRequest* oldRequest,
171 const SavePageRequest* newRequest, 222 const SavePageRequest* newRequest,
172 RequestCompareFunction comparator) { 223 RequestCompareFunction comparator) {
173
174 // If there is no old request, the new one is better. 224 // If there is no old request, the new one is better.
175 if (oldRequest == nullptr) 225 if (oldRequest == nullptr)
176 return true; 226 return true;
177 227
178 // User requested pages get priority. 228 // User requested pages get priority.
179 if (newRequest->user_requested() && !oldRequest->user_requested()) 229 if (newRequest->user_requested() && !oldRequest->user_requested())
180 return true; 230 return true;
181 231
182 // Otherwise, use the comparison function for the current policy, which 232 // Otherwise, use the comparison function for the current policy, which
183 // returns true if the older request is better. 233 // returns true if the older request is better.
184 return !(CALL_MEMBER_FUNCTION(this, comparator)(oldRequest, newRequest)); 234 return !(CALL_MEMBER_FUNCTION(this, comparator)(oldRequest, newRequest));
185 } 235 }
186 236
187 // Compare the results, checking request count before recency. Returns true if 237 // Compare the results, checking request count before recency. Returns true if
188 // left hand side is better, false otherwise. 238 // left hand side is better, false otherwise.
189 bool RequestPicker::RetryCountFirstCompareFunction( 239 bool PickRequestTask::RetryCountFirstCompareFunction(
190 const SavePageRequest* left, const SavePageRequest* right) { 240 const SavePageRequest* left,
241 const SavePageRequest* right) {
191 // Check the attempt count. 242 // Check the attempt count.
192 int result = CompareRetryCount(left, right); 243 int result = CompareRetryCount(left, right);
193 244
194 if (result != 0) 245 if (result != 0)
195 return (result > 0); 246 return (result > 0);
196 247
197 // If we get here, the attempt counts were the same, so check recency. 248 // If we get here, the attempt counts were the same, so check recency.
198 result = CompareCreationTime(left, right); 249 result = CompareCreationTime(left, right);
199 250
200 return (result > 0); 251 return (result > 0);
201 } 252 }
202 253
203 // Compare the results, checking recency before request count. Returns true if 254 // Compare the results, checking recency before request count. Returns true if
204 // left hand side is better, false otherwise. 255 // left hand side is better, false otherwise.
205 bool RequestPicker::RecencyFirstCompareFunction( 256 bool PickRequestTask::RecencyFirstCompareFunction(
206 const SavePageRequest* left, const SavePageRequest* right) { 257 const SavePageRequest* left,
258 const SavePageRequest* right) {
207 // Check the recency. 259 // Check the recency.
208 int result = CompareCreationTime(left, right); 260 int result = CompareCreationTime(left, right);
209 261
210 if (result != 0) 262 if (result != 0)
211 return (result > 0); 263 return (result > 0);
212 264
213 // If we get here, the recency was the same, so check the attempt count. 265 // If we get here, the recency was the same, so check the attempt count.
214 result = CompareRetryCount(left, right); 266 result = CompareRetryCount(left, right);
215 267
216 return (result > 0); 268 return (result > 0);
217 } 269 }
218 270
219 // Compare left and right side, returning 1 if the left side is better 271 // Compare left and right side, returning 1 if the left side is better
220 // (preferred by policy), 0 if the same, and -1 if the right side is better. 272 // (preferred by policy), 0 if the same, and -1 if the right side is better.
221 int RequestPicker::CompareRetryCount( 273 int PickRequestTask::CompareRetryCount(const SavePageRequest* left,
222 const SavePageRequest* left, const SavePageRequest* right) { 274 const SavePageRequest* right) {
223 // Check the attempt count. 275 // Check the attempt count.
224 int result = signum(left->completed_attempt_count() - 276 int result = signum(left->completed_attempt_count() -
225 right->completed_attempt_count()); 277 right->completed_attempt_count());
226 278
227 // Flip the direction of comparison if policy prefers fewer retries. 279 // Flip the direction of comparison if policy prefers fewer retries.
228 if (fewer_retries_better_) 280 if (policy_->ShouldPreferUntriedRequests())
229 result *= -1; 281 result *= -1;
230 282
231 return result; 283 return result;
232 } 284 }
233 285
234 // Compare left and right side, returning 1 if the left side is better 286 // Compare left and right side, returning 1 if the left side is better
235 // (preferred by policy), 0 if the same, and -1 if the right side is better. 287 // (preferred by policy), 0 if the same, and -1 if the right side is better.
236 int RequestPicker::CompareCreationTime( 288 int PickRequestTask::CompareCreationTime(const SavePageRequest* left,
237 const SavePageRequest* left, const SavePageRequest* right) { 289 const SavePageRequest* right) {
238 // Check the recency. 290 // Check the recency.
239 base::TimeDelta difference = left->creation_time() - right->creation_time(); 291 base::TimeDelta difference = left->creation_time() - right->creation_time();
240 int result = signum(difference.InMilliseconds()); 292 int result = signum(difference.InMilliseconds());
241 293
242 // Flip the direction of comparison if policy prefers fewer retries. 294 // Flip the direction of comparison if policy prefers fewer retries.
243 if (earlier_requests_better_) 295 if (policy_->ShouldPreferEarlierRequests())
244 result *= -1; 296 result *= -1;
245 297
246 return result; 298 return result;
247 } 299 }
248 300
249 void RequestPicker::SplitRequests(
250 std::vector<std::unique_ptr<SavePageRequest>> requests,
251 std::vector<std::unique_ptr<SavePageRequest>>* valid_requests,
252 std::vector<std::unique_ptr<SavePageRequest>>* expired_requests) {
253 for (auto& request : requests) {
254 if (base::Time::Now() - request->creation_time() >=
255 base::TimeDelta::FromSeconds(kRequestExpirationTimeInSeconds)) {
256 expired_requests->push_back(std::move(request));
257 } else {
258 valid_requests->push_back(std::move(request));
259 }
260 }
261 }
262
263 // Callback used after expired requests are deleted from the queue and notifies
264 // the coordinator.
265 void RequestPicker::OnRequestExpired(
266 std::unique_ptr<UpdateRequestsResult> result) {
267 const RequestCoordinator::BackgroundSavePageResult save_page_result(
268 RequestCoordinator::BackgroundSavePageResult::EXPIRED);
269 for (const auto& request : result->updated_items) {
270 event_logger_->RecordDroppedSavePageRequest(
271 request.client_id().name_space, save_page_result, request.request_id());
272 notifier_->NotifyCompleted(request, save_page_result);
273 }
274 }
275
276 } // namespace offline_pages 301 } // namespace offline_pages
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698