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.h"
9 #include "sync/notifier/invalidation_util.h"
10 #include "sync/notifier/single_object_invalidation_set.h"
11 #include "sync/sessions/nudge_tracker.h"
16 DataTypeTracker::DataTypeTracker(const invalidation::ObjectId
& object_id
)
17 : local_nudge_count_(0),
18 local_refresh_request_count_(0),
19 payload_buffer_size_(NudgeTracker::kDefaultMaxPayloadsPerType
),
20 drop_tracker_(object_id
) { }
22 DataTypeTracker::~DataTypeTracker() { }
24 void DataTypeTracker::RecordLocalChange() {
28 void DataTypeTracker::RecordLocalRefreshRequest() {
29 local_refresh_request_count_
++;
34 bool IsInvalidationVersionLessThan(
35 const Invalidation
& a
,
36 const Invalidation
& b
) {
37 InvalidationVersionLessThan comparator
;
38 return comparator(a
, b
);
43 void DataTypeTracker::RecordRemoteInvalidations(
44 const SingleObjectInvalidationSet
& invalidations
) {
45 // Merge the incoming invalidations into our list of pending invalidations.
47 // We won't use STL algorithms here because our concept of equality doesn't
48 // quite fit the expectations of set_intersection. In particular, two
49 // invalidations can be equal according to the SingleObjectInvalidationSet's
50 // rules (ie. have equal versions), but still have different AckHandle values
51 // and need to be acknowledged separately.
53 // The invalidaitons service can only track one outsanding invalidation per
54 // type and version, so the acknowledgement here should be redundant. We'll
55 // acknowledge them anyway since it should do no harm, and makes this code a
56 // bit easier to test.
58 // Overlaps should be extremely rare for most invalidations. They can happen
59 // for unknown version invalidations, though.
60 SingleObjectInvalidationSet::const_iterator incoming_it
=
61 invalidations
.begin();
62 SingleObjectInvalidationSet::const_iterator existing_it
=
63 pending_invalidations_
.begin();
65 while (incoming_it
!= invalidations
.end()) {
66 // Keep existing_it ahead of incoming_it.
67 while (existing_it
!= pending_invalidations_
.end()
68 && IsInvalidationVersionLessThan(*existing_it
, *incoming_it
)) {
72 if (existing_it
!= pending_invalidations_
.end()
73 && !IsInvalidationVersionLessThan(*incoming_it
, *existing_it
)
74 && !IsInvalidationVersionLessThan(*existing_it
, *incoming_it
)) {
75 // Incoming overlaps with existing. Either both are unknown versions
76 // (likely) or these two have the same version number (very unlikely).
77 // Acknowledge and overwrite existing.
78 SingleObjectInvalidationSet::const_iterator old_inv
= existing_it
;
80 old_inv
->Acknowledge();
81 pending_invalidations_
.Erase(old_inv
);
82 pending_invalidations_
.Insert(*incoming_it
);
85 DCHECK(existing_it
== pending_invalidations_
.end()
86 || IsInvalidationVersionLessThan(*incoming_it
, *existing_it
));
87 // The incoming_it points at a version not in the pending_invalidations_
88 // list. Add it to the list then increment past it.
89 pending_invalidations_
.Insert(*incoming_it
);
94 // Those incoming invalidations may have caused us to exceed our buffer size.
95 // Trim some items from our list, if necessary.
96 while (pending_invalidations_
.GetSize() > payload_buffer_size_
) {
97 pending_invalidations_
.begin()->Drop(&drop_tracker_
);
98 pending_invalidations_
.Erase(pending_invalidations_
.begin());
102 void DataTypeTracker::RecordSuccessfulSyncCycle() {
103 // If we were throttled, then we would have been excluded from this cycle's
104 // GetUpdates and Commit actions. Our state remains unchanged.
108 local_nudge_count_
= 0;
109 local_refresh_request_count_
= 0;
111 // TODO(rlarocque): If we want this to be correct even if we should happen to
112 // crash before writing all our state, we should wait until the results of
113 // this sync cycle have been written to disk before updating the invalidations
114 // state. See crbug.com/324996.
115 for (SingleObjectInvalidationSet::const_iterator it
=
116 pending_invalidations_
.begin();
117 it
!= pending_invalidations_
.end(); ++it
) {
120 pending_invalidations_
.Clear();
122 if (drop_tracker_
.IsRecoveringFromDropEvent()) {
123 drop_tracker_
.RecordRecoveryFromDropEvent();
127 // This limit will take effect on all future invalidations received.
128 void DataTypeTracker::UpdatePayloadBufferSize(size_t new_size
) {
129 payload_buffer_size_
= new_size
;
132 bool DataTypeTracker::IsSyncRequired() const {
133 return !IsThrottled() &&
134 (local_nudge_count_
> 0 ||
135 local_refresh_request_count_
> 0 ||
136 HasPendingInvalidation() ||
137 drop_tracker_
.IsRecoveringFromDropEvent());
140 bool DataTypeTracker::IsGetUpdatesRequired() const {
141 return !IsThrottled() &&
142 (local_refresh_request_count_
> 0 ||
143 HasPendingInvalidation() ||
144 drop_tracker_
.IsRecoveringFromDropEvent());
147 bool DataTypeTracker::HasLocalChangePending() const {
148 return local_nudge_count_
> 0;
151 bool DataTypeTracker::HasPendingInvalidation() const {
152 return !pending_invalidations_
.IsEmpty();
155 void DataTypeTracker::SetLegacyNotificationHint(
156 sync_pb::DataTypeProgressMarker
* progress
) const {
157 DCHECK(!IsThrottled())
158 << "We should not make requests if the type is throttled.";
160 if (!pending_invalidations_
.IsEmpty() &&
161 !pending_invalidations_
.back().is_unknown_version()) {
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(pending_invalidations_
.back().payload());
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("");
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 (SingleObjectInvalidationSet::const_iterator it
=
179 pending_invalidations_
.begin();
180 it
!= pending_invalidations_
.end(); ++it
) {
181 if (!it
->is_unknown_version()) {
182 msg
->add_notification_hint(it
->payload());
186 msg
->set_server_dropped_hints(
187 pending_invalidations_
.StartsWithUnknownVersion());
188 msg
->set_client_dropped_hints(drop_tracker_
.IsRecoveringFromDropEvent());
189 msg
->set_local_modification_nudges(local_nudge_count_
);
190 msg
->set_datatype_refresh_nudges(local_refresh_request_count_
);
193 bool DataTypeTracker::IsThrottled() const {
194 return !unthrottle_time_
.is_null();
197 base::TimeDelta
DataTypeTracker::GetTimeUntilUnthrottle(
198 base::TimeTicks now
) const {
199 if (!IsThrottled()) {
201 return base::TimeDelta::FromSeconds(0);
203 return std::max(base::TimeDelta::FromSeconds(0),
204 unthrottle_time_
- now
);
207 void DataTypeTracker::ThrottleType(base::TimeDelta duration
,
208 base::TimeTicks now
) {
209 unthrottle_time_
= std::max(unthrottle_time_
, now
+ duration
);
212 void DataTypeTracker::UpdateThrottleState(base::TimeTicks now
) {
213 if (now
>= unthrottle_time_
) {
214 unthrottle_time_
= base::TimeTicks();
218 } // namespace sessions
219 } // namespace syncer