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),
19 sync_required_to_resolve_conflict_(false) {
22 DataTypeTracker::~DataTypeTracker() { }
24 base::TimeDelta
DataTypeTracker::RecordLocalChange() {
29 void DataTypeTracker::RecordLocalRefreshRequest() {
30 local_refresh_request_count_
++;
33 void DataTypeTracker::RecordRemoteInvalidation(
34 scoped_ptr
<InvalidationInterface
> incoming
) {
37 // Merge the incoming invalidation into our list of pending invalidations.
39 // We won't use STL algorithms here because our concept of equality doesn't
40 // quite fit the expectations of set_intersection. In particular, two
41 // invalidations can be equal according to the SingleObjectInvalidationSet's
42 // rules (ie. have equal versions), but still have different AckHandle values
43 // and need to be acknowledged separately.
45 // The invalidations service can only track one outsanding invalidation per
46 // type and version, so the acknowledgement here should be redundant. We'll
47 // acknowledge them anyway since it should do no harm, and makes this code a
48 // bit easier to test.
50 // Overlaps should be extremely rare for most invalidations. They can happen
51 // for unknown version invalidations, though.
53 ScopedVector
<InvalidationInterface
>::iterator it
=
54 pending_invalidations_
.begin();
56 // Find the lower bound.
57 while (it
!= pending_invalidations_
.end() &&
58 InvalidationInterface::LessThanByVersion(**it
, *incoming
)) {
62 if (it
!= pending_invalidations_
.end() &&
63 !InvalidationInterface::LessThanByVersion(*incoming
, **it
) &&
64 !InvalidationInterface::LessThanByVersion(**it
, *incoming
)) {
65 // Incoming overlaps with existing. Either both are unknown versions
66 // (likely) or these two have the same version number (very unlikely).
67 // Acknowledge and overwrite existing.
69 // Insert before the existing and get iterator to inserted.
70 ScopedVector
<InvalidationInterface
>::iterator it2
=
71 pending_invalidations_
.insert(it
, incoming
.release());
73 // Increment that iterator to the old one, then acknowledge and remove it.
75 (*it2
)->Acknowledge();
76 pending_invalidations_
.erase(it2
);
78 // The incoming has a version not in the pending_invalidations_ list.
79 // Add it to the list at the proper position.
80 pending_invalidations_
.insert(it
, incoming
.release());
83 // The incoming invalidation may have caused us to exceed our buffer size.
84 // Trim some items from our list, if necessary.
85 while (pending_invalidations_
.size() > payload_buffer_size_
) {
86 last_dropped_invalidation_
.reset(pending_invalidations_
.front());
87 last_dropped_invalidation_
->Drop();
88 pending_invalidations_
.weak_erase(pending_invalidations_
.begin());
92 void DataTypeTracker::RecordInitialSyncRequired() {
93 initial_sync_required_
= true;
96 void DataTypeTracker::RecordCommitConflict() {
97 sync_required_to_resolve_conflict_
= true;
100 void DataTypeTracker::RecordSuccessfulSyncCycle() {
101 // If we were throttled, then we would have been excluded from this cycle's
102 // GetUpdates and Commit actions. Our state remains unchanged.
106 local_nudge_count_
= 0;
107 local_refresh_request_count_
= 0;
109 // TODO(rlarocque): If we want this to be correct even if we should happen to
110 // crash before writing all our state, we should wait until the results of
111 // this sync cycle have been written to disk before updating the invalidations
112 // state. See crbug.com/324996.
113 for (ScopedVector
<InvalidationInterface
>::const_iterator it
=
114 pending_invalidations_
.begin();
115 it
!= pending_invalidations_
.end();
117 (*it
)->Acknowledge();
119 pending_invalidations_
.clear();
121 if (last_dropped_invalidation_
) {
122 last_dropped_invalidation_
->Acknowledge();
123 last_dropped_invalidation_
.reset();
126 initial_sync_required_
= false;
127 sync_required_to_resolve_conflict_
= false;
130 // This limit will take effect on all future invalidations received.
131 void DataTypeTracker::UpdatePayloadBufferSize(size_t new_size
) {
132 payload_buffer_size_
= new_size
;
135 bool DataTypeTracker::IsSyncRequired() const {
136 return !IsThrottled() && (HasLocalChangePending() || IsGetUpdatesRequired());
139 bool DataTypeTracker::IsGetUpdatesRequired() const {
140 return !IsThrottled() &&
141 (HasRefreshRequestPending() || HasPendingInvalidation() ||
142 IsInitialSyncRequired() || IsSyncRequiredToResolveConflict());
145 bool DataTypeTracker::HasLocalChangePending() const {
146 return local_nudge_count_
> 0;
149 bool DataTypeTracker::HasRefreshRequestPending() const {
150 return local_refresh_request_count_
> 0;
153 bool DataTypeTracker::HasPendingInvalidation() const {
154 return !pending_invalidations_
.empty() || last_dropped_invalidation_
;
157 bool DataTypeTracker::IsInitialSyncRequired() const {
158 return initial_sync_required_
;
161 bool DataTypeTracker::IsSyncRequiredToResolveConflict() const {
162 return sync_required_to_resolve_conflict_
;
165 void DataTypeTracker::SetLegacyNotificationHint(
166 sync_pb::DataTypeProgressMarker
* progress
) const {
167 DCHECK(!IsThrottled())
168 << "We should not make requests if the type is throttled.";
170 if (!pending_invalidations_
.empty() &&
171 !pending_invalidations_
.back()->IsUnknownVersion()) {
172 // The old-style source info can contain only one hint per type. We grab
173 // the most recent, to mimic the old coalescing behaviour.
174 progress
->set_notification_hint(
175 pending_invalidations_
.back()->GetPayload());
176 } else if (HasLocalChangePending()) {
177 // The old-style source info sent up an empty string (as opposed to
178 // nothing at all) when the type was locally nudged, but had not received
179 // any invalidations.
180 progress
->set_notification_hint(std::string());
184 void DataTypeTracker::FillGetUpdatesTriggersMessage(
185 sync_pb::GetUpdateTriggers
* msg
) const {
186 // Fill the list of payloads, if applicable. The payloads must be ordered
187 // oldest to newest, so we insert them in the same order as we've been storing
189 for (ScopedVector
<InvalidationInterface
>::const_iterator it
=
190 pending_invalidations_
.begin();
191 it
!= pending_invalidations_
.end();
193 if (!(*it
)->IsUnknownVersion()) {
194 msg
->add_notification_hint((*it
)->GetPayload());
198 msg
->set_server_dropped_hints(
199 !pending_invalidations_
.empty() &&
200 (*pending_invalidations_
.begin())->IsUnknownVersion());
201 msg
->set_client_dropped_hints(last_dropped_invalidation_
);
202 msg
->set_local_modification_nudges(local_nudge_count_
);
203 msg
->set_datatype_refresh_nudges(local_refresh_request_count_
);
204 msg
->set_initial_sync_in_progress(initial_sync_required_
);
205 msg
->set_sync_for_resolve_conflict_in_progress(
206 sync_required_to_resolve_conflict_
);
209 bool DataTypeTracker::IsThrottled() const {
210 return !unthrottle_time_
.is_null();
213 base::TimeDelta
DataTypeTracker::GetTimeUntilUnthrottle(
214 base::TimeTicks now
) const {
215 if (!IsThrottled()) {
217 return base::TimeDelta::FromSeconds(0);
219 return std::max(base::TimeDelta::FromSeconds(0),
220 unthrottle_time_
- now
);
223 void DataTypeTracker::ThrottleType(base::TimeDelta duration
,
224 base::TimeTicks now
) {
225 unthrottle_time_
= std::max(unthrottle_time_
, now
+ duration
);
228 void DataTypeTracker::UpdateThrottleState(base::TimeTicks now
) {
229 if (now
>= unthrottle_time_
) {
230 unthrottle_time_
= base::TimeTicks();
234 void DataTypeTracker::UpdateLocalNudgeDelay(base::TimeDelta delay
) {
235 nudge_delay_
= delay
;
238 } // namespace sessions
239 } // namespace syncer