• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.h"
9 #include "sync/notifier/invalidation_util.h"
10 #include "sync/notifier/single_object_invalidation_set.h"
11 #include "sync/sessions/nudge_tracker.h"
12 
13 namespace syncer {
14 namespace sessions {
15 
DataTypeTracker(const invalidation::ObjectId & object_id)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) { }
21 
~DataTypeTracker()22 DataTypeTracker::~DataTypeTracker() { }
23 
RecordLocalChange()24 void DataTypeTracker::RecordLocalChange() {
25   local_nudge_count_++;
26 }
27 
RecordLocalRefreshRequest()28 void DataTypeTracker::RecordLocalRefreshRequest() {
29   local_refresh_request_count_++;
30 }
31 
32 namespace {
33 
IsInvalidationVersionLessThan(const Invalidation & a,const Invalidation & b)34 bool IsInvalidationVersionLessThan(
35     const Invalidation& a,
36     const Invalidation& b) {
37   InvalidationVersionLessThan comparator;
38   return comparator(a, b);
39 }
40 
41 }  // namespace
42 
RecordRemoteInvalidations(const SingleObjectInvalidationSet & invalidations)43 void DataTypeTracker::RecordRemoteInvalidations(
44     const SingleObjectInvalidationSet& invalidations) {
45   // Merge the incoming invalidations into our list of pending invalidations.
46   //
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.
52   //
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.
57   //
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();
64 
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)) {
69       existing_it++;
70     }
71 
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;
79       existing_it++;
80       old_inv->Acknowledge();
81       pending_invalidations_.Erase(old_inv);
82       pending_invalidations_.Insert(*incoming_it);
83       incoming_it++;
84     } else {
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);
90       incoming_it++;
91     }
92   }
93 
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());
99   }
100 }
101 
RecordSuccessfulSyncCycle()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.
105   if (IsThrottled())
106     return;
107 
108   local_nudge_count_ = 0;
109   local_refresh_request_count_ = 0;
110 
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) {
118     it->Acknowledge();
119   }
120   pending_invalidations_.Clear();
121 
122   if (drop_tracker_.IsRecoveringFromDropEvent()) {
123     drop_tracker_.RecordRecoveryFromDropEvent();
124   }
125 }
126 
127 // This limit will take effect on all future invalidations received.
UpdatePayloadBufferSize(size_t new_size)128 void DataTypeTracker::UpdatePayloadBufferSize(size_t new_size) {
129   payload_buffer_size_ = new_size;
130 }
131 
IsSyncRequired() const132 bool DataTypeTracker::IsSyncRequired() const {
133   return !IsThrottled() && (HasLocalChangePending() || IsGetUpdatesRequired());
134 }
135 
IsGetUpdatesRequired() const136 bool DataTypeTracker::IsGetUpdatesRequired() const {
137   return !IsThrottled() &&
138       (HasRefreshRequestPending() || HasPendingInvalidation());
139 }
140 
HasLocalChangePending() const141 bool DataTypeTracker::HasLocalChangePending() const {
142   return local_nudge_count_ > 0;
143 }
144 
HasRefreshRequestPending() const145 bool DataTypeTracker::HasRefreshRequestPending() const {
146   return local_refresh_request_count_ > 0;
147 }
148 
HasPendingInvalidation() const149 bool DataTypeTracker::HasPendingInvalidation() const {
150   return !pending_invalidations_.IsEmpty()
151       || drop_tracker_.IsRecoveringFromDropEvent();
152 }
153 
SetLegacyNotificationHint(sync_pb::DataTypeProgressMarker * progress) const154 void DataTypeTracker::SetLegacyNotificationHint(
155     sync_pb::DataTypeProgressMarker* progress) const {
156   DCHECK(!IsThrottled())
157       << "We should not make requests if the type is throttled.";
158 
159   if (!pending_invalidations_.IsEmpty() &&
160       !pending_invalidations_.back().is_unknown_version()) {
161     // The old-style source info can contain only one hint per type.  We grab
162     // the most recent, to mimic the old coalescing behaviour.
163     progress->set_notification_hint(pending_invalidations_.back().payload());
164   } else if (HasLocalChangePending()) {
165     // The old-style source info sent up an empty string (as opposed to
166     // nothing at all) when the type was locally nudged, but had not received
167     // any invalidations.
168     progress->set_notification_hint("");
169   }
170 }
171 
FillGetUpdatesTriggersMessage(sync_pb::GetUpdateTriggers * msg) const172 void DataTypeTracker::FillGetUpdatesTriggersMessage(
173     sync_pb::GetUpdateTriggers* msg) const {
174   // Fill the list of payloads, if applicable.  The payloads must be ordered
175   // oldest to newest, so we insert them in the same order as we've been storing
176   // them internally.
177   for (SingleObjectInvalidationSet::const_iterator it =
178        pending_invalidations_.begin();
179        it != pending_invalidations_.end(); ++it) {
180     if (!it->is_unknown_version()) {
181       msg->add_notification_hint(it->payload());
182     }
183   }
184 
185   msg->set_server_dropped_hints(
186       pending_invalidations_.StartsWithUnknownVersion());
187   msg->set_client_dropped_hints(drop_tracker_.IsRecoveringFromDropEvent());
188   msg->set_local_modification_nudges(local_nudge_count_);
189   msg->set_datatype_refresh_nudges(local_refresh_request_count_);
190 }
191 
IsThrottled() const192 bool DataTypeTracker::IsThrottled() const {
193   return !unthrottle_time_.is_null();
194 }
195 
GetTimeUntilUnthrottle(base::TimeTicks now) const196 base::TimeDelta DataTypeTracker::GetTimeUntilUnthrottle(
197     base::TimeTicks now) const {
198   if (!IsThrottled()) {
199     NOTREACHED();
200     return base::TimeDelta::FromSeconds(0);
201   }
202   return std::max(base::TimeDelta::FromSeconds(0),
203                   unthrottle_time_ - now);
204 }
205 
ThrottleType(base::TimeDelta duration,base::TimeTicks now)206 void DataTypeTracker::ThrottleType(base::TimeDelta duration,
207                                       base::TimeTicks now) {
208   unthrottle_time_ = std::max(unthrottle_time_, now + duration);
209 }
210 
UpdateThrottleState(base::TimeTicks now)211 void DataTypeTracker::UpdateThrottleState(base::TimeTicks now) {
212   if (now >= unthrottle_time_) {
213     unthrottle_time_ = base::TimeTicks();
214   }
215 }
216 
217 }  // namespace sessions
218 }  // namespace syncer
219