| OLD | NEW |
| (Empty) |
| 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 <stddef.h> | |
| 8 | |
| 9 #include <utility> | |
| 10 | |
| 11 #include "base/memory/ptr_util.h" | |
| 12 #include "sync/internal_api/public/engine/polling_constants.h" | |
| 13 #include "sync/protocol/sync.pb.h" | |
| 14 | |
| 15 namespace syncer { | |
| 16 namespace sessions { | |
| 17 | |
| 18 namespace { | |
| 19 | |
| 20 // Delays for syncer nudges. | |
| 21 const int kDefaultNudgeDelayMilliseconds = 200; | |
| 22 const int kSlowNudgeDelayMilliseconds = 2000; | |
| 23 const int kDefaultSessionsCommitDelaySeconds = 10; | |
| 24 const int kSyncRefreshDelayMilliseconds = 500; | |
| 25 const int kSyncSchedulerDelayMilliseconds = 250; | |
| 26 | |
| 27 base::TimeDelta GetDefaultDelayForType(ModelType model_type, | |
| 28 base::TimeDelta minimum_delay) { | |
| 29 switch (model_type) { | |
| 30 case AUTOFILL: | |
| 31 // Accompany types rely on nudges from other types, and hence have long | |
| 32 // nudge delays. | |
| 33 return base::TimeDelta::FromSeconds(kDefaultShortPollIntervalSeconds); | |
| 34 case BOOKMARKS: | |
| 35 case PREFERENCES: | |
| 36 // Types with sometimes automatic changes get longer delays to allow more | |
| 37 // coalescing. | |
| 38 return base::TimeDelta::FromMilliseconds(kSlowNudgeDelayMilliseconds); | |
| 39 case SESSIONS: | |
| 40 case FAVICON_IMAGES: | |
| 41 case FAVICON_TRACKING: | |
| 42 // Types with navigation triggered changes get longer delays to allow more | |
| 43 // coalescing. | |
| 44 return base::TimeDelta::FromSeconds(kDefaultSessionsCommitDelaySeconds); | |
| 45 default: | |
| 46 return minimum_delay; | |
| 47 } | |
| 48 } | |
| 49 | |
| 50 } // namespace | |
| 51 | |
| 52 size_t NudgeTracker::kDefaultMaxPayloadsPerType = 10; | |
| 53 | |
| 54 NudgeTracker::NudgeTracker() | |
| 55 : invalidations_enabled_(false), | |
| 56 invalidations_out_of_sync_(true), | |
| 57 minimum_local_nudge_delay_( | |
| 58 base::TimeDelta::FromMilliseconds(kDefaultNudgeDelayMilliseconds)), | |
| 59 local_refresh_nudge_delay_( | |
| 60 base::TimeDelta::FromMilliseconds(kSyncRefreshDelayMilliseconds)), | |
| 61 remote_invalidation_nudge_delay_( | |
| 62 base::TimeDelta::FromMilliseconds(kSyncSchedulerDelayMilliseconds)) { | |
| 63 ModelTypeSet protocol_types = ProtocolTypes(); | |
| 64 // Default initialize all the type trackers. | |
| 65 for (ModelTypeSet::Iterator it = protocol_types.First(); it.Good(); | |
| 66 it.Inc()) { | |
| 67 type_trackers_.insert( | |
| 68 std::make_pair(it.Get(), base::WrapUnique(new DataTypeTracker()))); | |
| 69 } | |
| 70 } | |
| 71 | |
| 72 NudgeTracker::~NudgeTracker() { } | |
| 73 | |
| 74 bool NudgeTracker::IsSyncRequired() const { | |
| 75 if (IsRetryRequired()) | |
| 76 return true; | |
| 77 | |
| 78 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
| 79 it != type_trackers_.end(); ++it) { | |
| 80 if (it->second->IsSyncRequired()) { | |
| 81 return true; | |
| 82 } | |
| 83 } | |
| 84 | |
| 85 return false; | |
| 86 } | |
| 87 | |
| 88 bool NudgeTracker::IsGetUpdatesRequired() const { | |
| 89 if (invalidations_out_of_sync_) | |
| 90 return true; | |
| 91 | |
| 92 if (IsRetryRequired()) | |
| 93 return true; | |
| 94 | |
| 95 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
| 96 it != type_trackers_.end(); ++it) { | |
| 97 if (it->second->IsGetUpdatesRequired()) { | |
| 98 return true; | |
| 99 } | |
| 100 } | |
| 101 return false; | |
| 102 } | |
| 103 | |
| 104 bool NudgeTracker::IsRetryRequired() const { | |
| 105 if (sync_cycle_start_time_.is_null()) | |
| 106 return false; | |
| 107 | |
| 108 if (current_retry_time_.is_null()) | |
| 109 return false; | |
| 110 | |
| 111 return current_retry_time_ <= sync_cycle_start_time_; | |
| 112 } | |
| 113 | |
| 114 void NudgeTracker::RecordSuccessfulSyncCycle() { | |
| 115 // If a retry was required, we've just serviced it. Unset the flag. | |
| 116 if (IsRetryRequired()) | |
| 117 current_retry_time_ = base::TimeTicks(); | |
| 118 | |
| 119 // A successful cycle while invalidations are enabled puts us back into sync. | |
| 120 invalidations_out_of_sync_ = !invalidations_enabled_; | |
| 121 | |
| 122 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
| 123 it != type_trackers_.end(); ++it) { | |
| 124 it->second->RecordSuccessfulSyncCycle(); | |
| 125 } | |
| 126 } | |
| 127 | |
| 128 base::TimeDelta NudgeTracker::RecordLocalChange(ModelTypeSet types) { | |
| 129 // Start with the longest delay. | |
| 130 base::TimeDelta delay = | |
| 131 base::TimeDelta::FromSeconds(kDefaultShortPollIntervalSeconds); | |
| 132 for (ModelTypeSet::Iterator type_it = types.First(); type_it.Good(); | |
| 133 type_it.Inc()) { | |
| 134 TypeTrackerMap::const_iterator tracker_it = | |
| 135 type_trackers_.find(type_it.Get()); | |
| 136 DCHECK(tracker_it != type_trackers_.end()); | |
| 137 | |
| 138 // Only if the type tracker has a valid delay (non-zero) that is shorter | |
| 139 // than the calculated delay do we update the calculated delay. | |
| 140 base::TimeDelta type_delay = tracker_it->second->RecordLocalChange(); | |
| 141 if (type_delay.is_zero()) { | |
| 142 type_delay = GetDefaultDelayForType(type_it.Get(), | |
| 143 minimum_local_nudge_delay_); | |
| 144 } | |
| 145 if (type_delay < delay) | |
| 146 delay = type_delay; | |
| 147 } | |
| 148 return delay; | |
| 149 } | |
| 150 | |
| 151 base::TimeDelta NudgeTracker::RecordLocalRefreshRequest(ModelTypeSet types) { | |
| 152 for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) { | |
| 153 TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(it.Get()); | |
| 154 DCHECK(tracker_it != type_trackers_.end()); | |
| 155 tracker_it->second->RecordLocalRefreshRequest(); | |
| 156 } | |
| 157 return local_refresh_nudge_delay_; | |
| 158 } | |
| 159 | |
| 160 base::TimeDelta NudgeTracker::RecordRemoteInvalidation( | |
| 161 syncer::ModelType type, | |
| 162 std::unique_ptr<InvalidationInterface> invalidation) { | |
| 163 // Forward the invalidations to the proper recipient. | |
| 164 TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(type); | |
| 165 DCHECK(tracker_it != type_trackers_.end()); | |
| 166 tracker_it->second->RecordRemoteInvalidation(std::move(invalidation)); | |
| 167 return remote_invalidation_nudge_delay_; | |
| 168 } | |
| 169 | |
| 170 void NudgeTracker::RecordInitialSyncRequired(syncer::ModelType type) { | |
| 171 TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(type); | |
| 172 DCHECK(tracker_it != type_trackers_.end()); | |
| 173 tracker_it->second->RecordInitialSyncRequired(); | |
| 174 } | |
| 175 | |
| 176 void NudgeTracker::RecordCommitConflict(syncer::ModelType type) { | |
| 177 TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(type); | |
| 178 DCHECK(tracker_it != type_trackers_.end()); | |
| 179 tracker_it->second->RecordCommitConflict(); | |
| 180 } | |
| 181 | |
| 182 void NudgeTracker::OnInvalidationsEnabled() { | |
| 183 invalidations_enabled_ = true; | |
| 184 } | |
| 185 | |
| 186 void NudgeTracker::OnInvalidationsDisabled() { | |
| 187 invalidations_enabled_ = false; | |
| 188 invalidations_out_of_sync_ = true; | |
| 189 } | |
| 190 | |
| 191 void NudgeTracker::SetTypesThrottledUntil( | |
| 192 ModelTypeSet types, | |
| 193 base::TimeDelta length, | |
| 194 base::TimeTicks now) { | |
| 195 for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) { | |
| 196 TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(it.Get()); | |
| 197 tracker_it->second->ThrottleType(length, now); | |
| 198 } | |
| 199 } | |
| 200 | |
| 201 void NudgeTracker::UpdateTypeThrottlingState(base::TimeTicks now) { | |
| 202 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
| 203 it != type_trackers_.end(); ++it) { | |
| 204 it->second->UpdateThrottleState(now); | |
| 205 } | |
| 206 } | |
| 207 | |
| 208 bool NudgeTracker::IsAnyTypeThrottled() const { | |
| 209 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
| 210 it != type_trackers_.end(); ++it) { | |
| 211 if (it->second->IsThrottled()) { | |
| 212 return true; | |
| 213 } | |
| 214 } | |
| 215 return false; | |
| 216 } | |
| 217 | |
| 218 bool NudgeTracker::IsTypeThrottled(ModelType type) const { | |
| 219 DCHECK(type_trackers_.find(type) != type_trackers_.end()); | |
| 220 return type_trackers_.find(type)->second->IsThrottled(); | |
| 221 } | |
| 222 | |
| 223 base::TimeDelta NudgeTracker::GetTimeUntilNextUnthrottle( | |
| 224 base::TimeTicks now) const { | |
| 225 DCHECK(IsAnyTypeThrottled()) << "This function requires a pending unthrottle"; | |
| 226 | |
| 227 // Return min of GetTimeUntilUnthrottle() values for all IsThrottled() types. | |
| 228 base::TimeDelta time_until_next_unthrottle = base::TimeDelta::Max(); | |
| 229 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
| 230 it != type_trackers_.end(); ++it) { | |
| 231 if (it->second->IsThrottled()) { | |
| 232 time_until_next_unthrottle = std::min( | |
| 233 time_until_next_unthrottle, it->second->GetTimeUntilUnthrottle(now)); | |
| 234 } | |
| 235 } | |
| 236 DCHECK(!time_until_next_unthrottle.is_max()); | |
| 237 | |
| 238 return time_until_next_unthrottle; | |
| 239 } | |
| 240 | |
| 241 ModelTypeSet NudgeTracker::GetThrottledTypes() const { | |
| 242 ModelTypeSet result; | |
| 243 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
| 244 it != type_trackers_.end(); ++it) { | |
| 245 if (it->second->IsThrottled()) { | |
| 246 result.Put(it->first); | |
| 247 } | |
| 248 } | |
| 249 return result; | |
| 250 } | |
| 251 | |
| 252 ModelTypeSet NudgeTracker::GetNudgedTypes() const { | |
| 253 ModelTypeSet result; | |
| 254 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
| 255 it != type_trackers_.end(); ++it) { | |
| 256 if (it->second->HasLocalChangePending()) { | |
| 257 result.Put(it->first); | |
| 258 } | |
| 259 } | |
| 260 return result; | |
| 261 } | |
| 262 | |
| 263 ModelTypeSet NudgeTracker::GetNotifiedTypes() const { | |
| 264 ModelTypeSet result; | |
| 265 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
| 266 it != type_trackers_.end(); ++it) { | |
| 267 if (it->second->HasPendingInvalidation()) { | |
| 268 result.Put(it->first); | |
| 269 } | |
| 270 } | |
| 271 return result; | |
| 272 } | |
| 273 | |
| 274 ModelTypeSet NudgeTracker::GetRefreshRequestedTypes() const { | |
| 275 ModelTypeSet result; | |
| 276 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
| 277 it != type_trackers_.end(); ++it) { | |
| 278 if (it->second->HasRefreshRequestPending()) { | |
| 279 result.Put(it->first); | |
| 280 } | |
| 281 } | |
| 282 return result; | |
| 283 } | |
| 284 | |
| 285 void NudgeTracker::SetLegacyNotificationHint( | |
| 286 ModelType type, | |
| 287 sync_pb::DataTypeProgressMarker* progress) const { | |
| 288 DCHECK(type_trackers_.find(type) != type_trackers_.end()); | |
| 289 type_trackers_.find(type)->second->SetLegacyNotificationHint(progress); | |
| 290 } | |
| 291 | |
| 292 sync_pb::GetUpdatesCallerInfo::GetUpdatesSource NudgeTracker::GetLegacySource() | |
| 293 const { | |
| 294 // There's an order to these sources: NOTIFICATION, DATATYPE_REFRESH, LOCAL, | |
| 295 // RETRY. The server makes optimization decisions based on this field, so | |
| 296 // it's important to get this right. Setting it wrong could lead to missed | |
| 297 // updates. | |
| 298 // | |
| 299 // This complexity is part of the reason why we're deprecating 'source' in | |
| 300 // favor of 'origin'. | |
| 301 bool has_invalidation_pending = false; | |
| 302 bool has_refresh_request_pending = false; | |
| 303 bool has_commit_pending = false; | |
| 304 bool is_initial_sync_required = false; | |
| 305 bool has_retry = IsRetryRequired(); | |
| 306 | |
| 307 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
| 308 it != type_trackers_.end(); ++it) { | |
| 309 const DataTypeTracker& tracker = *it->second; | |
| 310 if (!tracker.IsThrottled() && tracker.HasPendingInvalidation()) { | |
| 311 has_invalidation_pending = true; | |
| 312 } | |
| 313 if (!tracker.IsThrottled() && tracker.HasRefreshRequestPending()) { | |
| 314 has_refresh_request_pending = true; | |
| 315 } | |
| 316 if (!tracker.IsThrottled() && tracker.HasLocalChangePending()) { | |
| 317 has_commit_pending = true; | |
| 318 } | |
| 319 if (!tracker.IsThrottled() && tracker.IsInitialSyncRequired()) { | |
| 320 is_initial_sync_required = true; | |
| 321 } | |
| 322 } | |
| 323 | |
| 324 if (has_invalidation_pending) { | |
| 325 return sync_pb::GetUpdatesCallerInfo::NOTIFICATION; | |
| 326 } else if (has_refresh_request_pending) { | |
| 327 return sync_pb::GetUpdatesCallerInfo::DATATYPE_REFRESH; | |
| 328 } else if (is_initial_sync_required) { | |
| 329 // Not quite accurate, but good enough for our purposes. This setting of | |
| 330 // SOURCE is just a backward-compatibility hack anyway. | |
| 331 return sync_pb::GetUpdatesCallerInfo::DATATYPE_REFRESH; | |
| 332 } else if (has_commit_pending) { | |
| 333 return sync_pb::GetUpdatesCallerInfo::LOCAL; | |
| 334 } else if (has_retry) { | |
| 335 return sync_pb::GetUpdatesCallerInfo::RETRY; | |
| 336 } else { | |
| 337 return sync_pb::GetUpdatesCallerInfo::UNKNOWN; | |
| 338 } | |
| 339 } | |
| 340 | |
| 341 void NudgeTracker::FillProtoMessage( | |
| 342 ModelType type, | |
| 343 sync_pb::GetUpdateTriggers* msg) const { | |
| 344 DCHECK(type_trackers_.find(type) != type_trackers_.end()); | |
| 345 | |
| 346 // Fill what we can from the global data. | |
| 347 msg->set_invalidations_out_of_sync(invalidations_out_of_sync_); | |
| 348 | |
| 349 // Delegate the type-specific work to the DataTypeTracker class. | |
| 350 type_trackers_.find(type)->second->FillGetUpdatesTriggersMessage(msg); | |
| 351 } | |
| 352 | |
| 353 void NudgeTracker::SetSyncCycleStartTime(base::TimeTicks now) { | |
| 354 sync_cycle_start_time_ = now; | |
| 355 | |
| 356 // If current_retry_time_ is still set, that means we have an old retry time | |
| 357 // left over from a previous cycle. For example, maybe we tried to perform | |
| 358 // this retry, hit a network connection error, and now we're in exponential | |
| 359 // backoff. In that case, we want this sync cycle to include the GU retry | |
| 360 // flag so we leave this variable set regardless of whether or not there is an | |
| 361 // overwrite pending. | |
| 362 if (!current_retry_time_.is_null()) { | |
| 363 return; | |
| 364 } | |
| 365 | |
| 366 // If do not have a current_retry_time_, but we do have a next_retry_time_ and | |
| 367 // it is ready to go, then we set it as the current_retry_time_. It will stay | |
| 368 // there until a GU retry has succeeded. | |
| 369 if (!next_retry_time_.is_null() && | |
| 370 next_retry_time_ <= sync_cycle_start_time_) { | |
| 371 current_retry_time_ = next_retry_time_; | |
| 372 next_retry_time_ = base::TimeTicks(); | |
| 373 } | |
| 374 } | |
| 375 | |
| 376 void NudgeTracker::SetHintBufferSize(size_t size) { | |
| 377 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
| 378 it != type_trackers_.end(); ++it) { | |
| 379 it->second->UpdatePayloadBufferSize(size); | |
| 380 } | |
| 381 } | |
| 382 | |
| 383 void NudgeTracker::SetNextRetryTime(base::TimeTicks retry_time) { | |
| 384 next_retry_time_ = retry_time; | |
| 385 } | |
| 386 | |
| 387 void NudgeTracker::OnReceivedCustomNudgeDelays( | |
| 388 const std::map<ModelType, base::TimeDelta>& delay_map) { | |
| 389 for (std::map<ModelType, base::TimeDelta>::const_iterator iter = | |
| 390 delay_map.begin(); | |
| 391 iter != delay_map.end(); | |
| 392 ++iter) { | |
| 393 ModelType type = iter->first; | |
| 394 DCHECK(syncer::ProtocolTypes().Has(type)); | |
| 395 TypeTrackerMap::const_iterator type_iter = type_trackers_.find(type); | |
| 396 if (type_iter == type_trackers_.end()) | |
| 397 continue; | |
| 398 | |
| 399 if (iter->second > minimum_local_nudge_delay_) { | |
| 400 type_iter->second->UpdateLocalNudgeDelay(iter->second); | |
| 401 } else { | |
| 402 type_iter->second->UpdateLocalNudgeDelay( | |
| 403 GetDefaultDelayForType(type, | |
| 404 minimum_local_nudge_delay_)); | |
| 405 } | |
| 406 } | |
| 407 } | |
| 408 | |
| 409 void NudgeTracker::SetDefaultNudgeDelay(base::TimeDelta nudge_delay) { | |
| 410 minimum_local_nudge_delay_ = nudge_delay; | |
| 411 } | |
| 412 | |
| 413 } // namespace sessions | |
| 414 } // namespace syncer | |
| OLD | NEW |