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 |