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