• 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/base/invalidation.h"
9 #include "sync/notifier/invalidation_util.h"
10 #include "sync/notifier/object_id_invalidation_map.h"
11 #include "sync/protocol/sync.pb.h"
12 
13 namespace syncer {
14 namespace sessions {
15 
16 size_t NudgeTracker::kDefaultMaxPayloadsPerType = 10;
17 
NudgeTracker()18 NudgeTracker::NudgeTracker()
19     : invalidations_enabled_(false),
20       invalidations_out_of_sync_(true) {
21   ModelTypeSet protocol_types = ProtocolTypes();
22   // Default initialize all the type trackers.
23   for (ModelTypeSet::Iterator it = protocol_types.First(); it.Good();
24        it.Inc()) {
25     invalidation::ObjectId id;
26     if (!RealModelTypeToObjectId(it.Get(), &id)) {
27       NOTREACHED();
28     } else {
29       type_trackers_.insert(std::make_pair(it.Get(), DataTypeTracker(id)));
30     }
31   }
32 }
33 
~NudgeTracker()34 NudgeTracker::~NudgeTracker() { }
35 
IsSyncRequired() const36 bool NudgeTracker::IsSyncRequired() const {
37   if (IsRetryRequired())
38     return true;
39 
40   for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
41        it != type_trackers_.end(); ++it) {
42     if (it->second.IsSyncRequired()) {
43       return true;
44     }
45   }
46 
47   return false;
48 }
49 
IsGetUpdatesRequired() const50 bool NudgeTracker::IsGetUpdatesRequired() const {
51   if (invalidations_out_of_sync_)
52     return true;
53 
54   if (IsRetryRequired())
55     return true;
56 
57   for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
58        it != type_trackers_.end(); ++it) {
59     if (it->second.IsGetUpdatesRequired()) {
60       return true;
61     }
62   }
63   return false;
64 }
65 
IsRetryRequired() const66 bool NudgeTracker::IsRetryRequired() const {
67   if (sync_cycle_start_time_.is_null())
68     return false;
69 
70   if (current_retry_time_.is_null())
71     return false;
72 
73   return current_retry_time_ < sync_cycle_start_time_;
74 }
75 
RecordSuccessfulSyncCycle()76 void NudgeTracker::RecordSuccessfulSyncCycle() {
77   // If a retry was required, we've just serviced it.  Unset the flag.
78   if (IsRetryRequired())
79     current_retry_time_ = base::TimeTicks();
80 
81   // A successful cycle while invalidations are enabled puts us back into sync.
82   invalidations_out_of_sync_ = !invalidations_enabled_;
83 
84   for (TypeTrackerMap::iterator it = type_trackers_.begin();
85        it != type_trackers_.end(); ++it) {
86     it->second.RecordSuccessfulSyncCycle();
87   }
88 }
89 
RecordLocalChange(ModelTypeSet types)90 void NudgeTracker::RecordLocalChange(ModelTypeSet types) {
91   for (ModelTypeSet::Iterator type_it = types.First(); type_it.Good();
92        type_it.Inc()) {
93     TypeTrackerMap::iterator tracker_it = type_trackers_.find(type_it.Get());
94     DCHECK(tracker_it != type_trackers_.end());
95     tracker_it->second.RecordLocalChange();
96   }
97 }
98 
RecordLocalRefreshRequest(ModelTypeSet types)99 void NudgeTracker::RecordLocalRefreshRequest(ModelTypeSet types) {
100   for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) {
101     TypeTrackerMap::iterator tracker_it = type_trackers_.find(it.Get());
102     DCHECK(tracker_it != type_trackers_.end());
103     tracker_it->second.RecordLocalRefreshRequest();
104   }
105 }
106 
RecordRemoteInvalidation(const ObjectIdInvalidationMap & invalidation_map)107 void NudgeTracker::RecordRemoteInvalidation(
108     const ObjectIdInvalidationMap& invalidation_map) {
109   // Be very careful here.  The invalidations acknowledgement system requires a
110   // sort of manual memory management.  We'll leak a small amount of memory if
111   // we fail to acknowledge or drop any of these incoming invalidations.
112 
113   ObjectIdSet id_set = invalidation_map.GetObjectIds();
114   for (ObjectIdSet::iterator it = id_set.begin(); it != id_set.end(); ++it) {
115     ModelType type;
116 
117     // This should never happen.  If it does, we'll start to leak memory.
118     if (!ObjectIdToRealModelType(*it, &type)) {
119       NOTREACHED()
120           << "Object ID " << ObjectIdToString(*it)
121           << " does not map to valid model type";
122       continue;
123     }
124 
125     // Forward the invalidations to the proper recipient.
126     TypeTrackerMap::iterator tracker_it = type_trackers_.find(type);
127     DCHECK(tracker_it != type_trackers_.end());
128     tracker_it->second.RecordRemoteInvalidations(
129         invalidation_map.ForObject(*it));
130   }
131 }
132 
OnInvalidationsEnabled()133 void NudgeTracker::OnInvalidationsEnabled() {
134   invalidations_enabled_ = true;
135 }
136 
OnInvalidationsDisabled()137 void NudgeTracker::OnInvalidationsDisabled() {
138   invalidations_enabled_ = false;
139   invalidations_out_of_sync_ = true;
140 }
141 
SetTypesThrottledUntil(ModelTypeSet types,base::TimeDelta length,base::TimeTicks now)142 void NudgeTracker::SetTypesThrottledUntil(
143     ModelTypeSet types,
144     base::TimeDelta length,
145     base::TimeTicks now) {
146   for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) {
147     TypeTrackerMap::iterator tracker_it = type_trackers_.find(it.Get());
148     tracker_it->second.ThrottleType(length, now);
149   }
150 }
151 
UpdateTypeThrottlingState(base::TimeTicks now)152 void NudgeTracker::UpdateTypeThrottlingState(base::TimeTicks now) {
153   for (TypeTrackerMap::iterator it = type_trackers_.begin();
154        it != type_trackers_.end(); ++it) {
155     it->second.UpdateThrottleState(now);
156   }
157 }
158 
IsAnyTypeThrottled() const159 bool NudgeTracker::IsAnyTypeThrottled() const {
160   for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
161        it != type_trackers_.end(); ++it) {
162     if (it->second.IsThrottled()) {
163       return true;
164     }
165   }
166   return false;
167 }
168 
IsTypeThrottled(ModelType type) const169 bool NudgeTracker::IsTypeThrottled(ModelType type) const {
170   DCHECK(type_trackers_.find(type) != type_trackers_.end());
171   return type_trackers_.find(type)->second.IsThrottled();
172 }
173 
GetTimeUntilNextUnthrottle(base::TimeTicks now) const174 base::TimeDelta NudgeTracker::GetTimeUntilNextUnthrottle(
175     base::TimeTicks now) const {
176   DCHECK(IsAnyTypeThrottled()) << "This function requires a pending unthrottle";
177 
178   // Return min of GetTimeUntilUnthrottle() values for all IsThrottled() types.
179   base::TimeDelta time_until_next_unthrottle = base::TimeDelta::Max();
180   for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
181        it != type_trackers_.end(); ++it) {
182     if (it->second.IsThrottled()) {
183       time_until_next_unthrottle =
184           std::min(time_until_next_unthrottle,
185                    it->second.GetTimeUntilUnthrottle(now));
186     }
187   }
188   DCHECK(!time_until_next_unthrottle.is_max());
189 
190   return time_until_next_unthrottle;
191 }
192 
GetThrottledTypes() const193 ModelTypeSet NudgeTracker::GetThrottledTypes() const {
194   ModelTypeSet result;
195   for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
196        it != type_trackers_.end(); ++it) {
197     if (it->second.IsThrottled()) {
198       result.Put(it->first);
199     }
200   }
201   return result;
202 }
203 
GetNudgedTypes() const204 ModelTypeSet NudgeTracker::GetNudgedTypes() const {
205   ModelTypeSet result;
206   for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
207        it != type_trackers_.end(); ++it) {
208     if (it->second.HasLocalChangePending()) {
209       result.Put(it->first);
210     }
211   }
212   return result;
213 }
214 
GetNotifiedTypes() const215 ModelTypeSet NudgeTracker::GetNotifiedTypes() const {
216   ModelTypeSet result;
217   for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
218        it != type_trackers_.end(); ++it) {
219     if (it->second.HasPendingInvalidation()) {
220       result.Put(it->first);
221     }
222   }
223   return result;
224 }
225 
GetRefreshRequestedTypes() const226 ModelTypeSet NudgeTracker::GetRefreshRequestedTypes() const {
227   ModelTypeSet result;
228   for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
229        it != type_trackers_.end(); ++it) {
230     if (it->second.HasRefreshRequestPending()) {
231       result.Put(it->first);
232     }
233   }
234   return result;
235 }
236 
SetLegacyNotificationHint(ModelType type,sync_pb::DataTypeProgressMarker * progress) const237 void NudgeTracker::SetLegacyNotificationHint(
238     ModelType type,
239     sync_pb::DataTypeProgressMarker* progress) const {
240   DCHECK(type_trackers_.find(type) != type_trackers_.end());
241   type_trackers_.find(type)->second.SetLegacyNotificationHint(progress);
242 }
243 
GetLegacySource() const244 sync_pb::GetUpdatesCallerInfo::GetUpdatesSource NudgeTracker::GetLegacySource()
245     const {
246   // There's an order to these sources: NOTIFICATION, DATATYPE_REFRESH, LOCAL,
247   // RETRY.  The server makes optimization decisions based on this field, so
248   // it's important to get this right.  Setting it wrong could lead to missed
249   // updates.
250   //
251   // This complexity is part of the reason why we're deprecating 'source' in
252   // favor of 'origin'.
253   bool has_invalidation_pending = false;
254   bool has_refresh_request_pending = false;
255   bool has_commit_pending = false;
256   bool has_retry = IsRetryRequired();
257 
258   for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
259        it != type_trackers_.end(); ++it) {
260     const DataTypeTracker& tracker = it->second;
261     if (!tracker.IsThrottled() && tracker.HasPendingInvalidation()) {
262       has_invalidation_pending = true;
263     }
264     if (!tracker.IsThrottled() && tracker.HasRefreshRequestPending()) {
265       has_refresh_request_pending = true;
266     }
267     if (!tracker.IsThrottled() && tracker.HasLocalChangePending()) {
268       has_commit_pending = true;
269     }
270   }
271 
272   if (has_invalidation_pending) {
273     return sync_pb::GetUpdatesCallerInfo::NOTIFICATION;
274   } else if (has_refresh_request_pending) {
275     return sync_pb::GetUpdatesCallerInfo::DATATYPE_REFRESH;
276   } else if (has_commit_pending) {
277     return sync_pb::GetUpdatesCallerInfo::LOCAL;
278   } else if (has_retry) {
279     return sync_pb::GetUpdatesCallerInfo::RETRY;
280   } else {
281     return sync_pb::GetUpdatesCallerInfo::UNKNOWN;
282   }
283 }
284 
FillProtoMessage(ModelType type,sync_pb::GetUpdateTriggers * msg) const285 void NudgeTracker::FillProtoMessage(
286     ModelType type,
287     sync_pb::GetUpdateTriggers* msg) const {
288   DCHECK(type_trackers_.find(type) != type_trackers_.end());
289 
290   // Fill what we can from the global data.
291   msg->set_invalidations_out_of_sync(invalidations_out_of_sync_);
292 
293   // Delegate the type-specific work to the DataTypeTracker class.
294   type_trackers_.find(type)->second.FillGetUpdatesTriggersMessage(msg);
295 }
296 
SetSyncCycleStartTime(base::TimeTicks now)297 void NudgeTracker::SetSyncCycleStartTime(base::TimeTicks now) {
298   sync_cycle_start_time_ = now;
299 
300   // If current_retry_time_ is still set, that means we have an old retry time
301   // left over from a previous cycle.  For example, maybe we tried to perform
302   // this retry, hit a network connection error, and now we're in exponential
303   // backoff.  In that case, we want this sync cycle to include the GU retry
304   // flag so we leave this variable set regardless of whether or not there is an
305   // overwrite pending.
306   if (!current_retry_time_.is_null()) {
307     return;
308   }
309 
310   // If do not have a current_retry_time_, but we do have a next_retry_time_ and
311   // it is ready to go, then we set it as the current_retry_time_.  It will stay
312   // there until a GU retry has succeeded.
313   if (!next_retry_time_.is_null() &&
314       next_retry_time_ < sync_cycle_start_time_) {
315     current_retry_time_ = next_retry_time_;
316     next_retry_time_ = base::TimeTicks();
317   }
318 }
319 
SetHintBufferSize(size_t size)320 void NudgeTracker::SetHintBufferSize(size_t size) {
321   for (TypeTrackerMap::iterator it = type_trackers_.begin();
322        it != type_trackers_.end(); ++it) {
323     it->second.UpdatePayloadBufferSize(size);
324   }
325 }
326 
SetNextRetryTime(base::TimeTicks retry_time)327 void NudgeTracker::SetNextRetryTime(base::TimeTicks retry_time) {
328   next_retry_time_ = retry_time;
329 }
330 
331 }  // namespace sessions
332 }  // namespace syncer
333