• 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_interface.h"
9 #include "sync/sessions/nudge_tracker.h"
10 
11 namespace syncer {
12 namespace sessions {
13 
DataTypeTracker()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 }
20 
~DataTypeTracker()21 DataTypeTracker::~DataTypeTracker() { }
22 
RecordLocalChange()23 base::TimeDelta DataTypeTracker::RecordLocalChange() {
24   local_nudge_count_++;
25   return nudge_delay_;
26 }
27 
RecordLocalRefreshRequest()28 void DataTypeTracker::RecordLocalRefreshRequest() {
29   local_refresh_request_count_++;
30 }
31 
RecordRemoteInvalidation(scoped_ptr<InvalidationInterface> incoming)32 void DataTypeTracker::RecordRemoteInvalidation(
33     scoped_ptr<InvalidationInterface> incoming) {
34   DCHECK(incoming);
35 
36   // Merge the incoming invalidation into our list of pending invalidations.
37   //
38   // We won't use STL algorithms here because our concept of equality doesn't
39   // quite fit the expectations of set_intersection.  In particular, two
40   // invalidations can be equal according to the SingleObjectInvalidationSet's
41   // rules (ie. have equal versions), but still have different AckHandle values
42   // and need to be acknowledged separately.
43   //
44   // The invalidations service can only track one outsanding invalidation per
45   // type and version, so the acknowledgement here should be redundant.  We'll
46   // acknowledge them anyway since it should do no harm, and makes this code a
47   // bit easier to test.
48   //
49   // Overlaps should be extremely rare for most invalidations.  They can happen
50   // for unknown version invalidations, though.
51 
52   ScopedVector<InvalidationInterface>::iterator it =
53       pending_invalidations_.begin();
54 
55   // Find the lower bound.
56   while (it != pending_invalidations_.end() &&
57          InvalidationInterface::LessThanByVersion(**it, *incoming)) {
58     it++;
59   }
60 
61   if (it != pending_invalidations_.end() &&
62       !InvalidationInterface::LessThanByVersion(*incoming, **it) &&
63       !InvalidationInterface::LessThanByVersion(**it, *incoming)) {
64     // Incoming overlaps with existing.  Either both are unknown versions
65     // (likely) or these two have the same version number (very unlikely).
66     // Acknowledge and overwrite existing.
67 
68     // Insert before the existing and get iterator to inserted.
69     ScopedVector<InvalidationInterface>::iterator it2 =
70         pending_invalidations_.insert(it, incoming.release());
71 
72     // Increment that iterator to the old one, then acknowledge and remove it.
73     ++it2;
74     (*it2)->Acknowledge();
75     pending_invalidations_.erase(it2);
76   } else {
77     // The incoming has a version not in the pending_invalidations_ list.
78     // Add it to the list at the proper position.
79     pending_invalidations_.insert(it, incoming.release());
80   }
81 
82   // The incoming invalidation may have caused us to exceed our buffer size.
83   // Trim some items from our list, if necessary.
84   while (pending_invalidations_.size() > payload_buffer_size_) {
85     last_dropped_invalidation_.reset(pending_invalidations_.front());
86     last_dropped_invalidation_->Drop();
87     pending_invalidations_.weak_erase(pending_invalidations_.begin());
88   }
89 }
90 
RecordInitialSyncRequired()91 void DataTypeTracker::RecordInitialSyncRequired() {
92   initial_sync_required_ = true;
93 }
94 
RecordSuccessfulSyncCycle()95 void DataTypeTracker::RecordSuccessfulSyncCycle() {
96   // If we were throttled, then we would have been excluded from this cycle's
97   // GetUpdates and Commit actions.  Our state remains unchanged.
98   if (IsThrottled())
99     return;
100 
101   local_nudge_count_ = 0;
102   local_refresh_request_count_ = 0;
103 
104   // TODO(rlarocque): If we want this to be correct even if we should happen to
105   // crash before writing all our state, we should wait until the results of
106   // this sync cycle have been written to disk before updating the invalidations
107   // state.  See crbug.com/324996.
108   for (ScopedVector<InvalidationInterface>::const_iterator it =
109            pending_invalidations_.begin();
110        it != pending_invalidations_.end();
111        ++it) {
112     (*it)->Acknowledge();
113   }
114   pending_invalidations_.clear();
115 
116   if (last_dropped_invalidation_) {
117     last_dropped_invalidation_->Acknowledge();
118     last_dropped_invalidation_.reset();
119   }
120 
121   initial_sync_required_ = false;
122 }
123 
124 // This limit will take effect on all future invalidations received.
UpdatePayloadBufferSize(size_t new_size)125 void DataTypeTracker::UpdatePayloadBufferSize(size_t new_size) {
126   payload_buffer_size_ = new_size;
127 }
128 
IsSyncRequired() const129 bool DataTypeTracker::IsSyncRequired() const {
130   return !IsThrottled() && (HasLocalChangePending() || IsGetUpdatesRequired());
131 }
132 
IsGetUpdatesRequired() const133 bool DataTypeTracker::IsGetUpdatesRequired() const {
134   return !IsThrottled() &&
135          (HasRefreshRequestPending() || HasPendingInvalidation() ||
136           IsInitialSyncRequired());
137 }
138 
HasLocalChangePending() const139 bool DataTypeTracker::HasLocalChangePending() const {
140   return local_nudge_count_ > 0;
141 }
142 
HasRefreshRequestPending() const143 bool DataTypeTracker::HasRefreshRequestPending() const {
144   return local_refresh_request_count_ > 0;
145 }
146 
HasPendingInvalidation() const147 bool DataTypeTracker::HasPendingInvalidation() const {
148   return !pending_invalidations_.empty() || last_dropped_invalidation_;
149 }
150 
IsInitialSyncRequired() const151 bool DataTypeTracker::IsInitialSyncRequired() const {
152   return initial_sync_required_;
153 }
154 
SetLegacyNotificationHint(sync_pb::DataTypeProgressMarker * progress) const155 void DataTypeTracker::SetLegacyNotificationHint(
156     sync_pb::DataTypeProgressMarker* progress) const {
157   DCHECK(!IsThrottled())
158       << "We should not make requests if the type is throttled.";
159 
160   if (!pending_invalidations_.empty() &&
161       !pending_invalidations_.back()->IsUnknownVersion()) {
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(
165         pending_invalidations_.back()->GetPayload());
166   } else if (HasLocalChangePending()) {
167     // The old-style source info sent up an empty string (as opposed to
168     // nothing at all) when the type was locally nudged, but had not received
169     // any invalidations.
170     progress->set_notification_hint(std::string());
171   }
172 }
173 
FillGetUpdatesTriggersMessage(sync_pb::GetUpdateTriggers * msg) const174 void DataTypeTracker::FillGetUpdatesTriggersMessage(
175     sync_pb::GetUpdateTriggers* msg) const {
176   // Fill the list of payloads, if applicable.  The payloads must be ordered
177   // oldest to newest, so we insert them in the same order as we've been storing
178   // them internally.
179   for (ScopedVector<InvalidationInterface>::const_iterator it =
180            pending_invalidations_.begin();
181        it != pending_invalidations_.end();
182        ++it) {
183     if (!(*it)->IsUnknownVersion()) {
184       msg->add_notification_hint((*it)->GetPayload());
185     }
186   }
187 
188   msg->set_server_dropped_hints(
189       !pending_invalidations_.empty() &&
190       (*pending_invalidations_.begin())->IsUnknownVersion());
191   msg->set_client_dropped_hints(last_dropped_invalidation_);
192   msg->set_local_modification_nudges(local_nudge_count_);
193   msg->set_datatype_refresh_nudges(local_refresh_request_count_);
194   msg->set_initial_sync_in_progress(initial_sync_required_);
195 }
196 
IsThrottled() const197 bool DataTypeTracker::IsThrottled() const {
198   return !unthrottle_time_.is_null();
199 }
200 
GetTimeUntilUnthrottle(base::TimeTicks now) const201 base::TimeDelta DataTypeTracker::GetTimeUntilUnthrottle(
202     base::TimeTicks now) const {
203   if (!IsThrottled()) {
204     NOTREACHED();
205     return base::TimeDelta::FromSeconds(0);
206   }
207   return std::max(base::TimeDelta::FromSeconds(0),
208                   unthrottle_time_ - now);
209 }
210 
ThrottleType(base::TimeDelta duration,base::TimeTicks now)211 void DataTypeTracker::ThrottleType(base::TimeDelta duration,
212                                       base::TimeTicks now) {
213   unthrottle_time_ = std::max(unthrottle_time_, now + duration);
214 }
215 
UpdateThrottleState(base::TimeTicks now)216 void DataTypeTracker::UpdateThrottleState(base::TimeTicks now) {
217   if (now >= unthrottle_time_) {
218     unthrottle_time_ = base::TimeTicks();
219   }
220 }
221 
UpdateLocalNudgeDelay(base::TimeDelta delay)222 void DataTypeTracker::UpdateLocalNudgeDelay(base::TimeDelta delay) {
223   nudge_delay_ = delay;
224 }
225 
226 }  // namespace sessions
227 }  // namespace syncer
228