Home | History | Annotate | Download | only in sessions
      1 // Copyright (c) 2013 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 #include "sync/sessions/nudge_tracker.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "sync/internal_api/public/engine/polling_constants.h"
      9 #include "sync/protocol/sync.pb.h"
     10 
     11 namespace syncer {
     12 namespace sessions {
     13 
     14 namespace {
     15 
     16 // Delays for syncer nudges.
     17 const int kDefaultNudgeDelayMilliseconds = 200;
     18 const int kSlowNudgeDelayMilliseconds = 2000;
     19 const int kDefaultSessionsCommitDelaySeconds = 10;
     20 const int kSyncRefreshDelayMilliseconds = 500;
     21 const int kSyncSchedulerDelayMilliseconds = 250;
     22 
     23 base::TimeDelta GetDefaultDelayForType(ModelType model_type,
     24                                        base::TimeDelta minimum_delay) {
     25   switch (model_type) {
     26    case AUTOFILL:
     27      // Accompany types rely on nudges from other types, and hence have long
     28      // nudge delays.
     29      return base::TimeDelta::FromSeconds(kDefaultShortPollIntervalSeconds);
     30    case BOOKMARKS:
     31    case PREFERENCES:
     32      // Types with sometimes automatic changes get longer delays to allow more
     33      // coalescing.
     34      return base::TimeDelta::FromMilliseconds(kSlowNudgeDelayMilliseconds);
     35    case SESSIONS:
     36    case FAVICON_IMAGES:
     37    case FAVICON_TRACKING:
     38      // Types with navigation triggered changes get longer delays to allow more
     39      // coalescing.
     40      return base::TimeDelta::FromSeconds(kDefaultSessionsCommitDelaySeconds);
     41    default:
     42      return minimum_delay;
     43   }
     44 }
     45 
     46 }  // namespace
     47 
     48 size_t NudgeTracker::kDefaultMaxPayloadsPerType = 10;
     49 
     50 NudgeTracker::NudgeTracker()
     51     : type_tracker_deleter_(&type_trackers_),
     52       invalidations_enabled_(false),
     53       invalidations_out_of_sync_(true),
     54       minimum_local_nudge_delay_(
     55           base::TimeDelta::FromMilliseconds(kDefaultNudgeDelayMilliseconds)),
     56       local_refresh_nudge_delay_(
     57           base::TimeDelta::FromMilliseconds(kSyncRefreshDelayMilliseconds)),
     58       remote_invalidation_nudge_delay_(
     59           base::TimeDelta::FromMilliseconds(kSyncSchedulerDelayMilliseconds)) {
     60   ModelTypeSet protocol_types = ProtocolTypes();
     61   // Default initialize all the type trackers.
     62   for (ModelTypeSet::Iterator it = protocol_types.First(); it.Good();
     63        it.Inc()) {
     64     type_trackers_.insert(std::make_pair(it.Get(), new DataTypeTracker()));
     65   }
     66 }
     67 
     68 NudgeTracker::~NudgeTracker() { }
     69 
     70 bool NudgeTracker::IsSyncRequired() const {
     71   if (IsRetryRequired())
     72     return true;
     73 
     74   for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
     75        it != type_trackers_.end(); ++it) {
     76     if (it->second->IsSyncRequired()) {
     77       return true;
     78     }
     79   }
     80 
     81   return false;
     82 }
     83 
     84 bool NudgeTracker::IsGetUpdatesRequired() const {
     85   if (invalidations_out_of_sync_)
     86     return true;
     87 
     88   if (IsRetryRequired())
     89     return true;
     90 
     91   for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
     92        it != type_trackers_.end(); ++it) {
     93     if (it->second->IsGetUpdatesRequired()) {
     94       return true;
     95     }
     96   }
     97   return false;
     98 }
     99 
    100 bool NudgeTracker::IsRetryRequired() const {
    101   if (sync_cycle_start_time_.is_null())
    102     return false;
    103 
    104   if (current_retry_time_.is_null())
    105     return false;
    106 
    107   return current_retry_time_ <= sync_cycle_start_time_;
    108 }
    109 
    110 void NudgeTracker::RecordSuccessfulSyncCycle() {
    111   // If a retry was required, we've just serviced it.  Unset the flag.
    112   if (IsRetryRequired())
    113     current_retry_time_ = base::TimeTicks();
    114 
    115   // A successful cycle while invalidations are enabled puts us back into sync.
    116   invalidations_out_of_sync_ = !invalidations_enabled_;
    117 
    118   for (TypeTrackerMap::iterator it = type_trackers_.begin();
    119        it != type_trackers_.end(); ++it) {
    120     it->second->RecordSuccessfulSyncCycle();
    121   }
    122 }
    123 
    124 base::TimeDelta NudgeTracker::RecordLocalChange(ModelTypeSet types) {
    125   // Start with the longest delay.
    126   base::TimeDelta delay =
    127       base::TimeDelta::FromMilliseconds(kDefaultShortPollIntervalSeconds);
    128   for (ModelTypeSet::Iterator type_it = types.First(); type_it.Good();
    129        type_it.Inc()) {
    130     TypeTrackerMap::iterator tracker_it = type_trackers_.find(type_it.Get());
    131     DCHECK(tracker_it != type_trackers_.end());
    132 
    133     // Only if the type tracker has a valid delay (non-zero) that is shorter
    134     // than the calculated delay do we update the calculated delay.
    135     base::TimeDelta type_delay = tracker_it->second->RecordLocalChange();
    136     if (type_delay == base::TimeDelta()) {
    137       type_delay = GetDefaultDelayForType(type_it.Get(),
    138                                           minimum_local_nudge_delay_);
    139     }
    140     if (type_delay < delay)
    141       delay = type_delay;
    142   }
    143   return delay;
    144 }
    145 
    146 base::TimeDelta NudgeTracker::RecordLocalRefreshRequest(ModelTypeSet types) {
    147   for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) {
    148     TypeTrackerMap::iterator tracker_it = type_trackers_.find(it.Get());
    149     DCHECK(tracker_it != type_trackers_.end());
    150     tracker_it->second->RecordLocalRefreshRequest();
    151   }
    152   return local_refresh_nudge_delay_;
    153 }
    154 
    155 base::TimeDelta NudgeTracker::RecordRemoteInvalidation(
    156     syncer::ModelType type,
    157     scoped_ptr<InvalidationInterface> invalidation) {
    158   // Forward the invalidations to the proper recipient.
    159   TypeTrackerMap::iterator tracker_it = type_trackers_.find(type);
    160   DCHECK(tracker_it != type_trackers_.end());
    161   tracker_it->second->RecordRemoteInvalidation(invalidation.Pass());
    162   return remote_invalidation_nudge_delay_;
    163 }
    164 
    165 void NudgeTracker::RecordInitialSyncRequired(syncer::ModelType type) {
    166   TypeTrackerMap::iterator tracker_it = type_trackers_.find(type);
    167   DCHECK(tracker_it != type_trackers_.end());
    168   tracker_it->second->RecordInitialSyncRequired();
    169 }
    170 
    171 void NudgeTracker::OnInvalidationsEnabled() {
    172   invalidations_enabled_ = true;
    173 }
    174 
    175 void NudgeTracker::OnInvalidationsDisabled() {
    176   invalidations_enabled_ = false;
    177   invalidations_out_of_sync_ = true;
    178 }
    179 
    180 void NudgeTracker::SetTypesThrottledUntil(
    181     ModelTypeSet types,
    182     base::TimeDelta length,
    183     base::TimeTicks now) {
    184   for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) {
    185     TypeTrackerMap::iterator tracker_it = type_trackers_.find(it.Get());
    186     tracker_it->second->ThrottleType(length, now);
    187   }
    188 }
    189 
    190 void NudgeTracker::UpdateTypeThrottlingState(base::TimeTicks now) {
    191   for (TypeTrackerMap::iterator it = type_trackers_.begin();
    192        it != type_trackers_.end(); ++it) {
    193     it->second->UpdateThrottleState(now);
    194   }
    195 }
    196 
    197 bool NudgeTracker::IsAnyTypeThrottled() const {
    198   for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
    199        it != type_trackers_.end(); ++it) {
    200     if (it->second->IsThrottled()) {
    201       return true;
    202     }
    203   }
    204   return false;
    205 }
    206 
    207 bool NudgeTracker::IsTypeThrottled(ModelType type) const {
    208   DCHECK(type_trackers_.find(type) != type_trackers_.end());
    209   return type_trackers_.find(type)->second->IsThrottled();
    210 }
    211 
    212 base::TimeDelta NudgeTracker::GetTimeUntilNextUnthrottle(
    213     base::TimeTicks now) const {
    214   DCHECK(IsAnyTypeThrottled()) << "This function requires a pending unthrottle";
    215 
    216   // Return min of GetTimeUntilUnthrottle() values for all IsThrottled() types.
    217   base::TimeDelta time_until_next_unthrottle = base::TimeDelta::Max();
    218   for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
    219        it != type_trackers_.end(); ++it) {
    220     if (it->second->IsThrottled()) {
    221       time_until_next_unthrottle = std::min(
    222           time_until_next_unthrottle, it->second->GetTimeUntilUnthrottle(now));
    223     }
    224   }
    225   DCHECK(!time_until_next_unthrottle.is_max());
    226 
    227   return time_until_next_unthrottle;
    228 }
    229 
    230 ModelTypeSet NudgeTracker::GetThrottledTypes() const {
    231   ModelTypeSet result;
    232   for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
    233        it != type_trackers_.end(); ++it) {
    234     if (it->second->IsThrottled()) {
    235       result.Put(it->first);
    236     }
    237   }
    238   return result;
    239 }
    240 
    241 ModelTypeSet NudgeTracker::GetNudgedTypes() const {
    242   ModelTypeSet result;
    243   for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
    244        it != type_trackers_.end(); ++it) {
    245     if (it->second->HasLocalChangePending()) {
    246       result.Put(it->first);
    247     }
    248   }
    249   return result;
    250 }
    251 
    252 ModelTypeSet NudgeTracker::GetNotifiedTypes() const {
    253   ModelTypeSet result;
    254   for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
    255        it != type_trackers_.end(); ++it) {
    256     if (it->second->HasPendingInvalidation()) {
    257       result.Put(it->first);
    258     }
    259   }
    260   return result;
    261 }
    262 
    263 ModelTypeSet NudgeTracker::GetRefreshRequestedTypes() const {
    264   ModelTypeSet result;
    265   for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
    266        it != type_trackers_.end(); ++it) {
    267     if (it->second->HasRefreshRequestPending()) {
    268       result.Put(it->first);
    269     }
    270   }
    271   return result;
    272 }
    273 
    274 void NudgeTracker::SetLegacyNotificationHint(
    275     ModelType type,
    276     sync_pb::DataTypeProgressMarker* progress) const {
    277   DCHECK(type_trackers_.find(type) != type_trackers_.end());
    278   type_trackers_.find(type)->second->SetLegacyNotificationHint(progress);
    279 }
    280 
    281 sync_pb::GetUpdatesCallerInfo::GetUpdatesSource NudgeTracker::GetLegacySource()
    282     const {
    283   // There's an order to these sources: NOTIFICATION, DATATYPE_REFRESH, LOCAL,
    284   // RETRY.  The server makes optimization decisions based on this field, so
    285   // it's important to get this right.  Setting it wrong could lead to missed
    286   // updates.
    287   //
    288   // This complexity is part of the reason why we're deprecating 'source' in
    289   // favor of 'origin'.
    290   bool has_invalidation_pending = false;
    291   bool has_refresh_request_pending = false;
    292   bool has_commit_pending = false;
    293   bool is_initial_sync_required = false;
    294   bool has_retry = IsRetryRequired();
    295 
    296   for (TypeTrackerMap::const_iterator it = type_trackers_.begin();
    297        it != type_trackers_.end(); ++it) {
    298     const DataTypeTracker& tracker = *it->second;
    299     if (!tracker.IsThrottled() && tracker.HasPendingInvalidation()) {
    300       has_invalidation_pending = true;
    301     }
    302     if (!tracker.IsThrottled() && tracker.HasRefreshRequestPending()) {
    303       has_refresh_request_pending = true;
    304     }
    305     if (!tracker.IsThrottled() && tracker.HasLocalChangePending()) {
    306       has_commit_pending = true;
    307     }
    308     if (!tracker.IsThrottled() && tracker.IsInitialSyncRequired()) {
    309       is_initial_sync_required = true;
    310     }
    311   }
    312 
    313   if (has_invalidation_pending) {
    314     return sync_pb::GetUpdatesCallerInfo::NOTIFICATION;
    315   } else if (has_refresh_request_pending) {
    316     return sync_pb::GetUpdatesCallerInfo::DATATYPE_REFRESH;
    317   } else if (is_initial_sync_required) {
    318     // Not quite accurate, but good enough for our purposes.  This setting of
    319     // SOURCE is just a backward-compatibility hack anyway.
    320     return sync_pb::GetUpdatesCallerInfo::DATATYPE_REFRESH;
    321   } else if (has_commit_pending) {
    322     return sync_pb::GetUpdatesCallerInfo::LOCAL;
    323   } else if (has_retry) {
    324     return sync_pb::GetUpdatesCallerInfo::RETRY;
    325   } else {
    326     return sync_pb::GetUpdatesCallerInfo::UNKNOWN;
    327   }
    328 }
    329 
    330 void NudgeTracker::FillProtoMessage(
    331     ModelType type,
    332     sync_pb::GetUpdateTriggers* msg) const {
    333   DCHECK(type_trackers_.find(type) != type_trackers_.end());
    334 
    335   // Fill what we can from the global data.
    336   msg->set_invalidations_out_of_sync(invalidations_out_of_sync_);
    337 
    338   // Delegate the type-specific work to the DataTypeTracker class.
    339   type_trackers_.find(type)->second->FillGetUpdatesTriggersMessage(msg);
    340 }
    341 
    342 void NudgeTracker::SetSyncCycleStartTime(base::TimeTicks now) {
    343   sync_cycle_start_time_ = now;
    344 
    345   // If current_retry_time_ is still set, that means we have an old retry time
    346   // left over from a previous cycle.  For example, maybe we tried to perform
    347   // this retry, hit a network connection error, and now we're in exponential
    348   // backoff.  In that case, we want this sync cycle to include the GU retry
    349   // flag so we leave this variable set regardless of whether or not there is an
    350   // overwrite pending.
    351   if (!current_retry_time_.is_null()) {
    352     return;
    353   }
    354 
    355   // If do not have a current_retry_time_, but we do have a next_retry_time_ and
    356   // it is ready to go, then we set it as the current_retry_time_.  It will stay
    357   // there until a GU retry has succeeded.
    358   if (!next_retry_time_.is_null() &&
    359       next_retry_time_ <= sync_cycle_start_time_) {
    360     current_retry_time_ = next_retry_time_;
    361     next_retry_time_ = base::TimeTicks();
    362   }
    363 }
    364 
    365 void NudgeTracker::SetHintBufferSize(size_t size) {
    366   for (TypeTrackerMap::iterator it = type_trackers_.begin();
    367        it != type_trackers_.end(); ++it) {
    368     it->second->UpdatePayloadBufferSize(size);
    369   }
    370 }
    371 
    372 void NudgeTracker::SetNextRetryTime(base::TimeTicks retry_time) {
    373   next_retry_time_ = retry_time;
    374 }
    375 
    376 void NudgeTracker::OnReceivedCustomNudgeDelays(
    377     const std::map<ModelType, base::TimeDelta>& delay_map) {
    378   for (std::map<ModelType, base::TimeDelta>::const_iterator iter =
    379            delay_map.begin();
    380        iter != delay_map.end();
    381        ++iter) {
    382     ModelType type = iter->first;
    383     DCHECK(syncer::ProtocolTypes().Has(type));
    384     TypeTrackerMap::iterator type_iter = type_trackers_.find(type);
    385     if (type_iter == type_trackers_.end())
    386       continue;
    387 
    388     if (iter->second > minimum_local_nudge_delay_) {
    389       type_iter->second->UpdateLocalNudgeDelay(iter->second);
    390     } else {
    391       type_iter->second->UpdateLocalNudgeDelay(
    392           GetDefaultDelayForType(type,
    393                                  minimum_local_nudge_delay_));
    394     }
    395   }
    396 }
    397 
    398 void NudgeTracker::SetDefaultNudgeDelay(base::TimeDelta nudge_delay) {
    399   minimum_local_nudge_delay_ = nudge_delay;
    400 }
    401 
    402 }  // namespace sessions
    403 }  // namespace syncer
    404