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       ModelTypeSet types,
     60       const tracked_objects::Location& nudge_location) OVERRIDE;
     61   virtual void ScheduleLocalRefreshRequest(
     62       ModelTypeSet types,
     63       const tracked_objects::Location& nudge_location) OVERRIDE;
     64   virtual void ScheduleInvalidationNudge(
     65       syncer::ModelType type,
     66       scoped_ptr<InvalidationInterface> invalidation,
     67       const tracked_objects::Location& nudge_location) OVERRIDE;
     68   virtual void ScheduleInitialSyncNudge(syncer::ModelType model_type) OVERRIDE;
     69   virtual void SetNotificationsEnabled(bool notifications_enabled) OVERRIDE;
     70 
     71   virtual void OnCredentialsUpdated() OVERRIDE;
     72   virtual void OnConnectionStatusChange() OVERRIDE;
     73 
     74   // SyncSession::Delegate implementation.
     75   virtual void OnThrottled(const base::TimeDelta& throttle_duration) OVERRIDE;
     76   virtual void OnTypesThrottled(
     77       ModelTypeSet types,
     78       const base::TimeDelta& throttle_duration) OVERRIDE;
     79   virtual bool IsCurrentlyThrottled() OVERRIDE;
     80   virtual void OnReceivedShortPollIntervalUpdate(
     81       const base::TimeDelta& new_interval) OVERRIDE;
     82   virtual void OnReceivedLongPollIntervalUpdate(
     83       const base::TimeDelta& new_interval) OVERRIDE;
     84   virtual void OnReceivedCustomNudgeDelays(
     85       const std::map<ModelType, base::TimeDelta>& nudge_delays) OVERRIDE;
     86   virtual void OnReceivedClientInvalidationHintBufferSize(int size) OVERRIDE;
     87   virtual void OnSyncProtocolError(
     88       const SyncProtocolError& sync_protocol_error) OVERRIDE;
     89   virtual void OnReceivedGuRetryDelay(const base::TimeDelta& delay) OVERRIDE;
     90   virtual void OnReceivedMigrationRequest(syncer::ModelTypeSet types) OVERRIDE;
     91 
     92   // Returns true if the client is currently in exponential backoff.
     93   bool IsBackingOff() const;
     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(SyncSchedulerTest, TransientPollFailure);
    115   FRIEND_TEST_ALL_PREFIXES(SyncSchedulerTest,
    116                            ServerConnectionChangeDuringBackoff);
    117   FRIEND_TEST_ALL_PREFIXES(SyncSchedulerTest,
    118                            ConnectionChangeCanaryPreemptedByNudge);
    119   FRIEND_TEST_ALL_PREFIXES(BackoffTriggersSyncSchedulerTest,
    120                            FailGetEncryptionKey);
    121   FRIEND_TEST_ALL_PREFIXES(SyncSchedulerTest, SuccessfulRetry);
    122   FRIEND_TEST_ALL_PREFIXES(SyncSchedulerTest, FailedRetry);
    123   FRIEND_TEST_ALL_PREFIXES(SyncSchedulerTest, ReceiveNewRetryDelay);
    124 
    125   struct SYNC_EXPORT_PRIVATE WaitInterval {
    126     enum Mode {
    127       // Uninitialized state, should not be set in practice.
    128       UNKNOWN = -1,
    129       // We enter a series of increasingly longer WaitIntervals if we experience
    130       // repeated transient failures.  We retry at the end of each interval.
    131       EXPONENTIAL_BACKOFF,
    132       // A server-initiated throttled interval.  We do not allow any syncing
    133       // during such an interval.
    134       THROTTLED,
    135     };
    136     WaitInterval();
    137     ~WaitInterval();
    138     WaitInterval(Mode mode, base::TimeDelta length);
    139 
    140     static const char* GetModeString(Mode mode);
    141 
    142     Mode mode;
    143     base::TimeDelta length;
    144   };
    145 
    146   static const char* GetModeString(Mode mode);
    147 
    148   void SetDefaultNudgeDelay(base::TimeDelta delay_ms);
    149 
    150   // Invoke the syncer to perform a nudge job.
    151   void DoNudgeSyncSessionJob(JobPriority priority);
    152 
    153   // Invoke the syncer to perform a configuration job.
    154   void DoConfigurationSyncSessionJob(JobPriority priority);
    155 
    156   // Helper function for Do{Nudge,Configuration}SyncSessionJob.
    157   void HandleFailure(
    158       const sessions::ModelNeutralState& model_neutral_state);
    159 
    160   // Invoke the Syncer to perform a poll job.
    161   void DoPollSyncSessionJob();
    162 
    163   // Helper function to calculate poll interval.
    164   base::TimeDelta GetPollInterval();
    165 
    166   // Adjusts the poll timer to account for new poll interval, and possibly
    167   // resets the poll interval, depedning on the flag's value.
    168   void AdjustPolling(PollAdjustType type);
    169 
    170   // Helper to restart waiting with |wait_interval_|'s timer.
    171   void RestartWaiting();
    172 
    173   // Determines if we're allowed to contact the server right now.
    174   bool CanRunJobNow(JobPriority priority);
    175 
    176   // Determines if we're allowed to contact the server right now.
    177   bool CanRunNudgeJobNow(JobPriority priority);
    178 
    179   // If the scheduler's current state supports it, this will create a job based
    180   // on the passed in parameters and coalesce it with any other pending jobs,
    181   // then post a delayed task to run it.  It may also choose to drop the job or
    182   // save it for later, depending on the scheduler's current state.
    183   void ScheduleNudgeImpl(
    184       const base::TimeDelta& delay,
    185       const tracked_objects::Location& nudge_location);
    186 
    187   // Helper to signal listeners about changed retry time.
    188   void NotifyRetryTime(base::Time retry_time);
    189 
    190   // Helper to signal listeners about changed throttled types.
    191   void NotifyThrottledTypesChanged(ModelTypeSet types);
    192 
    193   // Looks for pending work and, if it finds any, run this work at "canary"
    194   // priority.
    195   void TryCanaryJob();
    196 
    197   // At the moment TrySyncSessionJob just posts call to TrySyncSessionJobImpl on
    198   // current thread. In the future it will request access token here.
    199   void TrySyncSessionJob();
    200   void TrySyncSessionJobImpl();
    201 
    202   // Transitions out of the THROTTLED WaitInterval then calls TryCanaryJob().
    203   void Unthrottle();
    204 
    205   // Called when a per-type throttling interval expires.
    206   void TypeUnthrottle(base::TimeTicks unthrottle_time);
    207 
    208   // Runs a normal nudge job when the scheduled timer expires.
    209   void PerformDelayedNudge();
    210 
    211   // Attempts to exit EXPONENTIAL_BACKOFF by calling TryCanaryJob().
    212   void ExponentialBackoffRetry();
    213 
    214   // Called when the root cause of the current connection error is fixed.
    215   void OnServerConnectionErrorFixed();
    216 
    217   // Creates a session for a poll and performs the sync.
    218   void PollTimerCallback();
    219 
    220   // Creates a session for a retry and performs the sync.
    221   void RetryTimerCallback();
    222 
    223   // Returns the set of types that are enabled and not currently throttled.
    224   ModelTypeSet GetEnabledAndUnthrottledTypes();
    225 
    226   // Called as we are started to broadcast an initial session snapshot
    227   // containing data like initial_sync_ended.  Important when the client starts
    228   // up and does not need to perform an initial sync.
    229   void SendInitialSnapshot();
    230 
    231   // This is used for histogramming and analysis of ScheduleNudge* APIs.
    232   // SyncScheduler is the ultimate choke-point for all such invocations (with
    233   // and without InvalidationState variants, all NudgeSources, etc) and as such
    234   // is the most flexible place to do this bookkeeping.
    235   void UpdateNudgeTimeRecords(ModelTypeSet types);
    236 
    237   // For certain methods that need to worry about X-thread posting.
    238   WeakHandle<SyncSchedulerImpl> weak_handle_this_;
    239 
    240   // Used for logging.
    241   const std::string name_;
    242 
    243   // Set in Start(), unset in Stop().
    244   bool started_;
    245 
    246   // Modifiable versions of kDefaultLongPollIntervalSeconds which can be
    247   // updated by the server.
    248   base::TimeDelta syncer_short_poll_interval_seconds_;
    249   base::TimeDelta syncer_long_poll_interval_seconds_;
    250 
    251   // Periodic timer for polling.  See AdjustPolling.
    252   base::RepeatingTimer<SyncSchedulerImpl> poll_timer_;
    253 
    254   // The mode of operation.
    255   Mode mode_;
    256 
    257   // Current wait state.  Null if we're not in backoff and not throttled.
    258   scoped_ptr<WaitInterval> wait_interval_;
    259 
    260   scoped_ptr<BackoffDelayProvider> delay_provider_;
    261 
    262   // The event that will wake us up.
    263   base::OneShotTimer<SyncSchedulerImpl> pending_wakeup_timer_;
    264 
    265   // An event that fires when data type throttling expires.
    266   base::OneShotTimer<SyncSchedulerImpl> type_unthrottle_timer_;
    267 
    268   // Storage for variables related to an in-progress configure request.  Note
    269   // that (mode_ != CONFIGURATION_MODE) \implies !pending_configure_params_.
    270   scoped_ptr<ConfigurationParams> pending_configure_params_;
    271 
    272   // If we have a nudge pending to run soon, it will be listed here.
    273   base::TimeTicks scheduled_nudge_time_;
    274 
    275   // Keeps track of work that the syncer needs to handle.
    276   sessions::NudgeTracker nudge_tracker_;
    277 
    278   // Invoked to run through the sync cycle.
    279   scoped_ptr<Syncer> syncer_;
    280 
    281   sessions::SyncSessionContext* session_context_;
    282 
    283   // A map tracking LOCAL NudgeSource invocations of ScheduleNudge* APIs,
    284   // organized by datatype. Each datatype that was part of the types requested
    285   // in the call will have its TimeTicks value updated.
    286   typedef std::map<ModelType, base::TimeTicks> ModelTypeTimeMap;
    287   ModelTypeTimeMap last_local_nudges_by_model_type_;
    288 
    289   // Used as an "anti-reentrancy defensive assertion".
    290   // While true, it is illegal for any new scheduling activity to take place.
    291   // Ensures that higher layers don't break this law in response to events that
    292   // take place during a sync cycle. We call this out because such violations
    293   // could result in tight sync loops hitting sync servers.
    294   bool no_scheduling_allowed_;
    295 
    296   // crbug/251307. This is a workaround for M29. crbug/259913 tracks proper fix
    297   // for M30.
    298   // The issue is that poll job runs after few hours of inactivity and therefore
    299   // will always fail with auth error because of expired access token. Once
    300   // fresh access token is requested poll job is not retried.
    301   // The change is to remember that poll timer just fired and retry poll job
    302   // after credentials are updated.
    303   bool do_poll_after_credentials_updated_;
    304 
    305   // TryJob might get called for multiple reasons. It should only call
    306   // DoPollSyncSessionJob after some time since the last attempt.
    307   // last_poll_reset_ keeps track of when was last attempt.
    308   base::TimeTicks last_poll_reset_;
    309 
    310   // next_sync_session_job_priority_ defines which priority will be used next
    311   // time TrySyncSessionJobImpl is called. CANARY_PRIORITY allows syncer to run
    312   // even if scheduler is in exponential backoff. This is needed for events that
    313   // have chance of resolving previous error (e.g. network connection change
    314   // after NETWORK_UNAVAILABLE error).
    315   // It is reset back to NORMAL_PRIORITY on every call to TrySyncSessionJobImpl.
    316   JobPriority next_sync_session_job_priority_;
    317 
    318   // One-shot timer for scheduling GU retry according to delay set by server.
    319   base::OneShotTimer<SyncSchedulerImpl> retry_timer_;
    320 
    321   base::WeakPtrFactory<SyncSchedulerImpl> weak_ptr_factory_;
    322 
    323   // A second factory specially for weak_handle_this_, to allow the handle
    324   // to be const and alleviate threading concerns.
    325   base::WeakPtrFactory<SyncSchedulerImpl> weak_ptr_factory_for_weak_handle_;
    326 
    327   DISALLOW_COPY_AND_ASSIGN(SyncSchedulerImpl);
    328 };
    329 
    330 }  // namespace syncer
    331 
    332 #endif  // SYNC_ENGINE_SYNC_SCHEDULER_IMPL_H_
    333