Home | History | Annotate | Download | only in sessions
      1 // Copyright 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/data_type_tracker.h"
      6 
      7 #include "base/logging.h"
      8 #include "sync/internal_api/public/base/invalidation_interface.h"
      9 #include "sync/sessions/nudge_tracker.h"
     10 
     11 namespace syncer {
     12 namespace sessions {
     13 
     14 DataTypeTracker::DataTypeTracker()
     15     : local_nudge_count_(0),
     16       local_refresh_request_count_(0),
     17       payload_buffer_size_(NudgeTracker::kDefaultMaxPayloadsPerType),
     18       initial_sync_required_(false) {
     19 }
     20 
     21 DataTypeTracker::~DataTypeTracker() { }
     22 
     23 base::TimeDelta DataTypeTracker::RecordLocalChange() {
     24   local_nudge_count_++;
     25   return nudge_delay_;
     26 }
     27 
     28 void DataTypeTracker::RecordLocalRefreshRequest() {
     29   local_refresh_request_count_++;
     30 }
     31 
     32 void DataTypeTracker::RecordRemoteInvalidation(
     33     scoped_ptr<InvalidationInterface> incoming) {
     34   DCHECK(incoming);
     35 
     36   // Merge the incoming invalidation into our list of pending invalidations.
     37   //
     38   // We won't use STL algorithms here because our concept of equality doesn't
     39   // quite fit the expectations of set_intersection.  In particular, two
     40   // invalidations can be equal according to the SingleObjectInvalidationSet's
     41   // rules (ie. have equal versions), but still have different AckHandle values
     42   // and need to be acknowledged separately.
     43   //
     44   // The invalidations service can only track one outsanding invalidation per
     45   // type and version, so the acknowledgement here should be redundant.  We'll
     46   // acknowledge them anyway since it should do no harm, and makes this code a
     47   // bit easier to test.
     48   //
     49   // Overlaps should be extremely rare for most invalidations.  They can happen
     50   // for unknown version invalidations, though.
     51 
     52   ScopedVector<InvalidationInterface>::iterator it =
     53       pending_invalidations_.begin();
     54 
     55   // Find the lower bound.
     56   while (it != pending_invalidations_.end() &&
     57          InvalidationInterface::LessThanByVersion(**it, *incoming)) {
     58     it++;
     59   }
     60 
     61   if (it != pending_invalidations_.end() &&
     62       !InvalidationInterface::LessThanByVersion(*incoming, **it) &&
     63       !InvalidationInterface::LessThanByVersion(**it, *incoming)) {
     64     // Incoming overlaps with existing.  Either both are unknown versions
     65     // (likely) or these two have the same version number (very unlikely).
     66     // Acknowledge and overwrite existing.
     67 
     68     // Insert before the existing and get iterator to inserted.
     69     ScopedVector<InvalidationInterface>::iterator it2 =
     70         pending_invalidations_.insert(it, incoming.release());
     71 
     72     // Increment that iterator to the old one, then acknowledge and remove it.
     73     ++it2;
     74     (*it2)->Acknowledge();
     75     pending_invalidations_.erase(it2);
     76   } else {
     77     // The incoming has a version not in the pending_invalidations_ list.
     78     // Add it to the list at the proper position.
     79     pending_invalidations_.insert(it, incoming.release());
     80   }
     81 
     82   // The incoming invalidation may have caused us to exceed our buffer size.
     83   // Trim some items from our list, if necessary.
     84   while (pending_invalidations_.size() > payload_buffer_size_) {
     85     last_dropped_invalidation_.reset(pending_invalidations_.front());
     86     last_dropped_invalidation_->Drop();
     87     pending_invalidations_.weak_erase(pending_invalidations_.begin());
     88   }
     89 }
     90 
     91 void DataTypeTracker::RecordInitialSyncRequired() {
     92   initial_sync_required_ = true;
     93 }
     94 
     95 void DataTypeTracker::RecordSuccessfulSyncCycle() {
     96   // If we were throttled, then we would have been excluded from this cycle's
     97   // GetUpdates and Commit actions.  Our state remains unchanged.
     98   if (IsThrottled())
     99     return;
    100 
    101   local_nudge_count_ = 0;
    102   local_refresh_request_count_ = 0;
    103 
    104   // TODO(rlarocque): If we want this to be correct even if we should happen to
    105   // crash before writing all our state, we should wait until the results of
    106   // this sync cycle have been written to disk before updating the invalidations
    107   // state.  See crbug.com/324996.
    108   for (ScopedVector<InvalidationInterface>::const_iterator it =
    109            pending_invalidations_.begin();
    110        it != pending_invalidations_.end();
    111        ++it) {
    112     (*it)->Acknowledge();
    113   }
    114   pending_invalidations_.clear();
    115 
    116   if (last_dropped_invalidation_) {
    117     last_dropped_invalidation_->Acknowledge();
    118     last_dropped_invalidation_.reset();
    119   }
    120 
    121   initial_sync_required_ = false;
    122 }
    123 
    124 // This limit will take effect on all future invalidations received.
    125 void DataTypeTracker::UpdatePayloadBufferSize(size_t new_size) {
    126   payload_buffer_size_ = new_size;
    127 }
    128 
    129 bool DataTypeTracker::IsSyncRequired() const {
    130   return !IsThrottled() && (HasLocalChangePending() || IsGetUpdatesRequired());
    131 }
    132 
    133 bool DataTypeTracker::IsGetUpdatesRequired() const {
    134   return !IsThrottled() &&
    135          (HasRefreshRequestPending() || HasPendingInvalidation() ||
    136           IsInitialSyncRequired());
    137 }
    138 
    139 bool DataTypeTracker::HasLocalChangePending() const {
    140   return local_nudge_count_ > 0;
    141 }
    142 
    143 bool DataTypeTracker::HasRefreshRequestPending() const {
    144   return local_refresh_request_count_ > 0;
    145 }
    146 
    147 bool DataTypeTracker::HasPendingInvalidation() const {
    148   return !pending_invalidations_.empty() || last_dropped_invalidation_;
    149 }
    150 
    151 bool DataTypeTracker::IsInitialSyncRequired() const {
    152   return initial_sync_required_;
    153 }
    154 
    155 void DataTypeTracker::SetLegacyNotificationHint(
    156     sync_pb::DataTypeProgressMarker* progress) const {
    157   DCHECK(!IsThrottled())
    158       << "We should not make requests if the type is throttled.";
    159 
    160   if (!pending_invalidations_.empty() &&
    161       !pending_invalidations_.back()->IsUnknownVersion()) {
    162     // The old-style source info can contain only one hint per type.  We grab
    163     // the most recent, to mimic the old coalescing behaviour.
    164     progress->set_notification_hint(
    165         pending_invalidations_.back()->GetPayload());
    166   } else if (HasLocalChangePending()) {
    167     // The old-style source info sent up an empty string (as opposed to
    168     // nothing at all) when the type was locally nudged, but had not received
    169     // any invalidations.
    170     progress->set_notification_hint(std::string());
    171   }
    172 }
    173 
    174 void DataTypeTracker::FillGetUpdatesTriggersMessage(
    175     sync_pb::GetUpdateTriggers* msg) const {
    176   // Fill the list of payloads, if applicable.  The payloads must be ordered
    177   // oldest to newest, so we insert them in the same order as we've been storing
    178   // them internally.
    179   for (ScopedVector<InvalidationInterface>::const_iterator it =
    180            pending_invalidations_.begin();
    181        it != pending_invalidations_.end();
    182        ++it) {
    183     if (!(*it)->IsUnknownVersion()) {
    184       msg->add_notification_hint((*it)->GetPayload());
    185     }
    186   }
    187 
    188   msg->set_server_dropped_hints(
    189       !pending_invalidations_.empty() &&
    190       (*pending_invalidations_.begin())->IsUnknownVersion());
    191   msg->set_client_dropped_hints(last_dropped_invalidation_);
    192   msg->set_local_modification_nudges(local_nudge_count_);
    193   msg->set_datatype_refresh_nudges(local_refresh_request_count_);
    194   msg->set_initial_sync_in_progress(initial_sync_required_);
    195 }
    196 
    197 bool DataTypeTracker::IsThrottled() const {
    198   return !unthrottle_time_.is_null();
    199 }
    200 
    201 base::TimeDelta DataTypeTracker::GetTimeUntilUnthrottle(
    202     base::TimeTicks now) const {
    203   if (!IsThrottled()) {
    204     NOTREACHED();
    205     return base::TimeDelta::FromSeconds(0);
    206   }
    207   return std::max(base::TimeDelta::FromSeconds(0),
    208                   unthrottle_time_ - now);
    209 }
    210 
    211 void DataTypeTracker::ThrottleType(base::TimeDelta duration,
    212                                       base::TimeTicks now) {
    213   unthrottle_time_ = std::max(unthrottle_time_, now + duration);
    214 }
    215 
    216 void DataTypeTracker::UpdateThrottleState(base::TimeTicks now) {
    217   if (now >= unthrottle_time_) {
    218     unthrottle_time_ = base::TimeTicks();
    219   }
    220 }
    221 
    222 void DataTypeTracker::UpdateLocalNudgeDelay(base::TimeDelta delay) {
    223   nudge_delay_ = delay;
    224 }
    225 
    226 }  // namespace sessions
    227 }  // namespace syncer
    228