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.
5 #include "sync/sessions/nudge_tracker.h"
7 #include "base/basictypes.h"
8 #include "sync/protocol/sync.pb.h"
13 size_t NudgeTracker::kDefaultMaxPayloadsPerType
= 10;
15 NudgeTracker::NudgeTracker()
16 : type_tracker_deleter_(&type_trackers_
),
17 invalidations_enabled_(false),
18 invalidations_out_of_sync_(true) {
19 ModelTypeSet protocol_types
= ProtocolTypes();
20 // Default initialize all the type trackers.
21 for (ModelTypeSet::Iterator it
= protocol_types
.First(); it
.Good();
23 type_trackers_
.insert(std::make_pair(it
.Get(), new DataTypeTracker()));
27 NudgeTracker::~NudgeTracker() { }
29 bool NudgeTracker::IsSyncRequired() const {
30 if (IsRetryRequired())
33 for (TypeTrackerMap::const_iterator it
= type_trackers_
.begin();
34 it
!= type_trackers_
.end(); ++it
) {
35 if (it
->second
->IsSyncRequired()) {
43 bool NudgeTracker::IsGetUpdatesRequired() const {
44 if (invalidations_out_of_sync_
)
47 if (IsRetryRequired())
50 for (TypeTrackerMap::const_iterator it
= type_trackers_
.begin();
51 it
!= type_trackers_
.end(); ++it
) {
52 if (it
->second
->IsGetUpdatesRequired()) {
59 bool NudgeTracker::IsRetryRequired() const {
60 if (sync_cycle_start_time_
.is_null())
63 if (current_retry_time_
.is_null())
66 return current_retry_time_
< sync_cycle_start_time_
;
69 void NudgeTracker::RecordSuccessfulSyncCycle() {
70 // If a retry was required, we've just serviced it. Unset the flag.
71 if (IsRetryRequired())
72 current_retry_time_
= base::TimeTicks();
74 // A successful cycle while invalidations are enabled puts us back into sync.
75 invalidations_out_of_sync_
= !invalidations_enabled_
;
77 for (TypeTrackerMap::iterator it
= type_trackers_
.begin();
78 it
!= type_trackers_
.end(); ++it
) {
79 it
->second
->RecordSuccessfulSyncCycle();
83 void NudgeTracker::RecordLocalChange(ModelTypeSet types
) {
84 for (ModelTypeSet::Iterator type_it
= types
.First(); type_it
.Good();
86 TypeTrackerMap::iterator tracker_it
= type_trackers_
.find(type_it
.Get());
87 DCHECK(tracker_it
!= type_trackers_
.end());
88 tracker_it
->second
->RecordLocalChange();
92 void NudgeTracker::RecordLocalRefreshRequest(ModelTypeSet types
) {
93 for (ModelTypeSet::Iterator it
= types
.First(); it
.Good(); it
.Inc()) {
94 TypeTrackerMap::iterator tracker_it
= type_trackers_
.find(it
.Get());
95 DCHECK(tracker_it
!= type_trackers_
.end());
96 tracker_it
->second
->RecordLocalRefreshRequest();
100 void NudgeTracker::RecordRemoteInvalidation(
101 syncer::ModelType type
,
102 scoped_ptr
<InvalidationInterface
> invalidation
) {
103 // Forward the invalidations to the proper recipient.
104 TypeTrackerMap::iterator tracker_it
= type_trackers_
.find(type
);
105 DCHECK(tracker_it
!= type_trackers_
.end());
106 tracker_it
->second
->RecordRemoteInvalidation(invalidation
.Pass());
109 void NudgeTracker::OnInvalidationsEnabled() {
110 invalidations_enabled_
= true;
113 void NudgeTracker::OnInvalidationsDisabled() {
114 invalidations_enabled_
= false;
115 invalidations_out_of_sync_
= true;
118 void NudgeTracker::SetTypesThrottledUntil(
120 base::TimeDelta length
,
121 base::TimeTicks now
) {
122 for (ModelTypeSet::Iterator it
= types
.First(); it
.Good(); it
.Inc()) {
123 TypeTrackerMap::iterator tracker_it
= type_trackers_
.find(it
.Get());
124 tracker_it
->second
->ThrottleType(length
, now
);
128 void NudgeTracker::UpdateTypeThrottlingState(base::TimeTicks now
) {
129 for (TypeTrackerMap::iterator it
= type_trackers_
.begin();
130 it
!= type_trackers_
.end(); ++it
) {
131 it
->second
->UpdateThrottleState(now
);
135 bool NudgeTracker::IsAnyTypeThrottled() const {
136 for (TypeTrackerMap::const_iterator it
= type_trackers_
.begin();
137 it
!= type_trackers_
.end(); ++it
) {
138 if (it
->second
->IsThrottled()) {
145 bool NudgeTracker::IsTypeThrottled(ModelType type
) const {
146 DCHECK(type_trackers_
.find(type
) != type_trackers_
.end());
147 return type_trackers_
.find(type
)->second
->IsThrottled();
150 base::TimeDelta
NudgeTracker::GetTimeUntilNextUnthrottle(
151 base::TimeTicks now
) const {
152 DCHECK(IsAnyTypeThrottled()) << "This function requires a pending unthrottle";
154 // Return min of GetTimeUntilUnthrottle() values for all IsThrottled() types.
155 base::TimeDelta time_until_next_unthrottle
= base::TimeDelta::Max();
156 for (TypeTrackerMap::const_iterator it
= type_trackers_
.begin();
157 it
!= type_trackers_
.end(); ++it
) {
158 if (it
->second
->IsThrottled()) {
159 time_until_next_unthrottle
= std::min(
160 time_until_next_unthrottle
, it
->second
->GetTimeUntilUnthrottle(now
));
163 DCHECK(!time_until_next_unthrottle
.is_max());
165 return time_until_next_unthrottle
;
168 ModelTypeSet
NudgeTracker::GetThrottledTypes() const {
170 for (TypeTrackerMap::const_iterator it
= type_trackers_
.begin();
171 it
!= type_trackers_
.end(); ++it
) {
172 if (it
->second
->IsThrottled()) {
173 result
.Put(it
->first
);
179 ModelTypeSet
NudgeTracker::GetNudgedTypes() const {
181 for (TypeTrackerMap::const_iterator it
= type_trackers_
.begin();
182 it
!= type_trackers_
.end(); ++it
) {
183 if (it
->second
->HasLocalChangePending()) {
184 result
.Put(it
->first
);
190 ModelTypeSet
NudgeTracker::GetNotifiedTypes() const {
192 for (TypeTrackerMap::const_iterator it
= type_trackers_
.begin();
193 it
!= type_trackers_
.end(); ++it
) {
194 if (it
->second
->HasPendingInvalidation()) {
195 result
.Put(it
->first
);
201 ModelTypeSet
NudgeTracker::GetRefreshRequestedTypes() const {
203 for (TypeTrackerMap::const_iterator it
= type_trackers_
.begin();
204 it
!= type_trackers_
.end(); ++it
) {
205 if (it
->second
->HasRefreshRequestPending()) {
206 result
.Put(it
->first
);
212 void NudgeTracker::SetLegacyNotificationHint(
214 sync_pb::DataTypeProgressMarker
* progress
) const {
215 DCHECK(type_trackers_
.find(type
) != type_trackers_
.end());
216 type_trackers_
.find(type
)->second
->SetLegacyNotificationHint(progress
);
219 sync_pb::GetUpdatesCallerInfo::GetUpdatesSource
NudgeTracker::GetLegacySource()
221 // There's an order to these sources: NOTIFICATION, DATATYPE_REFRESH, LOCAL,
222 // RETRY. The server makes optimization decisions based on this field, so
223 // it's important to get this right. Setting it wrong could lead to missed
226 // This complexity is part of the reason why we're deprecating 'source' in
227 // favor of 'origin'.
228 bool has_invalidation_pending
= false;
229 bool has_refresh_request_pending
= false;
230 bool has_commit_pending
= false;
231 bool has_retry
= IsRetryRequired();
233 for (TypeTrackerMap::const_iterator it
= type_trackers_
.begin();
234 it
!= type_trackers_
.end(); ++it
) {
235 const DataTypeTracker
& tracker
= *it
->second
;
236 if (!tracker
.IsThrottled() && tracker
.HasPendingInvalidation()) {
237 has_invalidation_pending
= true;
239 if (!tracker
.IsThrottled() && tracker
.HasRefreshRequestPending()) {
240 has_refresh_request_pending
= true;
242 if (!tracker
.IsThrottled() && tracker
.HasLocalChangePending()) {
243 has_commit_pending
= true;
247 if (has_invalidation_pending
) {
248 return sync_pb::GetUpdatesCallerInfo::NOTIFICATION
;
249 } else if (has_refresh_request_pending
) {
250 return sync_pb::GetUpdatesCallerInfo::DATATYPE_REFRESH
;
251 } else if (has_commit_pending
) {
252 return sync_pb::GetUpdatesCallerInfo::LOCAL
;
253 } else if (has_retry
) {
254 return sync_pb::GetUpdatesCallerInfo::RETRY
;
256 return sync_pb::GetUpdatesCallerInfo::UNKNOWN
;
260 void NudgeTracker::FillProtoMessage(
262 sync_pb::GetUpdateTriggers
* msg
) const {
263 DCHECK(type_trackers_
.find(type
) != type_trackers_
.end());
265 // Fill what we can from the global data.
266 msg
->set_invalidations_out_of_sync(invalidations_out_of_sync_
);
268 // Delegate the type-specific work to the DataTypeTracker class.
269 type_trackers_
.find(type
)->second
->FillGetUpdatesTriggersMessage(msg
);
272 void NudgeTracker::SetSyncCycleStartTime(base::TimeTicks now
) {
273 sync_cycle_start_time_
= now
;
275 // If current_retry_time_ is still set, that means we have an old retry time
276 // left over from a previous cycle. For example, maybe we tried to perform
277 // this retry, hit a network connection error, and now we're in exponential
278 // backoff. In that case, we want this sync cycle to include the GU retry
279 // flag so we leave this variable set regardless of whether or not there is an
280 // overwrite pending.
281 if (!current_retry_time_
.is_null()) {
285 // If do not have a current_retry_time_, but we do have a next_retry_time_ and
286 // it is ready to go, then we set it as the current_retry_time_. It will stay
287 // there until a GU retry has succeeded.
288 if (!next_retry_time_
.is_null() &&
289 next_retry_time_
< sync_cycle_start_time_
) {
290 current_retry_time_
= next_retry_time_
;
291 next_retry_time_
= base::TimeTicks();
295 void NudgeTracker::SetHintBufferSize(size_t size
) {
296 for (TypeTrackerMap::iterator it
= type_trackers_
.begin();
297 it
!= type_trackers_
.end(); ++it
) {
298 it
->second
->UpdatePayloadBufferSize(size
);
302 void NudgeTracker::SetNextRetryTime(base::TimeTicks retry_time
) {
303 next_retry_time_
= retry_time
;
306 } // namespace sessions
307 } // namespace syncer