Home | History | Annotate | Download | only in engine
      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