| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 // | 4 // |
| 5 // A class to schedule syncer tasks intelligently. | 5 // A class to schedule syncer tasks intelligently. |
| 6 #ifndef SYNC_ENGINE_SYNC_SCHEDULER_H_ | 6 #ifndef SYNC_ENGINE_SYNC_SCHEDULER_H_ |
| 7 #define SYNC_ENGINE_SYNC_SCHEDULER_H_ | 7 #define SYNC_ENGINE_SYNC_SCHEDULER_H_ |
| 8 | 8 |
| 9 #include <string> | 9 #include <string> |
| 10 | 10 |
| 11 #include "base/callback.h" | 11 #include "base/callback.h" |
| 12 #include "base/compiler_specific.h" | 12 #include "base/compiler_specific.h" |
| 13 #include "base/gtest_prod_util.h" | |
| 14 #include "base/memory/linked_ptr.h" | |
| 15 #include "base/memory/scoped_ptr.h" | |
| 16 #include "base/memory/weak_ptr.h" | |
| 17 #include "base/observer_list.h" | |
| 18 #include "base/time.h" | 13 #include "base/time.h" |
| 19 #include "base/timer.h" | |
| 20 #include "sync/engine/net/server_connection_manager.h" | |
| 21 #include "sync/engine/nudge_source.h" | 14 #include "sync/engine/nudge_source.h" |
| 22 #include "sync/engine/syncer.h" | |
| 23 #include "sync/internal_api/public/base/model_type_payload_map.h" | 15 #include "sync/internal_api/public/base/model_type_payload_map.h" |
| 24 #include "sync/internal_api/public/engine/polling_constants.h" | |
| 25 #include "sync/internal_api/public/util/weak_handle.h" | |
| 26 #include "sync/sessions/sync_session.h" | 16 #include "sync/sessions/sync_session.h" |
| 27 #include "sync/sessions/sync_session_context.h" | |
| 28 | 17 |
| 29 class MessageLoop; | 18 class MessageLoop; |
| 30 | 19 |
| 31 namespace tracked_objects { | 20 namespace tracked_objects { |
| 32 class Location; | 21 class Location; |
| 33 } // namespace tracked_objects | 22 } // namespace tracked_objects |
| 34 | 23 |
| 35 namespace syncer { | 24 namespace syncer { |
| 36 | 25 |
| 37 struct ServerConnectionEvent; | 26 struct ServerConnectionEvent; |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 71 // normal mode. | 60 // normal mode. |
| 72 CONFIGURATION_MODE, | 61 CONFIGURATION_MODE, |
| 73 // Resumes polling and allows nudges, drops configuration tasks. Runs | 62 // Resumes polling and allows nudges, drops configuration tasks. Runs |
| 74 // through entire sync cycle. | 63 // through entire sync cycle. |
| 75 NORMAL_MODE, | 64 NORMAL_MODE, |
| 76 }; | 65 }; |
| 77 | 66 |
| 78 // All methods of SyncScheduler must be called on the same thread | 67 // All methods of SyncScheduler must be called on the same thread |
| 79 // (except for RequestEarlyExit()). | 68 // (except for RequestEarlyExit()). |
| 80 | 69 |
| 81 // |name| is a display string to identify the syncer thread. Takes | 70 SyncScheduler(); |
| 82 // |ownership of |syncer|. | |
| 83 SyncScheduler(const std::string& name, | |
| 84 sessions::SyncSessionContext* context, Syncer* syncer); | |
| 85 | |
| 86 // Calls Stop(). | |
| 87 virtual ~SyncScheduler(); | 71 virtual ~SyncScheduler(); |
| 88 | 72 |
| 89 // Start the scheduler with the given mode. If the scheduler is | 73 // Start the scheduler with the given mode. If the scheduler is |
| 90 // already started, switch to the given mode, although some | 74 // already started, switch to the given mode, although some |
| 91 // scheduled tasks from the old mode may still run. | 75 // scheduled tasks from the old mode may still run. |
| 92 virtual void Start(Mode mode); | 76 virtual void Start(Mode mode) = 0; |
| 93 | 77 |
| 94 // Schedules the configuration task specified by |params|. Returns true if | 78 // Schedules the configuration task specified by |params|. Returns true if |
| 95 // the configuration task executed immediately, false if it had to be | 79 // the configuration task executed immediately, false if it had to be |
| 96 // scheduled for a later attempt. |params.ready_task| is invoked whenever the | 80 // scheduled for a later attempt. |params.ready_task| is invoked whenever the |
| 97 // configuration task executes. | 81 // configuration task executes. |
| 98 // Note: must already be in CONFIGURATION mode. | 82 // Note: must already be in CONFIGURATION mode. |
| 99 virtual bool ScheduleConfiguration(const ConfigurationParams& params); | 83 virtual bool ScheduleConfiguration(const ConfigurationParams& params) = 0; |
| 100 | 84 |
| 101 // Request that any running syncer task stop as soon as possible and | 85 // Request that any running syncer task stop as soon as possible and |
| 102 // cancel all scheduled tasks. This function can be called from any thread, | 86 // cancel all scheduled tasks. This function can be called from any thread, |
| 103 // and should in fact be called from a thread that isn't the sync loop to | 87 // and should in fact be called from a thread that isn't the sync loop to |
| 104 // allow preempting ongoing sync cycles. | 88 // allow preempting ongoing sync cycles. |
| 105 // Invokes |callback| from the sync loop once syncer is idle and all tasks | 89 // Invokes |callback| from the sync loop once syncer is idle and all tasks |
| 106 // are cancelled. | 90 // are cancelled. |
| 107 void RequestStop(const base::Closure& callback); | 91 virtual void RequestStop(const base::Closure& callback) = 0; |
| 108 | 92 |
| 109 // The meat and potatoes. Both of these methods will post a delayed task | 93 // The meat and potatoes. Both of these methods will post a delayed task |
| 110 // to attempt the actual nudge (see ScheduleNudgeImpl). | 94 // to attempt the actual nudge (see ScheduleNudgeImpl). |
| 111 void ScheduleNudgeAsync(const base::TimeDelta& delay, NudgeSource source, | 95 virtual void ScheduleNudgeAsync( |
| 112 syncer::ModelTypeSet types, | 96 const base::TimeDelta& delay, |
| 113 const tracked_objects::Location& nudge_location); | 97 NudgeSource source, |
| 114 void ScheduleNudgeWithPayloadsAsync( | 98 syncer::ModelTypeSet types, |
| 99 const tracked_objects::Location& nudge_location) = 0; |
| 100 virtual void ScheduleNudgeWithPayloadsAsync( |
| 115 const base::TimeDelta& delay, NudgeSource source, | 101 const base::TimeDelta& delay, NudgeSource source, |
| 116 const syncer::ModelTypePayloadMap& types_with_payloads, | 102 const syncer::ModelTypePayloadMap& types_with_payloads, |
| 117 const tracked_objects::Location& nudge_location); | 103 const tracked_objects::Location& nudge_location) = 0; |
| 118 | |
| 119 void CleanupDisabledTypes(); | |
| 120 | 104 |
| 121 // Change status of notifications in the SyncSessionContext. | 105 // Change status of notifications in the SyncSessionContext. |
| 122 void set_notifications_enabled(bool notifications_enabled); | 106 virtual void SetNotificationsEnabled(bool notifications_enabled) = 0; |
| 123 | 107 |
| 124 base::TimeDelta sessions_commit_delay() const; | 108 virtual base::TimeDelta GetSessionsCommitDelay() const = 0; |
| 125 | |
| 126 // DDOS avoidance function. Calculates how long we should wait before trying | |
| 127 // again after a failed sync attempt, where the last delay was |base_delay|. | |
| 128 // TODO(tim): Look at URLRequestThrottlerEntryInterface. | |
| 129 static base::TimeDelta GetRecommendedDelay(const base::TimeDelta& base_delay); | |
| 130 | 109 |
| 131 // Called when credentials are updated by the user. | 110 // Called when credentials are updated by the user. |
| 132 void OnCredentialsUpdated(); | 111 virtual void OnCredentialsUpdated() = 0; |
| 133 | 112 |
| 134 // Called when the network layer detects a connection status change. | 113 // Called when the network layer detects a connection status change. |
| 135 void OnConnectionStatusChange(); | 114 virtual void OnConnectionStatusChange() = 0; |
| 136 | |
| 137 // SyncSession::Delegate implementation. | |
| 138 virtual void OnSilencedUntil( | |
| 139 const base::TimeTicks& silenced_until) OVERRIDE; | |
| 140 virtual bool IsSyncingCurrentlySilenced() OVERRIDE; | |
| 141 virtual void OnReceivedShortPollIntervalUpdate( | |
| 142 const base::TimeDelta& new_interval) OVERRIDE; | |
| 143 virtual void OnReceivedLongPollIntervalUpdate( | |
| 144 const base::TimeDelta& new_interval) OVERRIDE; | |
| 145 virtual void OnReceivedSessionsCommitDelay( | |
| 146 const base::TimeDelta& new_delay) OVERRIDE; | |
| 147 virtual void OnShouldStopSyncingPermanently() OVERRIDE; | |
| 148 virtual void OnSyncProtocolError( | |
| 149 const sessions::SyncSessionSnapshot& snapshot) OVERRIDE; | |
| 150 | |
| 151 private: | |
| 152 enum JobProcessDecision { | |
| 153 // Indicates we should continue with the current job. | |
| 154 CONTINUE, | |
| 155 // Indicates that we should save it to be processed later. | |
| 156 SAVE, | |
| 157 // Indicates we should drop this job. | |
| 158 DROP, | |
| 159 }; | |
| 160 | |
| 161 struct SyncSessionJob { | |
| 162 // An enum used to describe jobs for scheduling purposes. | |
| 163 enum SyncSessionJobPurpose { | |
| 164 // Uninitialized state, should never be hit in practice. | |
| 165 UNKNOWN = -1, | |
| 166 // Our poll timer schedules POLL jobs periodically based on a server | |
| 167 // assigned poll interval. | |
| 168 POLL, | |
| 169 // A nudge task can come from a variety of components needing to force | |
| 170 // a sync. The source is inferable from |session.source()|. | |
| 171 NUDGE, | |
| 172 // Typically used for fetching updates for a subset of the enabled types | |
| 173 // during initial sync or reconfiguration. We don't run all steps of | |
| 174 // the sync cycle for these (e.g. CleanupDisabledTypes is skipped). | |
| 175 CONFIGURATION, | |
| 176 // The user disabled some types and we have to clean up the data | |
| 177 // for those. | |
| 178 CLEANUP_DISABLED_TYPES, | |
| 179 }; | |
| 180 SyncSessionJob(); | |
| 181 SyncSessionJob(SyncSessionJobPurpose purpose, base::TimeTicks start, | |
| 182 linked_ptr<sessions::SyncSession> session, bool is_canary_job, | |
| 183 const ConfigurationParams& config_params, | |
| 184 const tracked_objects::Location& nudge_location); | |
| 185 ~SyncSessionJob(); | |
| 186 static const char* GetPurposeString(SyncSessionJobPurpose purpose); | |
| 187 | |
| 188 SyncSessionJobPurpose purpose; | |
| 189 base::TimeTicks scheduled_start; | |
| 190 linked_ptr<sessions::SyncSession> session; | |
| 191 bool is_canary_job; | |
| 192 ConfigurationParams config_params; | |
| 193 | |
| 194 // This is the location the job came from. Used for debugging. | |
| 195 // In case of multiple nudges getting coalesced this stores the | |
| 196 // first location that came in. | |
| 197 tracked_objects::Location from_here; | |
| 198 }; | |
| 199 friend class SyncSchedulerTest; | |
| 200 friend class SyncSchedulerWhiteboxTest; | |
| 201 friend class SyncerTest; | |
| 202 | |
| 203 FRIEND_TEST_ALL_PREFIXES(SyncSchedulerWhiteboxTest, | |
| 204 DropNudgeWhileExponentialBackOff); | |
| 205 FRIEND_TEST_ALL_PREFIXES(SyncSchedulerWhiteboxTest, SaveNudge); | |
| 206 FRIEND_TEST_ALL_PREFIXES(SyncSchedulerWhiteboxTest, | |
| 207 SaveNudgeWhileTypeThrottled); | |
| 208 FRIEND_TEST_ALL_PREFIXES(SyncSchedulerWhiteboxTest, ContinueNudge); | |
| 209 FRIEND_TEST_ALL_PREFIXES(SyncSchedulerWhiteboxTest, DropPoll); | |
| 210 FRIEND_TEST_ALL_PREFIXES(SyncSchedulerWhiteboxTest, ContinuePoll); | |
| 211 FRIEND_TEST_ALL_PREFIXES(SyncSchedulerWhiteboxTest, ContinueConfiguration); | |
| 212 FRIEND_TEST_ALL_PREFIXES(SyncSchedulerWhiteboxTest, | |
| 213 SaveConfigurationWhileThrottled); | |
| 214 FRIEND_TEST_ALL_PREFIXES(SyncSchedulerWhiteboxTest, | |
| 215 SaveNudgeWhileThrottled); | |
| 216 FRIEND_TEST_ALL_PREFIXES(SyncSchedulerWhiteboxTest, | |
| 217 ContinueCanaryJobConfig); | |
| 218 FRIEND_TEST_ALL_PREFIXES(SyncSchedulerWhiteboxTest, | |
| 219 ContinueNudgeWhileExponentialBackOff); | |
| 220 FRIEND_TEST_ALL_PREFIXES(SyncSchedulerTest, TransientPollFailure); | |
| 221 | |
| 222 // A component used to get time delays associated with exponential backoff. | |
| 223 // Encapsulated into a class to facilitate testing. | |
| 224 class DelayProvider { | |
| 225 public: | |
| 226 DelayProvider(); | |
| 227 virtual base::TimeDelta GetDelay(const base::TimeDelta& last_delay); | |
| 228 virtual ~DelayProvider(); | |
| 229 private: | |
| 230 DISALLOW_COPY_AND_ASSIGN(DelayProvider); | |
| 231 }; | |
| 232 | |
| 233 struct WaitInterval { | |
| 234 enum Mode { | |
| 235 // Uninitialized state, should not be set in practice. | |
| 236 UNKNOWN = -1, | |
| 237 // A wait interval whose duration has been affected by exponential | |
| 238 // backoff. | |
| 239 // EXPONENTIAL_BACKOFF intervals are nudge-rate limited to 1 per interval. | |
| 240 EXPONENTIAL_BACKOFF, | |
| 241 // A server-initiated throttled interval. We do not allow any syncing | |
| 242 // during such an interval. | |
| 243 THROTTLED, | |
| 244 }; | |
| 245 WaitInterval(); | |
| 246 ~WaitInterval(); | |
| 247 WaitInterval(Mode mode, base::TimeDelta length); | |
| 248 | |
| 249 static const char* GetModeString(Mode mode); | |
| 250 | |
| 251 Mode mode; | |
| 252 | |
| 253 // This bool is set to true if we have observed a nudge during this | |
| 254 // interval and mode == EXPONENTIAL_BACKOFF. | |
| 255 bool had_nudge; | |
| 256 base::TimeDelta length; | |
| 257 base::OneShotTimer<SyncScheduler> timer; | |
| 258 | |
| 259 // Configure jobs are saved only when backing off or throttling. So we | |
| 260 // expose the pointer here. | |
| 261 scoped_ptr<SyncSessionJob> pending_configure_job; | |
| 262 }; | |
| 263 | |
| 264 static const char* GetModeString(Mode mode); | |
| 265 | |
| 266 static const char* GetDecisionString(JobProcessDecision decision); | |
| 267 | |
| 268 // Assign |start| and |end| to appropriate SyncerStep values for the | |
| 269 // specified |purpose|. | |
| 270 static void SetSyncerStepsForPurpose( | |
| 271 SyncSessionJob::SyncSessionJobPurpose purpose, | |
| 272 SyncerStep* start, SyncerStep* end); | |
| 273 | |
| 274 // Helpers that log before posting to |sync_loop_|. These will only post | |
| 275 // the task in between calls to Start/Stop. | |
| 276 void PostTask(const tracked_objects::Location& from_here, | |
| 277 const char* name, | |
| 278 const base::Closure& task); | |
| 279 void PostDelayedTask(const tracked_objects::Location& from_here, | |
| 280 const char* name, | |
| 281 const base::Closure& task, | |
| 282 base::TimeDelta delay); | |
| 283 | |
| 284 // Helper to assemble a job and post a delayed task to sync. | |
| 285 void ScheduleSyncSessionJob(const SyncSessionJob& job); | |
| 286 | |
| 287 // Invoke the Syncer to perform a sync. | |
| 288 void DoSyncSessionJob(const SyncSessionJob& job); | |
| 289 | |
| 290 // Called after the Syncer has performed the sync represented by |job|, to | |
| 291 // reset our state. | |
| 292 void FinishSyncSessionJob(const SyncSessionJob& job); | |
| 293 | |
| 294 // Record important state that might be needed in future syncs, such as which | |
| 295 // data types may require cleanup. | |
| 296 void UpdateCarryoverSessionState(const SyncSessionJob& old_job); | |
| 297 | |
| 298 // Helper to FinishSyncSessionJob to schedule the next sync operation. | |
| 299 void ScheduleNextSync(const SyncSessionJob& old_job); | |
| 300 | |
| 301 // Helper to configure polling intervals. Used by Start and ScheduleNextSync. | |
| 302 void AdjustPolling(const SyncSessionJob* old_job); | |
| 303 | |
| 304 // Helper to restart waiting with |wait_interval_|'s timer. | |
| 305 void RestartWaiting(); | |
| 306 | |
| 307 // Helper to ScheduleNextSync in case of consecutive sync errors. | |
| 308 void HandleContinuationError(const SyncSessionJob& old_job); | |
| 309 | |
| 310 // Determines if it is legal to run |job| by checking current | |
| 311 // operational mode, backoff or throttling, freshness | |
| 312 // (so we don't make redundant syncs), and connection. | |
| 313 bool ShouldRunJob(const SyncSessionJob& job); | |
| 314 | |
| 315 // Decide whether we should CONTINUE, SAVE or DROP the job. | |
| 316 JobProcessDecision DecideOnJob(const SyncSessionJob& job); | |
| 317 | |
| 318 // Decide on whether to CONTINUE, SAVE or DROP the job when we are in | |
| 319 // backoff mode. | |
| 320 JobProcessDecision DecideWhileInWaitInterval(const SyncSessionJob& job); | |
| 321 | |
| 322 // Saves the job for future execution. Note: It drops all the poll jobs. | |
| 323 void SaveJob(const SyncSessionJob& job); | |
| 324 | |
| 325 // Coalesces the current job with the pending nudge. | |
| 326 void InitOrCoalescePendingJob(const SyncSessionJob& job); | |
| 327 | |
| 328 // 'Impl' here refers to real implementation of public functions, running on | |
| 329 // |thread_|. | |
| 330 void StopImpl(const base::Closure& callback); | |
| 331 void ScheduleNudgeImpl( | |
| 332 const base::TimeDelta& delay, | |
| 333 sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source, | |
| 334 const syncer::ModelTypePayloadMap& types_with_payloads, | |
| 335 bool is_canary_job, const tracked_objects::Location& nudge_location); | |
| 336 | |
| 337 // Returns true if the client is currently in exponential backoff. | |
| 338 bool IsBackingOff() const; | |
| 339 | |
| 340 // Helper to signal all listeners registered with |session_context_|. | |
| 341 void Notify(SyncEngineEvent::EventCause cause); | |
| 342 | |
| 343 // Callback to change backoff state. | |
| 344 void DoCanaryJob(); | |
| 345 void Unthrottle(); | |
| 346 | |
| 347 // Executes the pending job. Called whenever an event occurs that may | |
| 348 // change conditions permitting a job to run. Like when network connection is | |
| 349 // re-established, mode changes etc. | |
| 350 void DoPendingJobIfPossible(bool is_canary_job); | |
| 351 | |
| 352 // Called when the root cause of the current connection error is fixed. | |
| 353 void OnServerConnectionErrorFixed(); | |
| 354 | |
| 355 // The pointer is owned by the caller. | |
| 356 syncer::sessions::SyncSession* CreateSyncSession( | |
| 357 const syncer::sessions::SyncSourceInfo& info); | |
| 358 | |
| 359 // Creates a session for a poll and performs the sync. | |
| 360 void PollTimerCallback(); | |
| 361 | |
| 362 // Used to update |connection_code_|, see below. | |
| 363 void UpdateServerConnectionManagerStatus( | |
| 364 HttpResponse::ServerConnectionCode code); | |
| 365 | |
| 366 // Called once the first time thread_ is started to broadcast an initial | |
| 367 // session snapshot containing data like initial_sync_ended. Important when | |
| 368 // the client starts up and does not need to perform an initial sync. | |
| 369 void SendInitialSnapshot(); | |
| 370 | |
| 371 virtual void OnActionableError(const sessions::SyncSessionSnapshot& snapshot); | |
| 372 | |
| 373 base::WeakPtrFactory<SyncScheduler> weak_ptr_factory_; | |
| 374 | |
| 375 // A second factory specially for weak_handle_this_, to allow the handle | |
| 376 // to be const and alleviate threading concerns. | |
| 377 base::WeakPtrFactory<SyncScheduler> weak_ptr_factory_for_weak_handle_; | |
| 378 | |
| 379 // For certain methods that need to worry about X-thread posting. | |
| 380 const WeakHandle<SyncScheduler> weak_handle_this_; | |
| 381 | |
| 382 // Used for logging. | |
| 383 const std::string name_; | |
| 384 | |
| 385 // The message loop this object is on. Almost all methods have to | |
| 386 // be called on this thread. | |
| 387 MessageLoop* const sync_loop_; | |
| 388 | |
| 389 // Set in Start(), unset in Stop(). | |
| 390 bool started_; | |
| 391 | |
| 392 // Modifiable versions of kDefaultLongPollIntervalSeconds which can be | |
| 393 // updated by the server. | |
| 394 base::TimeDelta syncer_short_poll_interval_seconds_; | |
| 395 base::TimeDelta syncer_long_poll_interval_seconds_; | |
| 396 | |
| 397 // Server-tweakable sessions commit delay. | |
| 398 base::TimeDelta sessions_commit_delay_; | |
| 399 | |
| 400 // Periodic timer for polling. See AdjustPolling. | |
| 401 base::RepeatingTimer<SyncScheduler> poll_timer_; | |
| 402 | |
| 403 // The mode of operation. | |
| 404 Mode mode_; | |
| 405 | |
| 406 // TODO(tim): Bug 26339. This needs to track more than just time I think, | |
| 407 // since the nudges could be for different types. Current impl doesn't care. | |
| 408 base::TimeTicks last_sync_session_end_time_; | |
| 409 | |
| 410 // The latest connection code we got while trying to connect. | |
| 411 HttpResponse::ServerConnectionCode connection_code_; | |
| 412 | |
| 413 // Tracks in-flight nudges so we can coalesce. | |
| 414 scoped_ptr<SyncSessionJob> pending_nudge_; | |
| 415 | |
| 416 // Current wait state. Null if we're not in backoff and not throttled. | |
| 417 scoped_ptr<WaitInterval> wait_interval_; | |
| 418 | |
| 419 scoped_ptr<DelayProvider> delay_provider_; | |
| 420 | |
| 421 // Invoked to run through the sync cycle. | |
| 422 scoped_ptr<Syncer> syncer_; | |
| 423 | |
| 424 sessions::SyncSessionContext *session_context_; | |
| 425 | |
| 426 DISALLOW_COPY_AND_ASSIGN(SyncScheduler); | |
| 427 }; | 115 }; |
| 428 | 116 |
| 429 } // namespace syncer | 117 } // namespace syncer |
| 430 | 118 |
| 431 #endif // SYNC_ENGINE_SYNC_SCHEDULER_H_ | 119 #endif // SYNC_ENGINE_SYNC_SCHEDULER_H_ |
| OLD | NEW |