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