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/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