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