Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / sync / sessions / nudge_tracker.cc
blob5fe3f369acc71f3046278c0470d449ebcf8442ea
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/engine/polling_constants.h"
9 #include "sync/protocol/sync.pb.h"
11 namespace syncer {
12 namespace sessions {
14 namespace {
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;
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;
46 } // namespace
48 size_t NudgeTracker::kDefaultMaxPayloadsPerType = 10;
50 NudgeTracker::NudgeTracker()
51 : invalidations_enabled_(false),
52 invalidations_out_of_sync_(true),
53 minimum_local_nudge_delay_(
54 base::TimeDelta::FromMilliseconds(kDefaultNudgeDelayMilliseconds)),
55 local_refresh_nudge_delay_(
56 base::TimeDelta::FromMilliseconds(kSyncRefreshDelayMilliseconds)),
57 remote_invalidation_nudge_delay_(
58 base::TimeDelta::FromMilliseconds(kSyncSchedulerDelayMilliseconds)) {
59 ModelTypeSet protocol_types = ProtocolTypes();
60 // Default initialize all the type trackers.
61 for (ModelTypeSet::Iterator it = protocol_types.First(); it.Good();
62 it.Inc()) {
63 type_trackers_.insert(it.Get(), make_scoped_ptr(new DataTypeTracker()));
67 NudgeTracker::~NudgeTracker() { }
69 bool NudgeTracker::IsSyncRequired() const {
70 if (IsRetryRequired())
71 return true;
73 for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
74 it != type_trackers_.end(); ++it) {
75 if (it->second->IsSyncRequired()) {
76 return true;
80 return false;
83 bool NudgeTracker::IsGetUpdatesRequired() const {
84 if (invalidations_out_of_sync_)
85 return true;
87 if (IsRetryRequired())
88 return true;
90 for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
91 it != type_trackers_.end(); ++it) {
92 if (it->second->IsGetUpdatesRequired()) {
93 return true;
96 return false;
99 bool NudgeTracker::IsRetryRequired() const {
100 if (sync_cycle_start_time_.is_null())
101 return false;
103 if (current_retry_time_.is_null())
104 return false;
106 return current_retry_time_ <= sync_cycle_start_time_;
109 void NudgeTracker::RecordSuccessfulSyncCycle() {
110 // If a retry was required, we've just serviced it. Unset the flag.
111 if (IsRetryRequired())
112 current_retry_time_ = base::TimeTicks();
114 // A successful cycle while invalidations are enabled puts us back into sync.
115 invalidations_out_of_sync_ = !invalidations_enabled_;
117 for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
118 it != type_trackers_.end(); ++it) {
119 it->second->RecordSuccessfulSyncCycle();
123 base::TimeDelta NudgeTracker::RecordLocalChange(ModelTypeSet types) {
124 // Start with the longest delay.
125 base::TimeDelta delay =
126 base::TimeDelta::FromSeconds(kDefaultShortPollIntervalSeconds);
127 for (ModelTypeSet::Iterator type_it = types.First(); type_it.Good();
128 type_it.Inc()) {
129 TypeTrackerMap::const_iterator tracker_it =
130 type_trackers_.find(type_it.Get());
131 DCHECK(tracker_it != type_trackers_.end());
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_);
140 if (type_delay < delay)
141 delay = type_delay;
143 return delay;
146 base::TimeDelta NudgeTracker::RecordLocalRefreshRequest(ModelTypeSet types) {
147 for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) {
148 TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(it.Get());
149 DCHECK(tracker_it != type_trackers_.end());
150 tracker_it->second->RecordLocalRefreshRequest();
152 return local_refresh_nudge_delay_;
155 base::TimeDelta NudgeTracker::RecordRemoteInvalidation(
156 syncer::ModelType type,
157 scoped_ptr<InvalidationInterface> invalidation) {
158 // Forward the invalidations to the proper recipient.
159 TypeTrackerMap::const_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_;
165 void NudgeTracker::RecordInitialSyncRequired(syncer::ModelType type) {
166 TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(type);
167 DCHECK(tracker_it != type_trackers_.end());
168 tracker_it->second->RecordInitialSyncRequired();
171 void NudgeTracker::RecordCommitConflict(syncer::ModelType type) {
172 TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(type);
173 DCHECK(tracker_it != type_trackers_.end());
174 tracker_it->second->RecordCommitConflict();
177 void NudgeTracker::OnInvalidationsEnabled() {
178 invalidations_enabled_ = true;
181 void NudgeTracker::OnInvalidationsDisabled() {
182 invalidations_enabled_ = false;
183 invalidations_out_of_sync_ = true;
186 void NudgeTracker::SetTypesThrottledUntil(
187 ModelTypeSet types,
188 base::TimeDelta length,
189 base::TimeTicks now) {
190 for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) {
191 TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(it.Get());
192 tracker_it->second->ThrottleType(length, now);
196 void NudgeTracker::UpdateTypeThrottlingState(base::TimeTicks now) {
197 for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
198 it != type_trackers_.end(); ++it) {
199 it->second->UpdateThrottleState(now);
203 bool NudgeTracker::IsAnyTypeThrottled() const {
204 for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
205 it != type_trackers_.end(); ++it) {
206 if (it->second->IsThrottled()) {
207 return true;
210 return false;
213 bool NudgeTracker::IsTypeThrottled(ModelType type) const {
214 DCHECK(type_trackers_.find(type) != type_trackers_.end());
215 return type_trackers_.find(type)->second->IsThrottled();
218 base::TimeDelta NudgeTracker::GetTimeUntilNextUnthrottle(
219 base::TimeTicks now) const {
220 DCHECK(IsAnyTypeThrottled()) << "This function requires a pending unthrottle";
222 // Return min of GetTimeUntilUnthrottle() values for all IsThrottled() types.
223 base::TimeDelta time_until_next_unthrottle = base::TimeDelta::Max();
224 for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
225 it != type_trackers_.end(); ++it) {
226 if (it->second->IsThrottled()) {
227 time_until_next_unthrottle = std::min(
228 time_until_next_unthrottle, it->second->GetTimeUntilUnthrottle(now));
231 DCHECK(!time_until_next_unthrottle.is_max());
233 return time_until_next_unthrottle;
236 ModelTypeSet NudgeTracker::GetThrottledTypes() const {
237 ModelTypeSet result;
238 for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
239 it != type_trackers_.end(); ++it) {
240 if (it->second->IsThrottled()) {
241 result.Put(it->first);
244 return result;
247 ModelTypeSet NudgeTracker::GetNudgedTypes() const {
248 ModelTypeSet result;
249 for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
250 it != type_trackers_.end(); ++it) {
251 if (it->second->HasLocalChangePending()) {
252 result.Put(it->first);
255 return result;
258 ModelTypeSet NudgeTracker::GetNotifiedTypes() const {
259 ModelTypeSet result;
260 for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
261 it != type_trackers_.end(); ++it) {
262 if (it->second->HasPendingInvalidation()) {
263 result.Put(it->first);
266 return result;
269 ModelTypeSet NudgeTracker::GetRefreshRequestedTypes() const {
270 ModelTypeSet result;
271 for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
272 it != type_trackers_.end(); ++it) {
273 if (it->second->HasRefreshRequestPending()) {
274 result.Put(it->first);
277 return result;
280 void NudgeTracker::SetLegacyNotificationHint(
281 ModelType type,
282 sync_pb::DataTypeProgressMarker* progress) const {
283 DCHECK(type_trackers_.find(type) != type_trackers_.end());
284 type_trackers_.find(type)->second->SetLegacyNotificationHint(progress);
287 sync_pb::GetUpdatesCallerInfo::GetUpdatesSource NudgeTracker::GetLegacySource()
288 const {
289 // There's an order to these sources: NOTIFICATION, DATATYPE_REFRESH, LOCAL,
290 // RETRY. The server makes optimization decisions based on this field, so
291 // it's important to get this right. Setting it wrong could lead to missed
292 // updates.
294 // This complexity is part of the reason why we're deprecating 'source' in
295 // favor of 'origin'.
296 bool has_invalidation_pending = false;
297 bool has_refresh_request_pending = false;
298 bool has_commit_pending = false;
299 bool is_initial_sync_required = false;
300 bool has_retry = IsRetryRequired();
302 for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
303 it != type_trackers_.end(); ++it) {
304 const DataTypeTracker& tracker = *it->second;
305 if (!tracker.IsThrottled() && tracker.HasPendingInvalidation()) {
306 has_invalidation_pending = true;
308 if (!tracker.IsThrottled() && tracker.HasRefreshRequestPending()) {
309 has_refresh_request_pending = true;
311 if (!tracker.IsThrottled() && tracker.HasLocalChangePending()) {
312 has_commit_pending = true;
314 if (!tracker.IsThrottled() && tracker.IsInitialSyncRequired()) {
315 is_initial_sync_required = true;
319 if (has_invalidation_pending) {
320 return sync_pb::GetUpdatesCallerInfo::NOTIFICATION;
321 } else if (has_refresh_request_pending) {
322 return sync_pb::GetUpdatesCallerInfo::DATATYPE_REFRESH;
323 } else if (is_initial_sync_required) {
324 // Not quite accurate, but good enough for our purposes. This setting of
325 // SOURCE is just a backward-compatibility hack anyway.
326 return sync_pb::GetUpdatesCallerInfo::DATATYPE_REFRESH;
327 } else if (has_commit_pending) {
328 return sync_pb::GetUpdatesCallerInfo::LOCAL;
329 } else if (has_retry) {
330 return sync_pb::GetUpdatesCallerInfo::RETRY;
331 } else {
332 return sync_pb::GetUpdatesCallerInfo::UNKNOWN;
336 void NudgeTracker::FillProtoMessage(
337 ModelType type,
338 sync_pb::GetUpdateTriggers* msg) const {
339 DCHECK(type_trackers_.find(type) != type_trackers_.end());
341 // Fill what we can from the global data.
342 msg->set_invalidations_out_of_sync(invalidations_out_of_sync_);
344 // Delegate the type-specific work to the DataTypeTracker class.
345 type_trackers_.find(type)->second->FillGetUpdatesTriggersMessage(msg);
348 void NudgeTracker::SetSyncCycleStartTime(base::TimeTicks now) {
349 sync_cycle_start_time_ = now;
351 // If current_retry_time_ is still set, that means we have an old retry time
352 // left over from a previous cycle. For example, maybe we tried to perform
353 // this retry, hit a network connection error, and now we're in exponential
354 // backoff. In that case, we want this sync cycle to include the GU retry
355 // flag so we leave this variable set regardless of whether or not there is an
356 // overwrite pending.
357 if (!current_retry_time_.is_null()) {
358 return;
361 // If do not have a current_retry_time_, but we do have a next_retry_time_ and
362 // it is ready to go, then we set it as the current_retry_time_. It will stay
363 // there until a GU retry has succeeded.
364 if (!next_retry_time_.is_null() &&
365 next_retry_time_ <= sync_cycle_start_time_) {
366 current_retry_time_ = next_retry_time_;
367 next_retry_time_ = base::TimeTicks();
371 void NudgeTracker::SetHintBufferSize(size_t size) {
372 for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
373 it != type_trackers_.end(); ++it) {
374 it->second->UpdatePayloadBufferSize(size);
378 void NudgeTracker::SetNextRetryTime(base::TimeTicks retry_time) {
379 next_retry_time_ = retry_time;
382 void NudgeTracker::OnReceivedCustomNudgeDelays(
383 const std::map<ModelType, base::TimeDelta>& delay_map) {
384 for (std::map<ModelType, base::TimeDelta>::const_iterator iter =
385 delay_map.begin();
386 iter != delay_map.end();
387 ++iter) {
388 ModelType type = iter->first;
389 DCHECK(syncer::ProtocolTypes().Has(type));
390 TypeTrackerMap::const_iterator type_iter = type_trackers_.find(type);
391 if (type_iter == type_trackers_.end())
392 continue;
394 if (iter->second > minimum_local_nudge_delay_) {
395 type_iter->second->UpdateLocalNudgeDelay(iter->second);
396 } else {
397 type_iter->second->UpdateLocalNudgeDelay(
398 GetDefaultDelayForType(type,
399 minimum_local_nudge_delay_));
404 void NudgeTracker::SetDefaultNudgeDelay(base::TimeDelta nudge_delay) {
405 minimum_local_nudge_delay_ = nudge_delay;
408 } // namespace sessions
409 } // namespace syncer