Home | History | Annotate | Download | only in notifier
      1 // Copyright (c) 2012 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/notifier/registration_manager.h"
      6 
      7 #include <algorithm>
      8 #include <cstddef>
      9 #include <iterator>
     10 #include <string>
     11 #include <utility>
     12 
     13 #include "base/rand_util.h"
     14 #include "base/stl_util.h"
     15 #include "google/cacheinvalidation/include/invalidation-client.h"
     16 #include "google/cacheinvalidation/include/types.h"
     17 #include "sync/notifier/invalidation_util.h"
     18 
     19 namespace syncer {
     20 
     21 RegistrationManager::PendingRegistrationInfo::PendingRegistrationInfo() {}
     22 
     23 RegistrationManager::RegistrationStatus::RegistrationStatus(
     24     const invalidation::ObjectId& id, RegistrationManager* manager)
     25     : id(id),
     26       registration_manager(manager),
     27       enabled(true),
     28       state(invalidation::InvalidationListener::UNREGISTERED) {
     29   DCHECK(registration_manager);
     30 }
     31 
     32 RegistrationManager::RegistrationStatus::~RegistrationStatus() {}
     33 
     34 void RegistrationManager::RegistrationStatus::DoRegister() {
     35   CHECK(enabled);
     36   // We might be called explicitly, so stop the timer manually and
     37   // reset the delay.
     38   registration_timer.Stop();
     39   delay = base::TimeDelta();
     40   registration_manager->DoRegisterId(id);
     41   DCHECK(!last_registration_request.is_null());
     42 }
     43 
     44 void RegistrationManager::RegistrationStatus::Disable() {
     45   enabled = false;
     46   state = invalidation::InvalidationListener::UNREGISTERED;
     47   registration_timer.Stop();
     48   delay = base::TimeDelta();
     49 }
     50 
     51 const int RegistrationManager::kInitialRegistrationDelaySeconds = 5;
     52 const int RegistrationManager::kRegistrationDelayExponent = 2;
     53 const double RegistrationManager::kRegistrationDelayMaxJitter = 0.5;
     54 const int RegistrationManager::kMinRegistrationDelaySeconds = 1;
     55 // 1 hour.
     56 const int RegistrationManager::kMaxRegistrationDelaySeconds = 60 * 60;
     57 
     58 RegistrationManager::RegistrationManager(
     59     invalidation::InvalidationClient* invalidation_client)
     60     : invalidation_client_(invalidation_client) {
     61   DCHECK(invalidation_client_);
     62 }
     63 
     64 RegistrationManager::~RegistrationManager() {
     65   DCHECK(CalledOnValidThread());
     66   STLDeleteValues(&registration_statuses_);
     67 }
     68 
     69 ObjectIdSet RegistrationManager::UpdateRegisteredIds(const ObjectIdSet& ids) {
     70   DCHECK(CalledOnValidThread());
     71 
     72   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 */);
     93     }
     94   }
     95 
     96   return to_unregister;
     97 }
     98 
     99 void RegistrationManager::MarkRegistrationLost(
    100     const invalidation::ObjectId& id) {
    101   DCHECK(CalledOnValidThread());
    102   RegistrationStatusMap::const_iterator it = registration_statuses_.find(id);
    103   if (it == registration_statuses_.end()) {
    104     DLOG(WARNING) << "Attempt to mark non-existent registration for "
    105                   << ObjectIdToString(id) << " as lost";
    106     return;
    107   }
    108   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);
    114 }
    115 
    116 void RegistrationManager::MarkAllRegistrationsLost() {
    117   DCHECK(CalledOnValidThread());
    118   for (RegistrationStatusMap::const_iterator it =
    119            registration_statuses_.begin();
    120        it != registration_statuses_.end(); ++it) {
    121     if (IsIdRegistered(it->first)) {
    122       MarkRegistrationLost(it->first);
    123     }
    124   }
    125 }
    126 
    127 void RegistrationManager::DisableId(const invalidation::ObjectId& id) {
    128   DCHECK(CalledOnValidThread());
    129   RegistrationStatusMap::const_iterator it = registration_statuses_.find(id);
    130   if (it == registration_statuses_.end()) {
    131     DLOG(WARNING) << "Attempt to disable non-existent registration for "
    132                   << ObjectIdToString(id);
    133     return;
    134   }
    135   it->second->Disable();
    136 }
    137 
    138 // static
    139 double 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 
    157 ObjectIdSet RegistrationManager::GetRegisteredIdsForTest() const {
    158   return GetRegisteredIds();
    159 }
    160 
    161 RegistrationManager::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 
    183 void 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 
    194 double 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 
    202 void RegistrationManager::TryRegisterId(const invalidation::ObjectId& id,
    203                                         bool is_retry) {
    204   DCHECK(CalledOnValidThread());
    205   RegistrationStatusMap::const_iterator it = registration_statuses_.find(id);
    206   if (it == registration_statuses_.end()) {
    207     DLOG(FATAL) << "TryRegisterId called on " << ObjectIdToString(id)
    208                 << " which is not in the registration map";
    209     return;
    210   }
    211   RegistrationStatus* status = it->second;
    212   if (!status->enabled) {
    213     // Disabled, so do nothing.
    214     return;
    215   }
    216   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;
    228     DVLOG(2) << "Registering "
    229              << ObjectIdToString(id) << " in "
    230              << delay.InMilliseconds() << " ms";
    231     status->registration_timer.Stop();
    232     status->registration_timer.Start(FROM_HERE,
    233         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));
    244     DVLOG(2) << "New next delay for "
    245              << ObjectIdToString(id) << " is "
    246              << status->next_delay.InSeconds() << " seconds";
    247   } else {
    248     DVLOG(2) << "Not a retry -- registering "
    249              << ObjectIdToString(id) << " immediately";
    250     status->delay = base::TimeDelta();
    251     status->next_delay = base::TimeDelta();
    252     status->DoRegister();
    253   }
    254 }
    255 
    256 void RegistrationManager::DoRegisterId(const invalidation::ObjectId& id) {
    257   DCHECK(CalledOnValidThread());
    258   invalidation_client_->Register(id);
    259   RegistrationStatusMap::const_iterator it = registration_statuses_.find(id);
    260   if (it == registration_statuses_.end()) {
    261     DLOG(FATAL) << "DoRegisterId called on " << ObjectIdToString(id)
    262                 << " which is not in the registration map";
    263     return;
    264   }
    265   it->second->state = invalidation::InvalidationListener::REGISTERED;
    266   it->second->last_registration_request = base::Time::Now();
    267 }
    268 
    269 void RegistrationManager::UnregisterId(const invalidation::ObjectId& id) {
    270   DCHECK(CalledOnValidThread());
    271   invalidation_client_->Unregister(id);
    272   RegistrationStatusMap::iterator it = registration_statuses_.find(id);
    273   if (it == registration_statuses_.end()) {
    274     DLOG(FATAL) << "UnregisterId called on " << ObjectIdToString(id)
    275                 << " which is not in the registration map";
    276     return;
    277   }
    278   delete it->second;
    279   registration_statuses_.erase(it);
    280 }
    281 
    282 
    283 ObjectIdSet RegistrationManager::GetRegisteredIds() const {
    284   DCHECK(CalledOnValidThread());
    285   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 
    296 bool 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;
    303 }
    304 
    305 }  // namespace syncer
    306