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