| 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 "components/sync/sessions_impl/nudge_tracker.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 | |
| 9 #include <utility> | |
| 10 | |
| 11 #include "base/memory/ptr_util.h" | |
| 12 #include "components/sync/engine/polling_constants.h" | |
| 13 #include "components/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 = | |
| 143 GetDefaultDelayForType(type_it.Get(), 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(ModelTypeSet types, | |
| 192 base::TimeDelta length, | |
| 193 base::TimeTicks now) { | |
| 194 for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) { | |
| 195 TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(it.Get()); | |
| 196 tracker_it->second->ThrottleType(length, now); | |
| 197 } | |
| 198 } | |
| 199 | |
| 200 void NudgeTracker::UpdateTypeThrottlingState(base::TimeTicks now) { | |
| 201 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
| 202 it != type_trackers_.end(); ++it) { | |
| 203 it->second->UpdateThrottleState(now); | |
| 204 } | |
| 205 } | |
| 206 | |
| 207 bool NudgeTracker::IsAnyTypeThrottled() const { | |
| 208 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
| 209 it != type_trackers_.end(); ++it) { | |
| 210 if (it->second->IsThrottled()) { | |
| 211 return true; | |
| 212 } | |
| 213 } | |
| 214 return false; | |
| 215 } | |
| 216 | |
| 217 bool NudgeTracker::IsTypeThrottled(ModelType type) const { | |
| 218 DCHECK(type_trackers_.find(type) != type_trackers_.end()); | |
| 219 return type_trackers_.find(type)->second->IsThrottled(); | |
| 220 } | |
| 221 | |
| 222 base::TimeDelta NudgeTracker::GetTimeUntilNextUnthrottle( | |
| 223 base::TimeTicks now) const { | |
| 224 DCHECK(IsAnyTypeThrottled()) << "This function requires a pending unthrottle"; | |
| 225 | |
| 226 // Return min of GetTimeUntilUnthrottle() values for all IsThrottled() types. | |
| 227 base::TimeDelta time_until_next_unthrottle = base::TimeDelta::Max(); | |
| 228 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
| 229 it != type_trackers_.end(); ++it) { | |
| 230 if (it->second->IsThrottled()) { | |
| 231 time_until_next_unthrottle = std::min( | |
| 232 time_until_next_unthrottle, it->second->GetTimeUntilUnthrottle(now)); | |
| 233 } | |
| 234 } | |
| 235 DCHECK(!time_until_next_unthrottle.is_max()); | |
| 236 | |
| 237 return time_until_next_unthrottle; | |
| 238 } | |
| 239 | |
| 240 ModelTypeSet NudgeTracker::GetThrottledTypes() const { | |
| 241 ModelTypeSet result; | |
| 242 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
| 243 it != type_trackers_.end(); ++it) { | |
| 244 if (it->second->IsThrottled()) { | |
| 245 result.Put(it->first); | |
| 246 } | |
| 247 } | |
| 248 return result; | |
| 249 } | |
| 250 | |
| 251 ModelTypeSet NudgeTracker::GetNudgedTypes() const { | |
| 252 ModelTypeSet result; | |
| 253 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
| 254 it != type_trackers_.end(); ++it) { | |
| 255 if (it->second->HasLocalChangePending()) { | |
| 256 result.Put(it->first); | |
| 257 } | |
| 258 } | |
| 259 return result; | |
| 260 } | |
| 261 | |
| 262 ModelTypeSet NudgeTracker::GetNotifiedTypes() const { | |
| 263 ModelTypeSet result; | |
| 264 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
| 265 it != type_trackers_.end(); ++it) { | |
| 266 if (it->second->HasPendingInvalidation()) { | |
| 267 result.Put(it->first); | |
| 268 } | |
| 269 } | |
| 270 return result; | |
| 271 } | |
| 272 | |
| 273 ModelTypeSet NudgeTracker::GetRefreshRequestedTypes() const { | |
| 274 ModelTypeSet result; | |
| 275 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
| 276 it != type_trackers_.end(); ++it) { | |
| 277 if (it->second->HasRefreshRequestPending()) { | |
| 278 result.Put(it->first); | |
| 279 } | |
| 280 } | |
| 281 return result; | |
| 282 } | |
| 283 | |
| 284 void NudgeTracker::SetLegacyNotificationHint( | |
| 285 ModelType type, | |
| 286 sync_pb::DataTypeProgressMarker* progress) const { | |
| 287 DCHECK(type_trackers_.find(type) != type_trackers_.end()); | |
| 288 type_trackers_.find(type)->second->SetLegacyNotificationHint(progress); | |
| 289 } | |
| 290 | |
| 291 sync_pb::GetUpdatesCallerInfo::GetUpdatesSource NudgeTracker::GetLegacySource() | |
| 292 const { | |
| 293 // There's an order to these sources: NOTIFICATION, DATATYPE_REFRESH, LOCAL, | |
| 294 // RETRY. The server makes optimization decisions based on this field, so | |
| 295 // it's important to get this right. Setting it wrong could lead to missed | |
| 296 // updates. | |
| 297 // | |
| 298 // This complexity is part of the reason why we're deprecating 'source' in | |
| 299 // favor of 'origin'. | |
| 300 bool has_invalidation_pending = false; | |
| 301 bool has_refresh_request_pending = false; | |
| 302 bool has_commit_pending = false; | |
| 303 bool is_initial_sync_required = false; | |
| 304 bool has_retry = IsRetryRequired(); | |
| 305 | |
| 306 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
| 307 it != type_trackers_.end(); ++it) { | |
| 308 const DataTypeTracker& tracker = *it->second; | |
| 309 if (!tracker.IsThrottled() && tracker.HasPendingInvalidation()) { | |
| 310 has_invalidation_pending = true; | |
| 311 } | |
| 312 if (!tracker.IsThrottled() && tracker.HasRefreshRequestPending()) { | |
| 313 has_refresh_request_pending = true; | |
| 314 } | |
| 315 if (!tracker.IsThrottled() && tracker.HasLocalChangePending()) { | |
| 316 has_commit_pending = true; | |
| 317 } | |
| 318 if (!tracker.IsThrottled() && tracker.IsInitialSyncRequired()) { | |
| 319 is_initial_sync_required = true; | |
| 320 } | |
| 321 } | |
| 322 | |
| 323 if (has_invalidation_pending) { | |
| 324 return sync_pb::GetUpdatesCallerInfo::NOTIFICATION; | |
| 325 } else if (has_refresh_request_pending) { | |
| 326 return sync_pb::GetUpdatesCallerInfo::DATATYPE_REFRESH; | |
| 327 } else if (is_initial_sync_required) { | |
| 328 // Not quite accurate, but good enough for our purposes. This setting of | |
| 329 // SOURCE is just a backward-compatibility hack anyway. | |
| 330 return sync_pb::GetUpdatesCallerInfo::DATATYPE_REFRESH; | |
| 331 } else if (has_commit_pending) { | |
| 332 return sync_pb::GetUpdatesCallerInfo::LOCAL; | |
| 333 } else if (has_retry) { | |
| 334 return sync_pb::GetUpdatesCallerInfo::RETRY; | |
| 335 } else { | |
| 336 return sync_pb::GetUpdatesCallerInfo::UNKNOWN; | |
| 337 } | |
| 338 } | |
| 339 | |
| 340 void NudgeTracker::FillProtoMessage(ModelType type, | |
| 341 sync_pb::GetUpdateTriggers* msg) const { | |
| 342 DCHECK(type_trackers_.find(type) != type_trackers_.end()); | |
| 343 | |
| 344 // Fill what we can from the global data. | |
| 345 msg->set_invalidations_out_of_sync(invalidations_out_of_sync_); | |
| 346 | |
| 347 // Delegate the type-specific work to the DataTypeTracker class. | |
| 348 type_trackers_.find(type)->second->FillGetUpdatesTriggersMessage(msg); | |
| 349 } | |
| 350 | |
| 351 void NudgeTracker::SetSyncCycleStartTime(base::TimeTicks now) { | |
| 352 sync_cycle_start_time_ = now; | |
| 353 | |
| 354 // If current_retry_time_ is still set, that means we have an old retry time | |
| 355 // left over from a previous cycle. For example, maybe we tried to perform | |
| 356 // this retry, hit a network connection error, and now we're in exponential | |
| 357 // backoff. In that case, we want this sync cycle to include the GU retry | |
| 358 // flag so we leave this variable set regardless of whether or not there is an | |
| 359 // overwrite pending. | |
| 360 if (!current_retry_time_.is_null()) { | |
| 361 return; | |
| 362 } | |
| 363 | |
| 364 // If do not have a current_retry_time_, but we do have a next_retry_time_ and | |
| 365 // it is ready to go, then we set it as the current_retry_time_. It will stay | |
| 366 // there until a GU retry has succeeded. | |
| 367 if (!next_retry_time_.is_null() && | |
| 368 next_retry_time_ <= sync_cycle_start_time_) { | |
| 369 current_retry_time_ = next_retry_time_; | |
| 370 next_retry_time_ = base::TimeTicks(); | |
| 371 } | |
| 372 } | |
| 373 | |
| 374 void NudgeTracker::SetHintBufferSize(size_t size) { | |
| 375 for (TypeTrackerMap::const_iterator it = type_trackers_.begin(); | |
| 376 it != type_trackers_.end(); ++it) { | |
| 377 it->second->UpdatePayloadBufferSize(size); | |
| 378 } | |
| 379 } | |
| 380 | |
| 381 void NudgeTracker::SetNextRetryTime(base::TimeTicks retry_time) { | |
| 382 next_retry_time_ = retry_time; | |
| 383 } | |
| 384 | |
| 385 void NudgeTracker::OnReceivedCustomNudgeDelays( | |
| 386 const std::map<ModelType, base::TimeDelta>& delay_map) { | |
| 387 for (std::map<ModelType, base::TimeDelta>::const_iterator iter = | |
| 388 delay_map.begin(); | |
| 389 iter != delay_map.end(); ++iter) { | |
| 390 ModelType type = iter->first; | |
| 391 DCHECK(syncer::ProtocolTypes().Has(type)); | |
| 392 TypeTrackerMap::const_iterator type_iter = type_trackers_.find(type); | |
| 393 if (type_iter == type_trackers_.end()) | |
| 394 continue; | |
| 395 | |
| 396 if (iter->second > minimum_local_nudge_delay_) { | |
| 397 type_iter->second->UpdateLocalNudgeDelay(iter->second); | |
| 398 } else { | |
| 399 type_iter->second->UpdateLocalNudgeDelay( | |
| 400 GetDefaultDelayForType(type, minimum_local_nudge_delay_)); | |
| 401 } | |
| 402 } | |
| 403 } | |
| 404 | |
| 405 void NudgeTracker::SetDefaultNudgeDelay(base::TimeDelta nudge_delay) { | |
| 406 minimum_local_nudge_delay_ = nudge_delay; | |
| 407 } | |
| 408 | |
| 409 } // namespace sessions | |
| 410 } // namespace syncer | |
| OLD | NEW |