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
DataTypeTracker()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
~DataTypeTracker()21 DataTypeTracker::~DataTypeTracker() { }
22
RecordLocalChange()23 base::TimeDelta DataTypeTracker::RecordLocalChange() {
24 local_nudge_count_++;
25 return nudge_delay_;
26 }
27
RecordLocalRefreshRequest()28 void DataTypeTracker::RecordLocalRefreshRequest() {
29 local_refresh_request_count_++;
30 }
31
RecordRemoteInvalidation(scoped_ptr<InvalidationInterface> incoming)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
RecordInitialSyncRequired()91 void DataTypeTracker::RecordInitialSyncRequired() {
92 initial_sync_required_ = true;
93 }
94
RecordSuccessfulSyncCycle()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.
UpdatePayloadBufferSize(size_t new_size)125 void DataTypeTracker::UpdatePayloadBufferSize(size_t new_size) {
126 payload_buffer_size_ = new_size;
127 }
128
IsSyncRequired() const129 bool DataTypeTracker::IsSyncRequired() const {
130 return !IsThrottled() && (HasLocalChangePending() || IsGetUpdatesRequired());
131 }
132
IsGetUpdatesRequired() const133 bool DataTypeTracker::IsGetUpdatesRequired() const {
134 return !IsThrottled() &&
135 (HasRefreshRequestPending() || HasPendingInvalidation() ||
136 IsInitialSyncRequired());
137 }
138
HasLocalChangePending() const139 bool DataTypeTracker::HasLocalChangePending() const {
140 return local_nudge_count_ > 0;
141 }
142
HasRefreshRequestPending() const143 bool DataTypeTracker::HasRefreshRequestPending() const {
144 return local_refresh_request_count_ > 0;
145 }
146
HasPendingInvalidation() const147 bool DataTypeTracker::HasPendingInvalidation() const {
148 return !pending_invalidations_.empty() || last_dropped_invalidation_;
149 }
150
IsInitialSyncRequired() const151 bool DataTypeTracker::IsInitialSyncRequired() const {
152 return initial_sync_required_;
153 }
154
SetLegacyNotificationHint(sync_pb::DataTypeProgressMarker * progress) const155 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
FillGetUpdatesTriggersMessage(sync_pb::GetUpdateTriggers * msg) const174 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
IsThrottled() const197 bool DataTypeTracker::IsThrottled() const {
198 return !unthrottle_time_.is_null();
199 }
200
GetTimeUntilUnthrottle(base::TimeTicks now) const201 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
ThrottleType(base::TimeDelta duration,base::TimeTicks now)211 void DataTypeTracker::ThrottleType(base::TimeDelta duration,
212 base::TimeTicks now) {
213 unthrottle_time_ = std::max(unthrottle_time_, now + duration);
214 }
215
UpdateThrottleState(base::TimeTicks now)216 void DataTypeTracker::UpdateThrottleState(base::TimeTicks now) {
217 if (now >= unthrottle_time_) {
218 unthrottle_time_ = base::TimeTicks();
219 }
220 }
221
UpdateLocalNudgeDelay(base::TimeDelta delay)222 void DataTypeTracker::UpdateLocalNudgeDelay(base::TimeDelta delay) {
223 nudge_delay_ = delay;
224 }
225
226 } // namespace sessions
227 } // namespace syncer
228