OLD | NEW |
| (Empty) |
1 // Copyright (c) 2013 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 "sync/sessions/nudge_tracker.h" | |
6 | |
7 #include <stddef.h> | |
8 | |
9 #include <utility> | |
10 | |
11 #include "base/memory/ptr_util.h" | |
12 #include "sync/internal_api/public/engine/polling_constants.h" | |
13 #include "sync/protocol/sync.pb.h" | |
14 | |
15 namespace syncer { | |
16 namespace sessions { | |
17 | |
18 namespace { | |
19 | |
20 // Delays for syncer nudges. | |
21 const int kDefaultNudgeDelayMilliseconds = 200; | |
22 const int kSlowNudgeDelayMilliseconds = 2000; | |
23 const int kDefaultSessionsCommitDelaySeconds = 10; | |
24 const int kSyncRefreshDelayMilliseconds = 500; | |
25 const int kSyncSchedulerDelayMilliseconds = 250; | |
26 | |
27 base::TimeDelta GetDefaultDelayForType(ModelType model_type, | |
28 base::TimeDelta minimum_delay) { | |
29 switch (model_type) { | |
30 case AUTOFILL: | |
31 // Accompany types rely on nudges from other types, and hence have long | |
32 // nudge delays. | |
33 return base::TimeDelta::FromSeconds(kDefaultShortPollIntervalSeconds); | |
34 case BOOKMARKS: | |
35 case PREFERENCES: | |
36 // Types with sometimes automatic changes get longer delays to allow more | |
37 // coalescing. | |
38 return base::TimeDelta::FromMilliseconds(kSlowNudgeDelayMilliseconds); | |
39 case SESSIONS: | |
40 case FAVICON_IMAGES: | |
41 case FAVICON_TRACKING: | |
42 // Types with navigation triggered changes get longer delays to allow more | |
43 // coalescing. | |
44 return base::TimeDelta::FromSeconds(kDefaultSessionsCommitDelaySeconds); | |
45 default: | |
46 return minimum_delay; | |
47 } | |
48 } | |
49 | |
50 } // namespace | |
51 | |
52 size_t NudgeTracker::kDefaultMaxPayloadsPerType = 10; | |
53 | |
54 NudgeTracker::NudgeTracker() | |
55 : invalidations_enabled_(false), | |
56 invalidations_out_of_sync_(true), | |
57 minimum_local_nudge_delay_( | |
58 base::TimeDelta::FromMilliseconds(kDefaultNudgeDelayMilliseconds)), | |
59 local_refresh_nudge_delay_( | |
60 base::TimeDelta::FromMilliseconds(kSyncRefreshDelayMilliseconds)), | |
61 remote_invalidation_nudge_delay_( | |
62 base::TimeDelta::FromMilliseconds(kSyncSchedulerDelayMilliseconds)) { | |
63 ModelTypeSet protocol_types = ProtocolTypes(); | |
64 // Default initialize all the type trackers. | |
65 for (ModelTypeSet::Iterator it = protocol_types.First(); it.Good(); | |
66 it.Inc()) { | |
67 type_trackers_.insert( | |
68 std::make_pair(it.Get(), base::WrapUnique(new DataTypeTracker()))); | |
69 } | |
70 } | |
71 | |
72 NudgeTracker::~NudgeTracker() { } | |
73 | |
74 bool NudgeTracker::IsSyncRequired() const { | |
75 if (IsRetryRequired()) | |
76 return true; | |
77 | |
78 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
79 it != type_trackers_.end(); ++it) { | |
80 if (it->second->IsSyncRequired()) { | |
81 return true; | |
82 } | |
83 } | |
84 | |
85 return false; | |
86 } | |
87 | |
88 bool NudgeTracker::IsGetUpdatesRequired() const { | |
89 if (invalidations_out_of_sync_) | |
90 return true; | |
91 | |
92 if (IsRetryRequired()) | |
93 return true; | |
94 | |
95 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
96 it != type_trackers_.end(); ++it) { | |
97 if (it->second->IsGetUpdatesRequired()) { | |
98 return true; | |
99 } | |
100 } | |
101 return false; | |
102 } | |
103 | |
104 bool NudgeTracker::IsRetryRequired() const { | |
105 if (sync_cycle_start_time_.is_null()) | |
106 return false; | |
107 | |
108 if (current_retry_time_.is_null()) | |
109 return false; | |
110 | |
111 return current_retry_time_ <= sync_cycle_start_time_; | |
112 } | |
113 | |
114 void NudgeTracker::RecordSuccessfulSyncCycle() { | |
115 // If a retry was required, we've just serviced it. Unset the flag. | |
116 if (IsRetryRequired()) | |
117 current_retry_time_ = base::TimeTicks(); | |
118 | |
119 // A successful cycle while invalidations are enabled puts us back into sync. | |
120 invalidations_out_of_sync_ = !invalidations_enabled_; | |
121 | |
122 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
123 it != type_trackers_.end(); ++it) { | |
124 it->second->RecordSuccessfulSyncCycle(); | |
125 } | |
126 } | |
127 | |
128 base::TimeDelta NudgeTracker::RecordLocalChange(ModelTypeSet types) { | |
129 // Start with the longest delay. | |
130 base::TimeDelta delay = | |
131 base::TimeDelta::FromSeconds(kDefaultShortPollIntervalSeconds); | |
132 for (ModelTypeSet::Iterator type_it = types.First(); type_it.Good(); | |
133 type_it.Inc()) { | |
134 TypeTrackerMap::const_iterator tracker_it = | |
135 type_trackers_.find(type_it.Get()); | |
136 DCHECK(tracker_it != type_trackers_.end()); | |
137 | |
138 // Only if the type tracker has a valid delay (non-zero) that is shorter | |
139 // than the calculated delay do we update the calculated delay. | |
140 base::TimeDelta type_delay = tracker_it->second->RecordLocalChange(); | |
141 if (type_delay.is_zero()) { | |
142 type_delay = GetDefaultDelayForType(type_it.Get(), | |
143 minimum_local_nudge_delay_); | |
144 } | |
145 if (type_delay < delay) | |
146 delay = type_delay; | |
147 } | |
148 return delay; | |
149 } | |
150 | |
151 base::TimeDelta NudgeTracker::RecordLocalRefreshRequest(ModelTypeSet types) { | |
152 for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) { | |
153 TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(it.Get()); | |
154 DCHECK(tracker_it != type_trackers_.end()); | |
155 tracker_it->second->RecordLocalRefreshRequest(); | |
156 } | |
157 return local_refresh_nudge_delay_; | |
158 } | |
159 | |
160 base::TimeDelta NudgeTracker::RecordRemoteInvalidation( | |
161 syncer::ModelType type, | |
162 std::unique_ptr<InvalidationInterface> invalidation) { | |
163 // Forward the invalidations to the proper recipient. | |
164 TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(type); | |
165 DCHECK(tracker_it != type_trackers_.end()); | |
166 tracker_it->second->RecordRemoteInvalidation(std::move(invalidation)); | |
167 return remote_invalidation_nudge_delay_; | |
168 } | |
169 | |
170 void NudgeTracker::RecordInitialSyncRequired(syncer::ModelType type) { | |
171 TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(type); | |
172 DCHECK(tracker_it != type_trackers_.end()); | |
173 tracker_it->second->RecordInitialSyncRequired(); | |
174 } | |
175 | |
176 void NudgeTracker::RecordCommitConflict(syncer::ModelType type) { | |
177 TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(type); | |
178 DCHECK(tracker_it != type_trackers_.end()); | |
179 tracker_it->second->RecordCommitConflict(); | |
180 } | |
181 | |
182 void NudgeTracker::OnInvalidationsEnabled() { | |
183 invalidations_enabled_ = true; | |
184 } | |
185 | |
186 void NudgeTracker::OnInvalidationsDisabled() { | |
187 invalidations_enabled_ = false; | |
188 invalidations_out_of_sync_ = true; | |
189 } | |
190 | |
191 void NudgeTracker::SetTypesThrottledUntil( | |
192 ModelTypeSet types, | |
193 base::TimeDelta length, | |
194 base::TimeTicks now) { | |
195 for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) { | |
196 TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(it.Get()); | |
197 tracker_it->second->ThrottleType(length, now); | |
198 } | |
199 } | |
200 | |
201 void NudgeTracker::UpdateTypeThrottlingState(base::TimeTicks now) { | |
202 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
203 it != type_trackers_.end(); ++it) { | |
204 it->second->UpdateThrottleState(now); | |
205 } | |
206 } | |
207 | |
208 bool NudgeTracker::IsAnyTypeThrottled() const { | |
209 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
210 it != type_trackers_.end(); ++it) { | |
211 if (it->second->IsThrottled()) { | |
212 return true; | |
213 } | |
214 } | |
215 return false; | |
216 } | |
217 | |
218 bool NudgeTracker::IsTypeThrottled(ModelType type) const { | |
219 DCHECK(type_trackers_.find(type) != type_trackers_.end()); | |
220 return type_trackers_.find(type)->second->IsThrottled(); | |
221 } | |
222 | |
223 base::TimeDelta NudgeTracker::GetTimeUntilNextUnthrottle( | |
224 base::TimeTicks now) const { | |
225 DCHECK(IsAnyTypeThrottled()) << "This function requires a pending unthrottle"; | |
226 | |
227 // Return min of GetTimeUntilUnthrottle() values for all IsThrottled() types. | |
228 base::TimeDelta time_until_next_unthrottle = base::TimeDelta::Max(); | |
229 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
230 it != type_trackers_.end(); ++it) { | |
231 if (it->second->IsThrottled()) { | |
232 time_until_next_unthrottle = std::min( | |
233 time_until_next_unthrottle, it->second->GetTimeUntilUnthrottle(now)); | |
234 } | |
235 } | |
236 DCHECK(!time_until_next_unthrottle.is_max()); | |
237 | |
238 return time_until_next_unthrottle; | |
239 } | |
240 | |
241 ModelTypeSet NudgeTracker::GetThrottledTypes() const { | |
242 ModelTypeSet result; | |
243 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
244 it != type_trackers_.end(); ++it) { | |
245 if (it->second->IsThrottled()) { | |
246 result.Put(it->first); | |
247 } | |
248 } | |
249 return result; | |
250 } | |
251 | |
252 ModelTypeSet NudgeTracker::GetNudgedTypes() const { | |
253 ModelTypeSet result; | |
254 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
255 it != type_trackers_.end(); ++it) { | |
256 if (it->second->HasLocalChangePending()) { | |
257 result.Put(it->first); | |
258 } | |
259 } | |
260 return result; | |
261 } | |
262 | |
263 ModelTypeSet NudgeTracker::GetNotifiedTypes() const { | |
264 ModelTypeSet result; | |
265 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
266 it != type_trackers_.end(); ++it) { | |
267 if (it->second->HasPendingInvalidation()) { | |
268 result.Put(it->first); | |
269 } | |
270 } | |
271 return result; | |
272 } | |
273 | |
274 ModelTypeSet NudgeTracker::GetRefreshRequestedTypes() const { | |
275 ModelTypeSet result; | |
276 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
277 it != type_trackers_.end(); ++it) { | |
278 if (it->second->HasRefreshRequestPending()) { | |
279 result.Put(it->first); | |
280 } | |
281 } | |
282 return result; | |
283 } | |
284 | |
285 void NudgeTracker::SetLegacyNotificationHint( | |
286 ModelType type, | |
287 sync_pb::DataTypeProgressMarker* progress) const { | |
288 DCHECK(type_trackers_.find(type) != type_trackers_.end()); | |
289 type_trackers_.find(type)->second->SetLegacyNotificationHint(progress); | |
290 } | |
291 | |
292 sync_pb::GetUpdatesCallerInfo::GetUpdatesSource NudgeTracker::GetLegacySource() | |
293 const { | |
294 // There's an order to these sources: NOTIFICATION, DATATYPE_REFRESH, LOCAL, | |
295 // RETRY. The server makes optimization decisions based on this field, so | |
296 // it's important to get this right. Setting it wrong could lead to missed | |
297 // updates. | |
298 // | |
299 // This complexity is part of the reason why we're deprecating 'source' in | |
300 // favor of 'origin'. | |
301 bool has_invalidation_pending = false; | |
302 bool has_refresh_request_pending = false; | |
303 bool has_commit_pending = false; | |
304 bool is_initial_sync_required = false; | |
305 bool has_retry = IsRetryRequired(); | |
306 | |
307 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
308 it != type_trackers_.end(); ++it) { | |
309 const DataTypeTracker& tracker = *it->second; | |
310 if (!tracker.IsThrottled() && tracker.HasPendingInvalidation()) { | |
311 has_invalidation_pending = true; | |
312 } | |
313 if (!tracker.IsThrottled() && tracker.HasRefreshRequestPending()) { | |
314 has_refresh_request_pending = true; | |
315 } | |
316 if (!tracker.IsThrottled() && tracker.HasLocalChangePending()) { | |
317 has_commit_pending = true; | |
318 } | |
319 if (!tracker.IsThrottled() && tracker.IsInitialSyncRequired()) { | |
320 is_initial_sync_required = true; | |
321 } | |
322 } | |
323 | |
324 if (has_invalidation_pending) { | |
325 return sync_pb::GetUpdatesCallerInfo::NOTIFICATION; | |
326 } else if (has_refresh_request_pending) { | |
327 return sync_pb::GetUpdatesCallerInfo::DATATYPE_REFRESH; | |
328 } else if (is_initial_sync_required) { | |
329 // Not quite accurate, but good enough for our purposes. This setting of | |
330 // SOURCE is just a backward-compatibility hack anyway. | |
331 return sync_pb::GetUpdatesCallerInfo::DATATYPE_REFRESH; | |
332 } else if (has_commit_pending) { | |
333 return sync_pb::GetUpdatesCallerInfo::LOCAL; | |
334 } else if (has_retry) { | |
335 return sync_pb::GetUpdatesCallerInfo::RETRY; | |
336 } else { | |
337 return sync_pb::GetUpdatesCallerInfo::UNKNOWN; | |
338 } | |
339 } | |
340 | |
341 void NudgeTracker::FillProtoMessage( | |
342 ModelType type, | |
343 sync_pb::GetUpdateTriggers* msg) const { | |
344 DCHECK(type_trackers_.find(type) != type_trackers_.end()); | |
345 | |
346 // Fill what we can from the global data. | |
347 msg->set_invalidations_out_of_sync(invalidations_out_of_sync_); | |
348 | |
349 // Delegate the type-specific work to the DataTypeTracker class. | |
350 type_trackers_.find(type)->second->FillGetUpdatesTriggersMessage(msg); | |
351 } | |
352 | |
353 void NudgeTracker::SetSyncCycleStartTime(base::TimeTicks now) { | |
354 sync_cycle_start_time_ = now; | |
355 | |
356 // If current_retry_time_ is still set, that means we have an old retry time | |
357 // left over from a previous cycle. For example, maybe we tried to perform | |
358 // this retry, hit a network connection error, and now we're in exponential | |
359 // backoff. In that case, we want this sync cycle to include the GU retry | |
360 // flag so we leave this variable set regardless of whether or not there is an | |
361 // overwrite pending. | |
362 if (!current_retry_time_.is_null()) { | |
363 return; | |
364 } | |
365 | |
366 // If do not have a current_retry_time_, but we do have a next_retry_time_ and | |
367 // it is ready to go, then we set it as the current_retry_time_. It will stay | |
368 // there until a GU retry has succeeded. | |
369 if (!next_retry_time_.is_null() && | |
370 next_retry_time_ <= sync_cycle_start_time_) { | |
371 current_retry_time_ = next_retry_time_; | |
372 next_retry_time_ = base::TimeTicks(); | |
373 } | |
374 } | |
375 | |
376 void NudgeTracker::SetHintBufferSize(size_t size) { | |
377 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
378 it != type_trackers_.end(); ++it) { | |
379 it->second->UpdatePayloadBufferSize(size); | |
380 } | |
381 } | |
382 | |
383 void NudgeTracker::SetNextRetryTime(base::TimeTicks retry_time) { | |
384 next_retry_time_ = retry_time; | |
385 } | |
386 | |
387 void NudgeTracker::OnReceivedCustomNudgeDelays( | |
388 const std::map<ModelType, base::TimeDelta>& delay_map) { | |
389 for (std::map<ModelType, base::TimeDelta>::const_iterator iter = | |
390 delay_map.begin(); | |
391 iter != delay_map.end(); | |
392 ++iter) { | |
393 ModelType type = iter->first; | |
394 DCHECK(syncer::ProtocolTypes().Has(type)); | |
395 TypeTrackerMap::const_iterator type_iter = type_trackers_.find(type); | |
396 if (type_iter == type_trackers_.end()) | |
397 continue; | |
398 | |
399 if (iter->second > minimum_local_nudge_delay_) { | |
400 type_iter->second->UpdateLocalNudgeDelay(iter->second); | |
401 } else { | |
402 type_iter->second->UpdateLocalNudgeDelay( | |
403 GetDefaultDelayForType(type, | |
404 minimum_local_nudge_delay_)); | |
405 } | |
406 } | |
407 } | |
408 | |
409 void NudgeTracker::SetDefaultNudgeDelay(base::TimeDelta nudge_delay) { | |
410 minimum_local_nudge_delay_ = nudge_delay; | |
411 } | |
412 | |
413 } // namespace sessions | |
414 } // namespace syncer | |
OLD | NEW |