• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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