Roll leveldb 3f7758:803d69 (v1.17 -> v1.18)
[chromium-blink-merge.git] / sync / sessions / nudge_tracker.cc
blob9c62b07aab015dc5e9341e99a8613a870983d740
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 : 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()));
68 NudgeTracker::~NudgeTracker() { }
70 bool NudgeTracker::IsSyncRequired() const {
71 if (IsRetryRequired())
72 return true;
74 for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
75 it != type_trackers_.end(); ++it) {
76 if (it->second->IsSyncRequired()) {
77 return true;
81 return false;
84 bool NudgeTracker::IsGetUpdatesRequired() const {
85 if (invalidations_out_of_sync_)
86 return true;
88 if (IsRetryRequired())
89 return true;
91 for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
92 it != type_trackers_.end(); ++it) {
93 if (it->second->IsGetUpdatesRequired()) {
94 return true;
97 return false;
100 bool NudgeTracker::IsRetryRequired() const {
101 if (sync_cycle_start_time_.is_null())
102 return false;
104 if (current_retry_time_.is_null())
105 return false;
107 return current_retry_time_ <= sync_cycle_start_time_;
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();
115 // A successful cycle while invalidations are enabled puts us back into sync.
116 invalidations_out_of_sync_ = !invalidations_enabled_;
118 for (TypeTrackerMap::iterator it = type_trackers_.begin();
119 it != type_trackers_.end(); ++it) {
120 it->second->RecordSuccessfulSyncCycle();
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());
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::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::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::iterator tracker_it = type_trackers_.find(type);
167 DCHECK(tracker_it != type_trackers_.end());
168 tracker_it->second->RecordInitialSyncRequired();
171 void NudgeTracker::OnInvalidationsEnabled() {
172 invalidations_enabled_ = true;
175 void NudgeTracker::OnInvalidationsDisabled() {
176 invalidations_enabled_ = false;
177 invalidations_out_of_sync_ = true;
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);
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);
197 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;
204 return false;
207 bool NudgeTracker::IsTypeThrottled(ModelType type) const {
208 DCHECK(type_trackers_.find(type) != type_trackers_.end());
209 return type_trackers_.find(type)->second->IsThrottled();
212 base::TimeDelta NudgeTracker::GetTimeUntilNextUnthrottle(
213 base::TimeTicks now) const {
214 DCHECK(IsAnyTypeThrottled()) << "This function requires a pending unthrottle";
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));
225 DCHECK(!time_until_next_unthrottle.is_max());
227 return time_until_next_unthrottle;
230 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);
238 return result;
241 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);
249 return result;
252 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);
260 return result;
263 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);
271 return result;
274 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);
281 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.
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();
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;
302 if (!tracker.IsThrottled() && tracker.HasRefreshRequestPending()) {
303 has_refresh_request_pending = true;
305 if (!tracker.IsThrottled() && tracker.HasLocalChangePending()) {
306 has_commit_pending = true;
308 if (!tracker.IsThrottled() && tracker.IsInitialSyncRequired()) {
309 is_initial_sync_required = true;
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;
330 void NudgeTracker::FillProtoMessage(
331 ModelType type,
332 sync_pb::GetUpdateTriggers* msg) const {
333 DCHECK(type_trackers_.find(type) != type_trackers_.end());
335 // Fill what we can from the global data.
336 msg->set_invalidations_out_of_sync(invalidations_out_of_sync_);
338 // Delegate the type-specific work to the DataTypeTracker class.
339 type_trackers_.find(type)->second->FillGetUpdatesTriggersMessage(msg);
342 void NudgeTracker::SetSyncCycleStartTime(base::TimeTicks now) {
343 sync_cycle_start_time_ = now;
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;
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();
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);
372 void NudgeTracker::SetNextRetryTime(base::TimeTicks retry_time) {
373 next_retry_time_ = retry_time;
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;
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_));
398 void NudgeTracker::SetDefaultNudgeDelay(base::TimeDelta nudge_delay) {
399 minimum_local_nudge_delay_ = nudge_delay;
402 } // namespace sessions
403 } // namespace syncer