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