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