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.
5 #include "sync/sessions/data_type_tracker.h"
7 #include "base/logging.h"
8 #include "sync/internal_api/public/base/invalidation_interface.h"
9 #include "sync/sessions/nudge_tracker.h"
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) {
21 DataTypeTracker::~DataTypeTracker() { }
23 void DataTypeTracker::RecordLocalChange() {
27 void DataTypeTracker::RecordLocalRefreshRequest() {
28 local_refresh_request_count_++;
31 void DataTypeTracker::RecordRemoteInvalidation(
32 scoped_ptr<InvalidationInterface> incoming) {
35 // Merge the incoming invalidation into our list of pending invalidations.
37 // We won't use STL algorithms here because our concept of equality doesn't
38 // quite fit the expectations of set_intersection. In particular, two
39 // invalidations can be equal according to the SingleObjectInvalidationSet's
40 // rules (ie. have equal versions), but still have different AckHandle values
41 // and need to be acknowledged separately.
43 // The invalidations service can only track one outsanding invalidation per
44 // type and version, so the acknowledgement here should be redundant. We'll
45 // acknowledge them anyway since it should do no harm, and makes this code a
46 // bit easier to test.
48 // Overlaps should be extremely rare for most invalidations. They can happen
49 // for unknown version invalidations, though.
51 ScopedVector<InvalidationInterface>::iterator it =
52 pending_invalidations_.begin();
54 // Find the lower bound.
55 while (it != pending_invalidations_.end() &&
56 InvalidationInterface::LessThanByVersion(**it, *incoming)) {
60 if (it != pending_invalidations_.end() &&
61 !InvalidationInterface::LessThanByVersion(*incoming, **it) &&
62 !InvalidationInterface::LessThanByVersion(**it, *incoming)) {
63 // Incoming overlaps with existing. Either both are unknown versions
64 // (likely) or these two have the same version number (very unlikely).
65 // Acknowledge and overwrite existing.
67 // Insert before the existing and get iterator to inserted.
68 ScopedVector<InvalidationInterface>::iterator it2 =
69 pending_invalidations_.insert(it, incoming.release());
71 // Increment that iterator to the old one, then acknowledge and remove it.
73 (*it2)->Acknowledge();
74 pending_invalidations_.erase(it2);
76 // The incoming has a version not in the pending_invalidations_ list.
77 // Add it to the list at the proper position.
78 pending_invalidations_.insert(it, incoming.release());
81 // The incoming invalidation may have caused us to exceed our buffer size.
82 // Trim some items from our list, if necessary.
83 while (pending_invalidations_.size() > payload_buffer_size_) {
84 last_dropped_invalidation_.reset(pending_invalidations_.front());
85 last_dropped_invalidation_->Drop();
86 pending_invalidations_.weak_erase(pending_invalidations_.begin());
90 void DataTypeTracker::RecordInitialSyncRequired() {
91 initial_sync_required_ = true;
94 void DataTypeTracker::RecordSuccessfulSyncCycle() {
95 // If we were throttled, then we would have been excluded from this cycle's
96 // GetUpdates and Commit actions. Our state remains unchanged.
100 local_nudge_count_ = 0;
101 local_refresh_request_count_ = 0;
103 // TODO(rlarocque): If we want this to be correct even if we should happen to
104 // crash before writing all our state, we should wait until the results of
105 // this sync cycle have been written to disk before updating the invalidations
106 // state. See crbug.com/324996.
107 for (ScopedVector<InvalidationInterface>::const_iterator it =
108 pending_invalidations_.begin();
109 it != pending_invalidations_.end();
111 (*it)->Acknowledge();
113 pending_invalidations_.clear();
115 if (last_dropped_invalidation_) {
116 last_dropped_invalidation_->Acknowledge();
117 last_dropped_invalidation_.reset();
120 initial_sync_required_ = false;
123 // This limit will take effect on all future invalidations received.
124 void DataTypeTracker::UpdatePayloadBufferSize(size_t new_size) {
125 payload_buffer_size_ = new_size;
128 bool DataTypeTracker::IsSyncRequired() const {
129 return !IsThrottled() && (HasLocalChangePending() || IsGetUpdatesRequired());
132 bool DataTypeTracker::IsGetUpdatesRequired() const {
133 return !IsThrottled() &&
134 (HasRefreshRequestPending() || HasPendingInvalidation() ||
135 IsInitialSyncRequired());
138 bool DataTypeTracker::HasLocalChangePending() const {
139 return local_nudge_count_ > 0;
142 bool DataTypeTracker::HasRefreshRequestPending() const {
143 return local_refresh_request_count_ > 0;
146 bool DataTypeTracker::HasPendingInvalidation() const {
147 return !pending_invalidations_.empty() || last_dropped_invalidation_;
150 bool DataTypeTracker::IsInitialSyncRequired() const {
151 return initial_sync_required_;
154 void DataTypeTracker::SetLegacyNotificationHint(
155 sync_pb::DataTypeProgressMarker* progress) const {
156 DCHECK(!IsThrottled())
157 << "We should not make requests if the type is throttled.";
159 if (!pending_invalidations_.empty() &&
160 !pending_invalidations_.back()->IsUnknownVersion()) {
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(
164 pending_invalidations_.back()->GetPayload());
165 } else if (HasLocalChangePending()) {
166 // The old-style source info sent up an empty string (as opposed to
167 // nothing at all) when the type was locally nudged, but had not received
168 // any invalidations.
169 progress->set_notification_hint(std::string());
173 void DataTypeTracker::FillGetUpdatesTriggersMessage(
174 sync_pb::GetUpdateTriggers* msg) const {
175 // Fill the list of payloads, if applicable. The payloads must be ordered
176 // oldest to newest, so we insert them in the same order as we've been storing
178 for (ScopedVector<InvalidationInterface>::const_iterator it =
179 pending_invalidations_.begin();
180 it != pending_invalidations_.end();
182 if (!(*it)->IsUnknownVersion()) {
183 msg->add_notification_hint((*it)->GetPayload());
187 msg->set_server_dropped_hints(
188 !pending_invalidations_.empty() &&
189 (*pending_invalidations_.begin())->IsUnknownVersion());
190 msg->set_client_dropped_hints(last_dropped_invalidation_);
191 msg->set_local_modification_nudges(local_nudge_count_);
192 msg->set_datatype_refresh_nudges(local_refresh_request_count_);
193 msg->set_initial_sync_in_progress(initial_sync_required_);
196 bool DataTypeTracker::IsThrottled() const {
197 return !unthrottle_time_.is_null();
200 base::TimeDelta DataTypeTracker::GetTimeUntilUnthrottle(
201 base::TimeTicks now) const {
202 if (!IsThrottled()) {
204 return base::TimeDelta::FromSeconds(0);
206 return std::max(base::TimeDelta::FromSeconds(0),
207 unthrottle_time_ - now);
210 void DataTypeTracker::ThrottleType(base::TimeDelta duration,
211 base::TimeTicks now) {
212 unthrottle_time_ = std::max(unthrottle_time_, now + duration);
215 void DataTypeTracker::UpdateThrottleState(base::TimeTicks now) {
216 if (now >= unthrottle_time_) {
217 unthrottle_time_ = base::TimeTicks();
221 } // namespace sessions
222 } // namespace syncer