1 // Copyright 2012 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 #ifndef SYNC_ENGINE_SYNC_SCHEDULER_IMPL_H_ 6 #define SYNC_ENGINE_SYNC_SCHEDULER_IMPL_H_ 7 8 #include <map> 9 #include <string> 10 11 #include "base/callback.h" 12 #include "base/cancelable_callback.h" 13 #include "base/compiler_specific.h" 14 #include "base/gtest_prod_util.h" 15 #include "base/memory/linked_ptr.h" 16 #include "base/memory/scoped_ptr.h" 17 #include "base/memory/weak_ptr.h" 18 #include "base/threading/non_thread_safe.h" 19 #include "base/time/time.h" 20 #include "base/timer/timer.h" 21 #include "sync/base/sync_export.h" 22 #include "sync/engine/net/server_connection_manager.h" 23 #include "sync/engine/nudge_source.h" 24 #include "sync/engine/sync_scheduler.h" 25 #include "sync/engine/syncer.h" 26 #include "sync/internal_api/public/engine/polling_constants.h" 27 #include "sync/internal_api/public/util/weak_handle.h" 28 #include "sync/sessions/nudge_tracker.h" 29 #include "sync/sessions/sync_session.h" 30 #include "sync/sessions/sync_session_context.h" 31 32 namespace syncer { 33 34 class BackoffDelayProvider; 35 36 namespace sessions { 37 struct ModelNeutralState; 38 } 39 40 class SYNC_EXPORT_PRIVATE SyncSchedulerImpl 41 : public SyncScheduler, 42 public base::NonThreadSafe { 43 public: 44 // |name| is a display string to identify the syncer thread. Takes 45 // |ownership of |syncer| and |delay_provider|. 46 SyncSchedulerImpl(const std::string& name, 47 BackoffDelayProvider* delay_provider, 48 sessions::SyncSessionContext* context, 49 Syncer* syncer); 50 51 // Calls Stop(). 52 virtual ~SyncSchedulerImpl(); 53 54 virtual void Start(Mode mode) OVERRIDE; 55 virtual void ScheduleConfiguration( 56 const ConfigurationParams& params) OVERRIDE; 57 virtual void Stop() OVERRIDE; 58 virtual void ScheduleLocalNudge( 59 const base::TimeDelta& desired_delay, 60 ModelTypeSet types, 61 const tracked_objects::Location& nudge_location) OVERRIDE; 62 virtual void ScheduleLocalRefreshRequest( 63 const base::TimeDelta& desired_delay, 64 ModelTypeSet types, 65 const tracked_objects::Location& nudge_location) OVERRIDE; 66 virtual void ScheduleInvalidationNudge( 67 const base::TimeDelta& desired_delay, 68 const ObjectIdInvalidationMap& invalidation_map, 69 const tracked_objects::Location& nudge_location) OVERRIDE; 70 virtual void SetNotificationsEnabled(bool notifications_enabled) OVERRIDE; 71 72 virtual base::TimeDelta GetSessionsCommitDelay() const OVERRIDE; 73 74 virtual void OnCredentialsUpdated() OVERRIDE; 75 virtual void OnConnectionStatusChange() OVERRIDE; 76 77 // SyncSession::Delegate implementation. 78 virtual void OnThrottled(const base::TimeDelta& throttle_duration) OVERRIDE; 79 virtual void OnTypesThrottled( 80 ModelTypeSet types, 81 const base::TimeDelta& throttle_duration) OVERRIDE; 82 virtual bool IsCurrentlyThrottled() OVERRIDE; 83 virtual void OnReceivedShortPollIntervalUpdate( 84 const base::TimeDelta& new_interval) OVERRIDE; 85 virtual void OnReceivedLongPollIntervalUpdate( 86 const base::TimeDelta& new_interval) OVERRIDE; 87 virtual void OnReceivedSessionsCommitDelay( 88 const base::TimeDelta& new_delay) OVERRIDE; 89 virtual void OnReceivedClientInvalidationHintBufferSize(int size) OVERRIDE; 90 virtual void OnSyncProtocolError( 91 const SyncProtocolError& sync_protocol_error) OVERRIDE; 92 virtual void OnReceivedGuRetryDelay(const base::TimeDelta& delay) OVERRIDE; 93 virtual void OnReceivedMigrationRequest(syncer::ModelTypeSet types) OVERRIDE; 94 95 // Returns true if the client is currently in exponential backoff. 96 bool IsBackingOff() const; 97 98 private: 99 enum JobPriority { 100 // Non-canary jobs respect exponential backoff. 101 NORMAL_PRIORITY, 102 // Canary jobs bypass exponential backoff, so use with extreme caution. 103 CANARY_PRIORITY 104 }; 105 106 enum PollAdjustType { 107 // Restart the poll interval. 108 FORCE_RESET, 109 // Restart the poll interval only if its length has changed. 110 UPDATE_INTERVAL, 111 }; 112 113 friend class SyncSchedulerTest; 114 friend class SyncSchedulerWhiteboxTest; 115 friend class SyncerTest; 116 117 FRIEND_TEST_ALL_PREFIXES(SyncSchedulerTest, TransientPollFailure); 118 FRIEND_TEST_ALL_PREFIXES(SyncSchedulerTest, 119 ServerConnectionChangeDuringBackoff); 120 FRIEND_TEST_ALL_PREFIXES(SyncSchedulerTest, 121 ConnectionChangeCanaryPreemptedByNudge); 122 FRIEND_TEST_ALL_PREFIXES(BackoffTriggersSyncSchedulerTest, 123 FailGetEncryptionKey); 124 FRIEND_TEST_ALL_PREFIXES(SyncSchedulerTest, SuccessfulRetry); 125 FRIEND_TEST_ALL_PREFIXES(SyncSchedulerTest, FailedRetry); 126 FRIEND_TEST_ALL_PREFIXES(SyncSchedulerTest, ReceiveNewRetryDelay); 127 128 struct SYNC_EXPORT_PRIVATE WaitInterval { 129 enum Mode { 130 // Uninitialized state, should not be set in practice. 131 UNKNOWN = -1, 132 // We enter a series of increasingly longer WaitIntervals if we experience 133 // repeated transient failures. We retry at the end of each interval. 134 EXPONENTIAL_BACKOFF, 135 // A server-initiated throttled interval. We do not allow any syncing 136 // during such an interval. 137 THROTTLED, 138 }; 139 WaitInterval(); 140 ~WaitInterval(); 141 WaitInterval(Mode mode, base::TimeDelta length); 142 143 static const char* GetModeString(Mode mode); 144 145 Mode mode; 146 base::TimeDelta length; 147 }; 148 149 static const char* GetModeString(Mode mode); 150 151 // Invoke the syncer to perform a nudge job. 152 void DoNudgeSyncSessionJob(JobPriority priority); 153 154 // Invoke the syncer to perform a configuration job. 155 void DoConfigurationSyncSessionJob(JobPriority priority); 156 157 // Helper function for Do{Nudge,Configuration}SyncSessionJob. 158 void HandleFailure( 159 const sessions::ModelNeutralState& model_neutral_state); 160 161 // Invoke the Syncer to perform a poll job. 162 void DoPollSyncSessionJob(); 163 164 // Helper function to calculate poll interval. 165 base::TimeDelta GetPollInterval(); 166 167 // Adjusts the poll timer to account for new poll interval, and possibly 168 // resets the poll interval, depedning on the flag's value. 169 void AdjustPolling(PollAdjustType type); 170 171 // Helper to restart waiting with |wait_interval_|'s timer. 172 void RestartWaiting(); 173 174 // Determines if we're allowed to contact the server right now. 175 bool CanRunJobNow(JobPriority priority); 176 177 // Determines if we're allowed to contact the server right now. 178 bool CanRunNudgeJobNow(JobPriority priority); 179 180 // If the scheduler's current state supports it, this will create a job based 181 // on the passed in parameters and coalesce it with any other pending jobs, 182 // then post a delayed task to run it. It may also choose to drop the job or 183 // save it for later, depending on the scheduler's current state. 184 void ScheduleNudgeImpl( 185 const base::TimeDelta& delay, 186 const tracked_objects::Location& nudge_location); 187 188 // Helper to signal listeners about changed retry time. 189 void NotifyRetryTime(base::Time retry_time); 190 191 // Helper to signal listeners about changed throttled types. 192 void NotifyThrottledTypesChanged(ModelTypeSet types); 193 194 // Looks for pending work and, if it finds any, run this work at "canary" 195 // priority. 196 void TryCanaryJob(); 197 198 // At the moment TrySyncSessionJob just posts call to TrySyncSessionJobImpl on 199 // current thread. In the future it will request access token here. 200 void TrySyncSessionJob(); 201 void TrySyncSessionJobImpl(); 202 203 // Transitions out of the THROTTLED WaitInterval then calls TryCanaryJob(). 204 void Unthrottle(); 205 206 // Called when a per-type throttling interval expires. 207 void TypeUnthrottle(base::TimeTicks unthrottle_time); 208 209 // Runs a normal nudge job when the scheduled timer expires. 210 void PerformDelayedNudge(); 211 212 // Attempts to exit EXPONENTIAL_BACKOFF by calling TryCanaryJob(). 213 void ExponentialBackoffRetry(); 214 215 // Called when the root cause of the current connection error is fixed. 216 void OnServerConnectionErrorFixed(); 217 218 // Creates a session for a poll and performs the sync. 219 void PollTimerCallback(); 220 221 // Creates a session for a retry and performs the sync. 222 void RetryTimerCallback(); 223 224 // Returns the set of types that are enabled and not currently throttled. 225 ModelTypeSet GetEnabledAndUnthrottledTypes(); 226 227 // Called as we are started to broadcast an initial session snapshot 228 // containing data like initial_sync_ended. Important when the client starts 229 // up and does not need to perform an initial sync. 230 void SendInitialSnapshot(); 231 232 // This is used for histogramming and analysis of ScheduleNudge* APIs. 233 // SyncScheduler is the ultimate choke-point for all such invocations (with 234 // and without InvalidationState variants, all NudgeSources, etc) and as such 235 // is the most flexible place to do this bookkeeping. 236 void UpdateNudgeTimeRecords(ModelTypeSet types); 237 238 // For certain methods that need to worry about X-thread posting. 239 WeakHandle<SyncSchedulerImpl> weak_handle_this_; 240 241 // Used for logging. 242 const std::string name_; 243 244 // Set in Start(), unset in Stop(). 245 bool started_; 246 247 // Modifiable versions of kDefaultLongPollIntervalSeconds which can be 248 // updated by the server. 249 base::TimeDelta syncer_short_poll_interval_seconds_; 250 base::TimeDelta syncer_long_poll_interval_seconds_; 251 252 // Server-tweakable sessions commit delay. 253 base::TimeDelta sessions_commit_delay_; 254 255 // Periodic timer for polling. See AdjustPolling. 256 base::RepeatingTimer<SyncSchedulerImpl> poll_timer_; 257 258 // The mode of operation. 259 Mode mode_; 260 261 // Current wait state. Null if we're not in backoff and not throttled. 262 scoped_ptr<WaitInterval> wait_interval_; 263 264 scoped_ptr<BackoffDelayProvider> delay_provider_; 265 266 // The event that will wake us up. 267 base::OneShotTimer<SyncSchedulerImpl> pending_wakeup_timer_; 268 269 // An event that fires when data type throttling expires. 270 base::OneShotTimer<SyncSchedulerImpl> type_unthrottle_timer_; 271 272 // Storage for variables related to an in-progress configure request. Note 273 // that (mode_ != CONFIGURATION_MODE) \implies !pending_configure_params_. 274 scoped_ptr<ConfigurationParams> pending_configure_params_; 275 276 // If we have a nudge pending to run soon, it will be listed here. 277 base::TimeTicks scheduled_nudge_time_; 278 279 // Keeps track of work that the syncer needs to handle. 280 sessions::NudgeTracker nudge_tracker_; 281 282 // Invoked to run through the sync cycle. 283 scoped_ptr<Syncer> syncer_; 284 285 sessions::SyncSessionContext* session_context_; 286 287 // A map tracking LOCAL NudgeSource invocations of ScheduleNudge* APIs, 288 // organized by datatype. Each datatype that was part of the types requested 289 // in the call will have its TimeTicks value updated. 290 typedef std::map<ModelType, base::TimeTicks> ModelTypeTimeMap; 291 ModelTypeTimeMap last_local_nudges_by_model_type_; 292 293 // Used as an "anti-reentrancy defensive assertion". 294 // While true, it is illegal for any new scheduling activity to take place. 295 // Ensures that higher layers don't break this law in response to events that 296 // take place during a sync cycle. We call this out because such violations 297 // could result in tight sync loops hitting sync servers. 298 bool no_scheduling_allowed_; 299 300 // crbug/251307. This is a workaround for M29. crbug/259913 tracks proper fix 301 // for M30. 302 // The issue is that poll job runs after few hours of inactivity and therefore 303 // will always fail with auth error because of expired access token. Once 304 // fresh access token is requested poll job is not retried. 305 // The change is to remember that poll timer just fired and retry poll job 306 // after credentials are updated. 307 bool do_poll_after_credentials_updated_; 308 309 // TryJob might get called for multiple reasons. It should only call 310 // DoPollSyncSessionJob after some time since the last attempt. 311 // last_poll_reset_ keeps track of when was last attempt. 312 base::TimeTicks last_poll_reset_; 313 314 // next_sync_session_job_priority_ defines which priority will be used next 315 // time TrySyncSessionJobImpl is called. CANARY_PRIORITY allows syncer to run 316 // even if scheduler is in exponential backoff. This is needed for events that 317 // have chance of resolving previous error (e.g. network connection change 318 // after NETWORK_UNAVAILABLE error). 319 // It is reset back to NORMAL_PRIORITY on every call to TrySyncSessionJobImpl. 320 JobPriority next_sync_session_job_priority_; 321 322 // One-shot timer for scheduling GU retry according to delay set by server. 323 base::OneShotTimer<SyncSchedulerImpl> retry_timer_; 324 325 base::WeakPtrFactory<SyncSchedulerImpl> weak_ptr_factory_; 326 327 // A second factory specially for weak_handle_this_, to allow the handle 328 // to be const and alleviate threading concerns. 329 base::WeakPtrFactory<SyncSchedulerImpl> weak_ptr_factory_for_weak_handle_; 330 331 DISALLOW_COPY_AND_ASSIGN(SyncSchedulerImpl); 332 }; 333 334 } // namespace syncer 335 336 #endif // SYNC_ENGINE_SYNC_SCHEDULER_IMPL_H_ 337