blob: f0a1f93061084720e9ea842590a9f79d9e29029e [file] [log] [blame]
[email protected]001bbfdc2014-07-17 19:28:461// Copyright 2014 The Chromium Authors. All rights reserved.
[email protected]6a1d0f52010-06-30 05:11:532// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]001bbfdc2014-07-17 19:28:465#include "components/invalidation/registration_manager.h"
[email protected]6a1d0f52010-06-30 05:11:536
[email protected]b7af0f92011-03-10 23:35:487#include <algorithm>
8#include <cstddef>
[email protected]cf686302012-06-28 05:24:459#include <iterator>
[email protected]6a1d0f52010-06-30 05:11:5310#include <string>
[email protected]8e4c88872012-06-28 04:34:3511#include <utility>
[email protected]6a1d0f52010-06-30 05:11:5312
[email protected]b7af0f92011-03-10 23:35:4813#include "base/rand_util.h"
[email protected]8e4c88872012-06-28 04:34:3514#include "base/stl_util.h"
[email protected]51766bf2014-07-24 01:13:4715#include "components/invalidation/invalidation_util.h"
[email protected]b0e1c132012-03-14 06:10:1316#include "google/cacheinvalidation/include/invalidation-client.h"
17#include "google/cacheinvalidation/include/types.h"
[email protected]6a1d0f52010-06-30 05:11:5318
[email protected]65f173552012-06-28 22:43:5819namespace syncer {
[email protected]6a1d0f52010-06-30 05:11:5320
[email protected]2ea4caeb2011-03-11 00:15:0621RegistrationManager::PendingRegistrationInfo::PendingRegistrationInfo() {}
22
[email protected]8e4c88872012-06-28 04:34:3523RegistrationManager::RegistrationStatus::RegistrationStatus(
24 const invalidation::ObjectId& id, RegistrationManager* manager)
25 : id(id),
26 registration_manager(manager),
[email protected]4bc73b492011-10-12 09:07:3827 enabled(true),
[email protected]8e4c88872012-06-28 04:34:3528 state(invalidation::InvalidationListener::UNREGISTERED) {
29 DCHECK(registration_manager);
30}
[email protected]b7af0f92011-03-10 23:35:4831
[email protected]8ba7fa52011-03-11 00:44:3032RegistrationManager::RegistrationStatus::~RegistrationStatus() {}
33
[email protected]b7af0f92011-03-10 23:35:4834void RegistrationManager::RegistrationStatus::DoRegister() {
[email protected]4bc73b492011-10-12 09:07:3835 CHECK(enabled);
[email protected]b7af0f92011-03-10 23:35:4836 // We might be called explicitly, so stop the timer manually and
37 // reset the delay.
38 registration_timer.Stop();
39 delay = base::TimeDelta();
[email protected]8e4c88872012-06-28 04:34:3540 registration_manager->DoRegisterId(id);
[email protected]b7af0f92011-03-10 23:35:4841 DCHECK(!last_registration_request.is_null());
42}
43
[email protected]4bc73b492011-10-12 09:07:3844void RegistrationManager::RegistrationStatus::Disable() {
45 enabled = false;
46 state = invalidation::InvalidationListener::UNREGISTERED;
47 registration_timer.Stop();
48 delay = base::TimeDelta();
49}
50
[email protected]b7af0f92011-03-10 23:35:4851const int RegistrationManager::kInitialRegistrationDelaySeconds = 5;
52const int RegistrationManager::kRegistrationDelayExponent = 2;
53const double RegistrationManager::kRegistrationDelayMaxJitter = 0.5;
54const int RegistrationManager::kMinRegistrationDelaySeconds = 1;
55// 1 hour.
56const int RegistrationManager::kMaxRegistrationDelaySeconds = 60 * 60;
57
[email protected]6a1d0f52010-06-30 05:11:5358RegistrationManager::RegistrationManager(
59 invalidation::InvalidationClient* invalidation_client)
60 : invalidation_client_(invalidation_client) {
61 DCHECK(invalidation_client_);
62}
63
64RegistrationManager::~RegistrationManager() {
[email protected]870bcb32012-06-11 22:27:5465 DCHECK(CalledOnValidThread());
[email protected]8e4c88872012-06-28 04:34:3566 STLDeleteValues(&registration_statuses_);
[email protected]6a1d0f52010-06-30 05:11:5367}
68
[email protected]38091252012-09-04 22:22:1869ObjectIdSet RegistrationManager::UpdateRegisteredIds(const ObjectIdSet& ids) {
[email protected]870bcb32012-06-11 22:27:5470 DCHECK(CalledOnValidThread());
[email protected]58d16662011-02-16 01:44:4571
[email protected]8e4c88872012-06-28 04:34:3572 const ObjectIdSet& old_ids = GetRegisteredIds();
73 const ObjectIdSet& to_register = ids;
74 ObjectIdSet to_unregister;
75 std::set_difference(old_ids.begin(), old_ids.end(),
76 ids.begin(), ids.end(),
77 std::inserter(to_unregister, to_unregister.begin()),
78 ObjectIdLessThan());
79
80 for (ObjectIdSet::const_iterator it = to_unregister.begin();
81 it != to_unregister.end(); ++it) {
82 UnregisterId(*it);
83 }
84
85 for (ObjectIdSet::const_iterator it = to_register.begin();
86 it != to_register.end(); ++it) {
87 if (!ContainsKey(registration_statuses_, *it)) {
88 registration_statuses_.insert(
89 std::make_pair(*it, new RegistrationStatus(*it, this)));
90 }
91 if (!IsIdRegistered(*it)) {
92 TryRegisterId(*it, false /* is-retry */);
[email protected]58d16662011-02-16 01:44:4593 }
[email protected]6a1d0f52010-06-30 05:11:5394 }
[email protected]38091252012-09-04 22:22:1895
96 return to_unregister;
[email protected]6a1d0f52010-06-30 05:11:5397}
98
[email protected]6a1d0f52010-06-30 05:11:5399void RegistrationManager::MarkRegistrationLost(
[email protected]8e4c88872012-06-28 04:34:35100 const invalidation::ObjectId& id) {
[email protected]870bcb32012-06-11 22:27:54101 DCHECK(CalledOnValidThread());
[email protected]8e4c88872012-06-28 04:34:35102 RegistrationStatusMap::const_iterator it = registration_statuses_.find(id);
103 if (it == registration_statuses_.end()) {
[email protected]941ddba2014-03-25 01:50:13104 DVLOG(1) << "Attempt to mark non-existent registration for "
105 << ObjectIdToString(id) << " as lost";
[email protected]4bc73b492011-10-12 09:07:38106 return;
107 }
[email protected]8e4c88872012-06-28 04:34:35108 if (!it->second->enabled) {
109 return;
110 }
111 it->second->state = invalidation::InvalidationListener::UNREGISTERED;
112 bool is_retry = !it->second->last_registration_request.is_null();
113 TryRegisterId(id, is_retry);
[email protected]6a1d0f52010-06-30 05:11:53114}
115
116void RegistrationManager::MarkAllRegistrationsLost() {
[email protected]870bcb32012-06-11 22:27:54117 DCHECK(CalledOnValidThread());
[email protected]8e4c88872012-06-28 04:34:35118 for (RegistrationStatusMap::const_iterator it =
119 registration_statuses_.begin();
120 it != registration_statuses_.end(); ++it) {
121 if (IsIdRegistered(it->first)) {
122 MarkRegistrationLost(it->first);
[email protected]6a1d0f52010-06-30 05:11:53123 }
[email protected]6a1d0f52010-06-30 05:11:53124 }
125}
126
[email protected]8e4c88872012-06-28 04:34:35127void RegistrationManager::DisableId(const invalidation::ObjectId& id) {
[email protected]870bcb32012-06-11 22:27:54128 DCHECK(CalledOnValidThread());
[email protected]8e4c88872012-06-28 04:34:35129 RegistrationStatusMap::const_iterator it = registration_statuses_.find(id);
130 if (it == registration_statuses_.end()) {
[email protected]941ddba2014-03-25 01:50:13131 DVLOG(1) << "Attempt to disable non-existent registration for "
132 << ObjectIdToString(id);
[email protected]8e4c88872012-06-28 04:34:35133 return;
[email protected]b7af0f92011-03-10 23:35:48134 }
[email protected]8e4c88872012-06-28 04:34:35135 it->second->Disable();
[email protected]b7af0f92011-03-10 23:35:48136}
137
138// static
139double RegistrationManager::CalculateBackoff(
140 double retry_interval,
141 double initial_retry_interval,
142 double min_retry_interval,
143 double max_retry_interval,
144 double backoff_exponent,
145 double jitter,
146 double max_jitter) {
147 // scaled_jitter lies in [-max_jitter, max_jitter].
148 double scaled_jitter = jitter * max_jitter;
149 double new_retry_interval =
150 (retry_interval == 0.0) ?
151 (initial_retry_interval * (1.0 + scaled_jitter)) :
152 (retry_interval * (backoff_exponent + scaled_jitter));
153 return std::max(min_retry_interval,
154 std::min(max_retry_interval, new_retry_interval));
155}
156
[email protected]8e4c88872012-06-28 04:34:35157ObjectIdSet RegistrationManager::GetRegisteredIdsForTest() const {
158 return GetRegisteredIds();
159}
160
161RegistrationManager::PendingRegistrationMap
162 RegistrationManager::GetPendingRegistrationsForTest() const {
163 DCHECK(CalledOnValidThread());
164 PendingRegistrationMap pending_registrations;
165 for (RegistrationStatusMap::const_iterator it =
166 registration_statuses_.begin();
167 it != registration_statuses_.end(); ++it) {
168 const invalidation::ObjectId& id = it->first;
169 RegistrationStatus* status = it->second;
170 if (status->registration_timer.IsRunning()) {
171 pending_registrations[id].last_registration_request =
172 status->last_registration_request;
173 pending_registrations[id].registration_attempt =
174 status->last_registration_attempt;
175 pending_registrations[id].delay = status->delay;
176 pending_registrations[id].actual_delay =
177 status->registration_timer.GetCurrentDelay();
178 }
179 }
180 return pending_registrations;
181}
182
183void RegistrationManager::FirePendingRegistrationsForTest() {
184 DCHECK(CalledOnValidThread());
185 for (RegistrationStatusMap::const_iterator it =
186 registration_statuses_.begin();
187 it != registration_statuses_.end(); ++it) {
188 if (it->second->registration_timer.IsRunning()) {
189 it->second->DoRegister();
190 }
191 }
192}
193
[email protected]b7af0f92011-03-10 23:35:48194double RegistrationManager::GetJitter() {
195 // |jitter| lies in [-1.0, 1.0), which is low-biased, but only
196 // barely.
197 //
198 // TODO(akalin): Fix the bias.
199 return 2.0 * base::RandDouble() - 1.0;
200}
201
[email protected]8e4c88872012-06-28 04:34:35202void RegistrationManager::TryRegisterId(const invalidation::ObjectId& id,
203 bool is_retry) {
[email protected]870bcb32012-06-11 22:27:54204 DCHECK(CalledOnValidThread());
[email protected]8e4c88872012-06-28 04:34:35205 RegistrationStatusMap::const_iterator it = registration_statuses_.find(id);
206 if (it == registration_statuses_.end()) {
[email protected]941ddba2014-03-25 01:50:13207 NOTREACHED() << "TryRegisterId called on " << ObjectIdToString(id)
208 << " which is not in the registration map";
[email protected]8e4c88872012-06-28 04:34:35209 return;
210 }
211 RegistrationStatus* status = it->second;
[email protected]4bc73b492011-10-12 09:07:38212 if (!status->enabled) {
213 // Disabled, so do nothing.
214 return;
215 }
[email protected]b7af0f92011-03-10 23:35:48216 status->last_registration_attempt = base::Time::Now();
217 if (is_retry) {
218 // If we're a retry, we must have tried at least once before.
219 DCHECK(!status->last_registration_request.is_null());
220 // delay = max(0, (now - last request) + next_delay)
221 status->delay =
222 (status->last_registration_request -
223 status->last_registration_attempt) +
224 status->next_delay;
225 base::TimeDelta delay =
226 (status->delay <= base::TimeDelta()) ?
227 base::TimeDelta() : status->delay;
[email protected]060588c2011-11-29 21:38:41228 DVLOG(2) << "Registering "
[email protected]8e4c88872012-06-28 04:34:35229 << ObjectIdToString(id) << " in "
[email protected]060588c2011-11-29 21:38:41230 << delay.InMilliseconds() << " ms";
[email protected]b7af0f92011-03-10 23:35:48231 status->registration_timer.Stop();
[email protected]d323a172011-09-02 18:23:02232 status->registration_timer.Start(FROM_HERE,
[email protected]b7af0f92011-03-10 23:35:48233 delay, status, &RegistrationManager::RegistrationStatus::DoRegister);
234 double next_delay_seconds =
235 CalculateBackoff(static_cast<double>(status->next_delay.InSeconds()),
236 kInitialRegistrationDelaySeconds,
237 kMinRegistrationDelaySeconds,
238 kMaxRegistrationDelaySeconds,
239 kRegistrationDelayExponent,
240 GetJitter(),
241 kRegistrationDelayMaxJitter);
242 status->next_delay =
243 base::TimeDelta::FromSeconds(static_cast<int64>(next_delay_seconds));
[email protected]060588c2011-11-29 21:38:41244 DVLOG(2) << "New next delay for "
[email protected]8e4c88872012-06-28 04:34:35245 << ObjectIdToString(id) << " is "
[email protected]060588c2011-11-29 21:38:41246 << status->next_delay.InSeconds() << " seconds";
[email protected]b7af0f92011-03-10 23:35:48247 } else {
[email protected]060588c2011-11-29 21:38:41248 DVLOG(2) << "Not a retry -- registering "
[email protected]8e4c88872012-06-28 04:34:35249 << ObjectIdToString(id) << " immediately";
[email protected]b7af0f92011-03-10 23:35:48250 status->delay = base::TimeDelta();
251 status->next_delay = base::TimeDelta();
252 status->DoRegister();
253 }
254}
255
[email protected]8e4c88872012-06-28 04:34:35256void RegistrationManager::DoRegisterId(const invalidation::ObjectId& id) {
[email protected]870bcb32012-06-11 22:27:54257 DCHECK(CalledOnValidThread());
[email protected]8e4c88872012-06-28 04:34:35258 invalidation_client_->Register(id);
259 RegistrationStatusMap::const_iterator it = registration_statuses_.find(id);
260 if (it == registration_statuses_.end()) {
[email protected]941ddba2014-03-25 01:50:13261 NOTREACHED() << "DoRegisterId called on " << ObjectIdToString(id)
262 << " which is not in the registration map";
[email protected]58d16662011-02-16 01:44:45263 return;
264 }
[email protected]8e4c88872012-06-28 04:34:35265 it->second->state = invalidation::InvalidationListener::REGISTERED;
266 it->second->last_registration_request = base::Time::Now();
[email protected]58d16662011-02-16 01:44:45267}
268
[email protected]8e4c88872012-06-28 04:34:35269void RegistrationManager::UnregisterId(const invalidation::ObjectId& id) {
[email protected]870bcb32012-06-11 22:27:54270 DCHECK(CalledOnValidThread());
[email protected]8e4c88872012-06-28 04:34:35271 invalidation_client_->Unregister(id);
272 RegistrationStatusMap::iterator it = registration_statuses_.find(id);
273 if (it == registration_statuses_.end()) {
[email protected]941ddba2014-03-25 01:50:13274 NOTREACHED() << "UnregisterId called on " << ObjectIdToString(id)
275 << " which is not in the registration map";
[email protected]58d16662011-02-16 01:44:45276 return;
277 }
[email protected]8e4c88872012-06-28 04:34:35278 delete it->second;
279 registration_statuses_.erase(it);
[email protected]58d16662011-02-16 01:44:45280}
281
[email protected]8e4c88872012-06-28 04:34:35282
283ObjectIdSet RegistrationManager::GetRegisteredIds() const {
[email protected]870bcb32012-06-11 22:27:54284 DCHECK(CalledOnValidThread());
[email protected]8e4c88872012-06-28 04:34:35285 ObjectIdSet ids;
286 for (RegistrationStatusMap::const_iterator it =
287 registration_statuses_.begin();
288 it != registration_statuses_.end(); ++it) {
289 if (IsIdRegistered(it->first)) {
290 ids.insert(it->first);
291 }
292 }
293 return ids;
294}
295
296bool RegistrationManager::IsIdRegistered(
297 const invalidation::ObjectId& id) const {
298 DCHECK(CalledOnValidThread());
299 RegistrationStatusMap::const_iterator it =
300 registration_statuses_.find(id);
301 return it != registration_statuses_.end() &&
302 it->second->state == invalidation::InvalidationListener::REGISTERED;
[email protected]6a1d0f52010-06-30 05:11:53303}
[email protected]b7af0f92011-03-10 23:35:48304
[email protected]65f173552012-06-28 22:43:58305} // namespace syncer