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
) {
20 DataTypeTracker::~DataTypeTracker() { }
22 void DataTypeTracker::RecordLocalChange() {
26 void DataTypeTracker::RecordLocalRefreshRequest() {
27 local_refresh_request_count_
++;
30 void DataTypeTracker::RecordRemoteInvalidation(
31 scoped_ptr
<InvalidationInterface
> incoming
) {
34 // Merge the incoming invalidation into our list of pending invalidations.
36 // We won't use STL algorithms here because our concept of equality doesn't
37 // quite fit the expectations of set_intersection. In particular, two
38 // invalidations can be equal according to the SingleObjectInvalidationSet's
39 // rules (ie. have equal versions), but still have different AckHandle values
40 // and need to be acknowledged separately.
42 // The invalidations service can only track one outsanding invalidation per
43 // type and version, so the acknowledgement here should be redundant. We'll
44 // acknowledge them anyway since it should do no harm, and makes this code a
45 // bit easier to test.
47 // Overlaps should be extremely rare for most invalidations. They can happen
48 // for unknown version invalidations, though.
50 ScopedVector
<InvalidationInterface
>::iterator it
=
51 pending_invalidations_
.begin();
53 // Find the lower bound.
54 while (it
!= pending_invalidations_
.end() &&
55 InvalidationInterface::LessThanByVersion(**it
, *incoming
)) {
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.
66 // Insert before the existing and get iterator to inserted.
67 ScopedVector
<InvalidationInterface
>::iterator it2
=
68 pending_invalidations_
.insert(it
, incoming
.release());
70 // Increment that iterator to the old one, then acknowledge and remove it.
72 (*it2
)->Acknowledge();
73 pending_invalidations_
.erase(it2
);
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());
80 // The incoming invalidation may have caused us to exceed our buffer size.
81 // Trim some items from our list, if necessary.
82 while (pending_invalidations_
.size() > payload_buffer_size_
) {
83 last_dropped_invalidation_
.reset(pending_invalidations_
.front());
84 last_dropped_invalidation_
->Drop();
85 pending_invalidations_
.weak_erase(pending_invalidations_
.begin());
89 void DataTypeTracker::RecordSuccessfulSyncCycle() {
90 // If we were throttled, then we would have been excluded from this cycle's
91 // GetUpdates and Commit actions. Our state remains unchanged.
95 local_nudge_count_
= 0;
96 local_refresh_request_count_
= 0;
98 // TODO(rlarocque): If we want this to be correct even if we should happen to
99 // crash before writing all our state, we should wait until the results of
100 // this sync cycle have been written to disk before updating the invalidations
101 // state. See crbug.com/324996.
102 for (ScopedVector
<InvalidationInterface
>::const_iterator it
=
103 pending_invalidations_
.begin();
104 it
!= pending_invalidations_
.end();
106 (*it
)->Acknowledge();
108 pending_invalidations_
.clear();
110 if (last_dropped_invalidation_
) {
111 last_dropped_invalidation_
->Acknowledge();
112 last_dropped_invalidation_
.reset();
116 // This limit will take effect on all future invalidations received.
117 void DataTypeTracker::UpdatePayloadBufferSize(size_t new_size
) {
118 payload_buffer_size_
= new_size
;
121 bool DataTypeTracker::IsSyncRequired() const {
122 return !IsThrottled() && (HasLocalChangePending() || IsGetUpdatesRequired());
125 bool DataTypeTracker::IsGetUpdatesRequired() const {
126 return !IsThrottled() &&
127 (HasRefreshRequestPending() || HasPendingInvalidation());
130 bool DataTypeTracker::HasLocalChangePending() const {
131 return local_nudge_count_
> 0;
134 bool DataTypeTracker::HasRefreshRequestPending() const {
135 return local_refresh_request_count_
> 0;
138 bool DataTypeTracker::HasPendingInvalidation() const {
139 return !pending_invalidations_
.empty() || last_dropped_invalidation_
;
142 void DataTypeTracker::SetLegacyNotificationHint(
143 sync_pb::DataTypeProgressMarker
* progress
) const {
144 DCHECK(!IsThrottled())
145 << "We should not make requests if the type is throttled.";
147 if (!pending_invalidations_
.empty() &&
148 !pending_invalidations_
.back()->IsUnknownVersion()) {
149 // The old-style source info can contain only one hint per type. We grab
150 // the most recent, to mimic the old coalescing behaviour.
151 progress
->set_notification_hint(
152 pending_invalidations_
.back()->GetPayload());
153 } else if (HasLocalChangePending()) {
154 // The old-style source info sent up an empty string (as opposed to
155 // nothing at all) when the type was locally nudged, but had not received
156 // any invalidations.
157 progress
->set_notification_hint(std::string());
161 void DataTypeTracker::FillGetUpdatesTriggersMessage(
162 sync_pb::GetUpdateTriggers
* msg
) const {
163 // Fill the list of payloads, if applicable. The payloads must be ordered
164 // oldest to newest, so we insert them in the same order as we've been storing
166 for (ScopedVector
<InvalidationInterface
>::const_iterator it
=
167 pending_invalidations_
.begin();
168 it
!= pending_invalidations_
.end();
170 if (!(*it
)->IsUnknownVersion()) {
171 msg
->add_notification_hint((*it
)->GetPayload());
175 msg
->set_server_dropped_hints(
176 !pending_invalidations_
.empty() &&
177 (*pending_invalidations_
.begin())->IsUnknownVersion());
178 msg
->set_client_dropped_hints(last_dropped_invalidation_
);
179 msg
->set_local_modification_nudges(local_nudge_count_
);
180 msg
->set_datatype_refresh_nudges(local_refresh_request_count_
);
183 bool DataTypeTracker::IsThrottled() const {
184 return !unthrottle_time_
.is_null();
187 base::TimeDelta
DataTypeTracker::GetTimeUntilUnthrottle(
188 base::TimeTicks now
) const {
189 if (!IsThrottled()) {
191 return base::TimeDelta::FromSeconds(0);
193 return std::max(base::TimeDelta::FromSeconds(0),
194 unthrottle_time_
- now
);
197 void DataTypeTracker::ThrottleType(base::TimeDelta duration
,
198 base::TimeTicks now
) {
199 unthrottle_time_
= std::max(unthrottle_time_
, now
+ duration
);
202 void DataTypeTracker::UpdateThrottleState(base::TimeTicks now
) {
203 if (now
>= unthrottle_time_
) {
204 unthrottle_time_
= base::TimeTicks();
208 } // namespace sessions
209 } // namespace syncer