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