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/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"
16 size_t NudgeTracker::kDefaultMaxPayloadsPerType = 10;
18 NudgeTracker::NudgeTracker()
19 : updates_source_(sync_pb::GetUpdatesCallerInfo::UNKNOWN),
20 invalidations_enabled_(false),
21 invalidations_out_of_sync_(true) {
22 ModelTypeSet protocol_types = ProtocolTypes();
23 // Default initialize all the type trackers.
24 for (ModelTypeSet::Iterator it = protocol_types.First(); it.Good();
26 invalidation::ObjectId id;
27 if (!RealModelTypeToObjectId(it.Get(), &id)) {
30 type_trackers_.insert(std::make_pair(it.Get(), DataTypeTracker(id)));
35 NudgeTracker::~NudgeTracker() { }
37 bool NudgeTracker::IsSyncRequired() const {
38 for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
39 it != type_trackers_.end(); ++it) {
40 if (it->second.IsSyncRequired()) {
47 bool NudgeTracker::IsGetUpdatesRequired() const {
48 if (invalidations_out_of_sync_)
51 if (IsRetryRequired())
54 for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
55 it != type_trackers_.end(); ++it) {
56 if (it->second.IsGetUpdatesRequired()) {
63 bool NudgeTracker::IsRetryRequired() const {
64 if (sync_cycle_start_time_.is_null())
67 if (current_retry_time_.is_null())
70 return current_retry_time_ < sync_cycle_start_time_;
73 void NudgeTracker::RecordSuccessfulSyncCycle() {
74 updates_source_ = sync_pb::GetUpdatesCallerInfo::UNKNOWN;
76 // If a retry was required, we've just serviced it. Unset the flag.
77 if (IsRetryRequired())
78 current_retry_time_ = base::TimeTicks();
80 // A successful cycle while invalidations are enabled puts us back into sync.
81 invalidations_out_of_sync_ = !invalidations_enabled_;
83 for (TypeTrackerMap::iterator it = type_trackers_.begin();
84 it != type_trackers_.end(); ++it) {
85 it->second.RecordSuccessfulSyncCycle();
89 void NudgeTracker::RecordLocalChange(ModelTypeSet types) {
90 // Don't overwrite an NOTIFICATION or DATATYPE_REFRESH source. The server
91 // makes some assumptions about the source; overriding these sources with
92 // LOCAL could lead to incorrect behaviour. This is part of the reason why
93 // we're deprecating 'source' in favor of 'origin'.
94 if (updates_source_ != sync_pb::GetUpdatesCallerInfo::NOTIFICATION
95 && updates_source_ != sync_pb::GetUpdatesCallerInfo::DATATYPE_REFRESH) {
96 updates_source_ = sync_pb::GetUpdatesCallerInfo::LOCAL;
99 for (ModelTypeSet::Iterator type_it = types.First(); type_it.Good();
101 TypeTrackerMap::iterator tracker_it = type_trackers_.find(type_it.Get());
102 DCHECK(tracker_it != type_trackers_.end());
103 tracker_it->second.RecordLocalChange();
107 void NudgeTracker::RecordLocalRefreshRequest(ModelTypeSet types) {
108 // Don't overwrite an NOTIFICATION source. The server makes some assumptions
109 // about the source. Overriding this source with LOCAL could lead to
110 // incorrect behaviour. This is part of the reason why we're deprecating
111 // 'source' in favor of 'origin'.
112 if (updates_source_ != sync_pb::GetUpdatesCallerInfo::NOTIFICATION) {
113 updates_source_ = sync_pb::GetUpdatesCallerInfo::DATATYPE_REFRESH;
116 for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) {
117 TypeTrackerMap::iterator tracker_it = type_trackers_.find(it.Get());
118 DCHECK(tracker_it != type_trackers_.end());
119 tracker_it->second.RecordLocalRefreshRequest();
123 void NudgeTracker::RecordRemoteInvalidation(
124 const ObjectIdInvalidationMap& invalidation_map) {
125 updates_source_ = sync_pb::GetUpdatesCallerInfo::NOTIFICATION;
127 // Be very careful here. The invalidations acknowledgement system requires a
128 // sort of manual memory management. We'll leak a small amount of memory if
129 // we fail to acknowledge or drop any of these incoming invalidations.
131 ObjectIdSet id_set = invalidation_map.GetObjectIds();
132 for (ObjectIdSet::iterator it = id_set.begin(); it != id_set.end(); ++it) {
135 // This should never happen. If it does, we'll start to leak memory.
136 if (!ObjectIdToRealModelType(*it, &type)) {
138 << "Object ID " << ObjectIdToString(*it)
139 << " does not map to valid model type";
143 // Forward the invalidations to the proper recipient.
144 TypeTrackerMap::iterator tracker_it = type_trackers_.find(type);
145 DCHECK(tracker_it != type_trackers_.end());
146 tracker_it->second.RecordRemoteInvalidations(
147 invalidation_map.ForObject(*it));
151 void NudgeTracker::OnInvalidationsEnabled() {
152 invalidations_enabled_ = true;
155 void NudgeTracker::OnInvalidationsDisabled() {
156 invalidations_enabled_ = false;
157 invalidations_out_of_sync_ = true;
160 void NudgeTracker::SetTypesThrottledUntil(
162 base::TimeDelta length,
163 base::TimeTicks now) {
164 for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) {
165 TypeTrackerMap::iterator tracker_it = type_trackers_.find(it.Get());
166 tracker_it->second.ThrottleType(length, now);
170 void NudgeTracker::UpdateTypeThrottlingState(base::TimeTicks now) {
171 for (TypeTrackerMap::iterator it = type_trackers_.begin();
172 it != type_trackers_.end(); ++it) {
173 it->second.UpdateThrottleState(now);
177 bool NudgeTracker::IsAnyTypeThrottled() const {
178 for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
179 it != type_trackers_.end(); ++it) {
180 if (it->second.IsThrottled()) {
187 bool NudgeTracker::IsTypeThrottled(ModelType type) const {
188 DCHECK(type_trackers_.find(type) != type_trackers_.end());
189 return type_trackers_.find(type)->second.IsThrottled();
192 base::TimeDelta NudgeTracker::GetTimeUntilNextUnthrottle(
193 base::TimeTicks now) const {
194 DCHECK(IsAnyTypeThrottled()) << "This function requires a pending unthrottle";
195 const base::TimeDelta kMaxTimeDelta =
196 base::TimeDelta::FromInternalValue(kint64max);
198 // Return min of GetTimeUntilUnthrottle() values for all IsThrottled() types.
199 base::TimeDelta time_until_next_unthrottle = kMaxTimeDelta;
200 for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
201 it != type_trackers_.end(); ++it) {
202 if (it->second.IsThrottled()) {
203 time_until_next_unthrottle =
204 std::min(time_until_next_unthrottle,
205 it->second.GetTimeUntilUnthrottle(now));
208 DCHECK(kMaxTimeDelta != time_until_next_unthrottle);
210 return time_until_next_unthrottle;
213 ModelTypeSet NudgeTracker::GetThrottledTypes() const {
215 for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
216 it != type_trackers_.end(); ++it) {
217 if (it->second.IsThrottled()) {
218 result.Put(it->first);
224 void NudgeTracker::SetLegacyNotificationHint(
226 sync_pb::DataTypeProgressMarker* progress) const {
227 DCHECK(type_trackers_.find(type) != type_trackers_.end());
228 type_trackers_.find(type)->second.SetLegacyNotificationHint(progress);
231 sync_pb::GetUpdatesCallerInfo::GetUpdatesSource NudgeTracker::updates_source()
233 return updates_source_;
236 void NudgeTracker::FillProtoMessage(
238 sync_pb::GetUpdateTriggers* msg) const {
239 DCHECK(type_trackers_.find(type) != type_trackers_.end());
241 // Fill what we can from the global data.
242 msg->set_invalidations_out_of_sync(invalidations_out_of_sync_);
244 // Delegate the type-specific work to the DataTypeTracker class.
245 type_trackers_.find(type)->second.FillGetUpdatesTriggersMessage(msg);
248 void NudgeTracker::SetSyncCycleStartTime(base::TimeTicks now) {
249 sync_cycle_start_time_ = now;
251 // If current_retry_time_ is still set, that means we have an old retry time
252 // left over from a previous cycle. For example, maybe we tried to perform
253 // this retry, hit a network connection error, and now we're in exponential
254 // backoff. In that case, we want this sync cycle to include the GU retry
255 // flag so we leave this variable set regardless of whether or not there is an
256 // overwrite pending.
257 if (!current_retry_time_.is_null()) {
261 // If do not have a current_retry_time_, but we do have a next_retry_time_ and
262 // it is ready to go, then we set it as the current_retry_time_. It will stay
263 // there until a GU retry has succeeded.
264 if (!next_retry_time_.is_null() &&
265 next_retry_time_ < sync_cycle_start_time_) {
266 current_retry_time_ = next_retry_time_;
267 next_retry_time_ = base::TimeTicks();
271 void NudgeTracker::SetHintBufferSize(size_t size) {
272 for (TypeTrackerMap::iterator it = type_trackers_.begin();
273 it != type_trackers_.end(); ++it) {
274 it->second.UpdatePayloadBufferSize(size);
278 void NudgeTracker::SetNextRetryTime(base::TimeTicks retry_time) {
279 next_retry_time_ = retry_time;
282 } // namespace sessions
283 } // namespace syncer