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