Home | History | Annotate | Download | only in glue
      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 "chrome/browser/sync/glue/model_association_manager.h"
      6 
      7 #include <algorithm>
      8 #include <functional>
      9 
     10 #include "base/debug/trace_event.h"
     11 #include "base/logging.h"
     12 #include "base/message_loop/message_loop.h"
     13 #include "base/metrics/histogram.h"
     14 #include "sync/internal_api/public/base/model_type.h"
     15 
     16 using content::BrowserThread;
     17 using syncer::ModelTypeSet;
     18 
     19 namespace browser_sync {
     20 
     21 namespace {
     22 
     23 static const syncer::ModelType kStartOrder[] = {
     24   syncer::NIGORI,               //  Listed for completeness.
     25   syncer::DEVICE_INFO,          //  Listed for completeness.
     26   syncer::EXPERIMENTS,          //  Listed for completeness.
     27   syncer::PROXY_TABS,           //  Listed for completeness.
     28 
     29   // Kick off the association of the non-UI types first so they can associate
     30   // in parallel with the UI types.
     31   syncer::PASSWORDS,
     32   syncer::AUTOFILL,
     33   syncer::AUTOFILL_PROFILE,
     34   syncer::EXTENSION_SETTINGS,
     35   syncer::APP_SETTINGS,
     36   syncer::TYPED_URLS,
     37   syncer::HISTORY_DELETE_DIRECTIVES,
     38   syncer::SYNCED_NOTIFICATIONS,
     39 
     40   // UI thread data types.
     41   syncer::BOOKMARKS,
     42   syncer::MANAGED_USERS,        //  Syncing managed users on initial login might
     43                                 //  block creating a new managed user, so we
     44                                 //  want to do it early.
     45   syncer::PREFERENCES,
     46   syncer::PRIORITY_PREFERENCES,
     47   syncer::EXTENSIONS,
     48   syncer::APPS,
     49   syncer::APP_LIST,
     50   syncer::THEMES,
     51   syncer::SEARCH_ENGINES,
     52   syncer::SESSIONS,
     53   syncer::APP_NOTIFICATIONS,
     54   syncer::DICTIONARY,
     55   syncer::FAVICON_IMAGES,
     56   syncer::FAVICON_TRACKING,
     57   syncer::MANAGED_USER_SETTINGS,
     58   syncer::ARTICLES,
     59 };
     60 
     61 COMPILE_ASSERT(arraysize(kStartOrder) ==
     62                syncer::MODEL_TYPE_COUNT - syncer::FIRST_REAL_MODEL_TYPE,
     63                kStartOrder_IncorrectSize);
     64 
     65 // The amount of time we wait for association to finish. If some types haven't
     66 // finished association by the time, configuration result will be
     67 // PARTIAL_SUCCESS and DataTypeManager is notified of the unfinished types.
     68 const int64 kAssociationTimeOutInSeconds = 600;
     69 
     70 syncer::DataTypeAssociationStats BuildAssociationStatsFromMergeResults(
     71     const syncer::SyncMergeResult& local_merge_result,
     72     const syncer::SyncMergeResult& syncer_merge_result,
     73     const base::TimeDelta& association_wait_time,
     74     const base::TimeDelta& association_time) {
     75   DCHECK_EQ(local_merge_result.model_type(), syncer_merge_result.model_type());
     76   syncer::DataTypeAssociationStats stats;
     77   stats.had_error = local_merge_result.error().IsSet() ||
     78                     syncer_merge_result.error().IsSet();
     79   stats.num_local_items_before_association =
     80       local_merge_result.num_items_before_association();
     81   stats.num_sync_items_before_association =
     82       syncer_merge_result.num_items_before_association();
     83   stats.num_local_items_after_association =
     84       local_merge_result.num_items_after_association();
     85   stats.num_sync_items_after_association =
     86       syncer_merge_result.num_items_after_association();
     87   stats.num_local_items_added =
     88       local_merge_result.num_items_added();
     89   stats.num_local_items_deleted =
     90       local_merge_result.num_items_deleted();
     91   stats.num_local_items_modified =
     92       local_merge_result.num_items_modified();
     93   stats.local_version_pre_association =
     94       local_merge_result.pre_association_version();
     95   stats.num_sync_items_added =
     96       syncer_merge_result.num_items_added();
     97   stats.num_sync_items_deleted =
     98       syncer_merge_result.num_items_deleted();
     99   stats.num_sync_items_modified =
    100       syncer_merge_result.num_items_modified();
    101   stats.sync_version_pre_association =
    102       syncer_merge_result.pre_association_version();
    103   stats.association_wait_time = association_wait_time;
    104   stats.association_time = association_time;
    105   return stats;
    106 }
    107 
    108 }  // namespace
    109 
    110 ModelAssociationManager::ModelAssociationManager(
    111     const DataTypeController::TypeMap* controllers,
    112     ModelAssociationResultProcessor* processor)
    113     : state_(IDLE),
    114       controllers_(controllers),
    115       result_processor_(processor),
    116       weak_ptr_factory_(this),
    117       configure_status_(DataTypeManager::UNKNOWN) {
    118   // Ensure all data type controllers are stopped.
    119   for (DataTypeController::TypeMap::const_iterator it = controllers_->begin();
    120        it != controllers_->end(); ++it) {
    121     DCHECK_EQ(DataTypeController::NOT_RUNNING, (*it).second->state());
    122   }
    123 }
    124 
    125 ModelAssociationManager::~ModelAssociationManager() {
    126 }
    127 
    128 void ModelAssociationManager::Initialize(syncer::ModelTypeSet desired_types) {
    129   // state_ can be INITIALIZED_TO_CONFIGURE if types are reconfigured when
    130   // data is being downloaded, so StartAssociationAsync() is never called for
    131   // the first configuration.
    132   DCHECK_NE(CONFIGURING, state_);
    133 
    134   // Only keep types that have controllers.
    135   desired_types_.Clear();
    136   slow_types_.Clear();
    137   for (syncer::ModelTypeSet::Iterator it = desired_types.First();
    138       it.Good(); it.Inc()) {
    139     if (controllers_->find(it.Get()) != controllers_->end())
    140       desired_types_.Put(it.Get());
    141   }
    142 
    143   DVLOG(1) << "ModelAssociationManager: Initializing for "
    144            << syncer::ModelTypeSetToString(desired_types_);
    145 
    146   state_ = INITIALIZED_TO_CONFIGURE;
    147 
    148   StopDisabledTypes();
    149   LoadEnabledTypes();
    150 }
    151 
    152 void ModelAssociationManager::StopDisabledTypes() {
    153   DVLOG(1) << "ModelAssociationManager: Stopping disabled types.";
    154   for (DataTypeController::TypeMap::const_iterator it = controllers_->begin();
    155        it != controllers_->end(); ++it) {
    156     DataTypeController* dtc = (*it).second.get();
    157     if (dtc->state() != DataTypeController::NOT_RUNNING &&
    158         (!desired_types_.Has(dtc->type()) ||
    159             failed_data_types_info_.count(dtc->type()) > 0)) {
    160       DVLOG(1) << "ModelTypeToString: stop " << dtc->name();
    161       dtc->Stop();
    162 
    163       loaded_types_.Remove(dtc->type());
    164       associated_types_.Remove(dtc->type());
    165     }
    166   }
    167 }
    168 
    169 void ModelAssociationManager::LoadEnabledTypes() {
    170   // Load in kStartOrder.
    171   for (size_t i = 0; i < arraysize(kStartOrder); i++) {
    172     syncer::ModelType type = kStartOrder[i];
    173     if (!desired_types_.Has(type))
    174       continue;
    175 
    176     DCHECK(controllers_->find(type) != controllers_->end());
    177     DataTypeController* dtc = controllers_->find(type)->second.get();
    178     if (dtc->state() == DataTypeController::NOT_RUNNING) {
    179       DCHECK(!loaded_types_.Has(dtc->type()));
    180       DCHECK(!associated_types_.Has(dtc->type()));
    181       dtc->LoadModels(base::Bind(&ModelAssociationManager::ModelLoadCallback,
    182                                  weak_ptr_factory_.GetWeakPtr()));
    183     }
    184   }
    185 }
    186 
    187 void ModelAssociationManager::StartAssociationAsync(
    188     const syncer::ModelTypeSet& types_to_associate) {
    189   DCHECK_NE(CONFIGURING, state_);
    190   state_ = CONFIGURING;
    191 
    192   association_start_time_ = base::TimeTicks::Now();
    193 
    194   requested_types_ = types_to_associate;
    195 
    196   associating_types_ = types_to_associate;
    197   associating_types_.RetainAll(desired_types_);
    198   associating_types_.RemoveAll(associated_types_);
    199 
    200   // Assume success.
    201   configure_status_ = DataTypeManager::OK;
    202 
    203   // Remove types that already failed.
    204   for (std::map<syncer::ModelType, syncer::SyncError>::const_iterator it =
    205           failed_data_types_info_.begin();
    206       it != failed_data_types_info_.end(); ++it) {
    207     associating_types_.Remove(it->first);
    208   }
    209 
    210   // Done if no types to associate.
    211   if (associating_types_.Empty()) {
    212     ModelAssociationDone();
    213     return;
    214   }
    215 
    216   timer_.Start(FROM_HERE,
    217                base::TimeDelta::FromSeconds(kAssociationTimeOutInSeconds),
    218                this,
    219                &ModelAssociationManager::ModelAssociationDone);
    220 
    221   // Start association of types that are loaded in specified order.
    222   for (size_t i = 0; i < arraysize(kStartOrder); i++) {
    223     syncer::ModelType type = kStartOrder[i];
    224     if (!associating_types_.Has(type) || !loaded_types_.Has(type))
    225       continue;
    226 
    227     DataTypeController* dtc = controllers_->find(type)->second.get();
    228     DCHECK(DataTypeController::MODEL_LOADED == dtc->state() ||
    229            DataTypeController::ASSOCIATING == dtc->state());
    230     if (dtc->state() == DataTypeController::MODEL_LOADED) {
    231       TRACE_EVENT_ASYNC_BEGIN1("sync", "ModelAssociation",
    232                                dtc,
    233                                "DataType",
    234                                ModelTypeToString(type));
    235 
    236       dtc->StartAssociating(
    237           base::Bind(&ModelAssociationManager::TypeStartCallback,
    238                      weak_ptr_factory_.GetWeakPtr(),
    239                      type, base::TimeTicks::Now()));
    240     }
    241   }
    242 }
    243 
    244 void ModelAssociationManager::ResetForNextAssociation() {
    245   DVLOG(1) << "ModelAssociationManager: Reseting for next configuration";
    246   // |loaded_types_| and |associated_types_| are not cleared. So
    247   // reconfiguration won't restart types that are already started.
    248   requested_types_.Clear();
    249   failed_data_types_info_.clear();
    250   associating_types_.Clear();
    251   needs_crypto_types_.Clear();
    252 }
    253 
    254 void ModelAssociationManager::Stop() {
    255   // Ignore callbacks from controllers.
    256   weak_ptr_factory_.InvalidateWeakPtrs();
    257 
    258   // Stop started data types.
    259   for (DataTypeController::TypeMap::const_iterator it = controllers_->begin();
    260        it != controllers_->end(); ++it) {
    261     DataTypeController* dtc = (*it).second.get();
    262     if (dtc->state() != DataTypeController::NOT_RUNNING) {
    263       dtc->Stop();
    264       DVLOG(1) << "ModelAssociationManager: Stopped " << dtc->name();
    265     }
    266   }
    267 
    268   desired_types_.Clear();
    269   loaded_types_.Clear();
    270   associated_types_.Clear();
    271   slow_types_.Clear();
    272 
    273   if (state_ == CONFIGURING) {
    274     if (configure_status_ == DataTypeManager::OK)
    275       configure_status_ = DataTypeManager::ABORTED;
    276     DVLOG(1) << "ModelAssociationManager: Calling OnModelAssociationDone";
    277     ModelAssociationDone();
    278   }
    279 
    280   ResetForNextAssociation();
    281 
    282   state_ = IDLE;
    283 }
    284 
    285 void ModelAssociationManager::AppendToFailedDatatypesAndLogError(
    286     const syncer::SyncError& error) {
    287   failed_data_types_info_[error.model_type()] = error;
    288   LOG(ERROR) << "Failed to associate models for "
    289              << syncer::ModelTypeToString(error.model_type());
    290   UMA_HISTOGRAM_ENUMERATION("Sync.ConfigureFailed",
    291                             ModelTypeToHistogramInt(error.model_type()),
    292                             syncer::MODEL_TYPE_COUNT);
    293 }
    294 
    295 void ModelAssociationManager::ModelLoadCallback(syncer::ModelType type,
    296                                                 syncer::SyncError error) {
    297   DVLOG(1) << "ModelAssociationManager: ModelLoadCallback for "
    298       << syncer::ModelTypeToString(type);
    299 
    300   // TODO(haitaol): temporary fix for 335606.
    301   if (slow_types_.Has(type))
    302     return;
    303 
    304   // This happens when slow loading type is disabled by new configuration.
    305   if (!desired_types_.Has(type))
    306     return;
    307 
    308   DCHECK(!loaded_types_.Has(type));
    309   if (error.IsSet()) {
    310     syncer::SyncMergeResult local_merge_result(type);
    311     local_merge_result.set_error(error);
    312     TypeStartCallback(type,
    313                       base::TimeTicks::Now(),
    314                       DataTypeController::ASSOCIATION_FAILED,
    315                       local_merge_result,
    316                       syncer::SyncMergeResult(type));
    317     return;
    318   }
    319 
    320   loaded_types_.Put(type);
    321   if (associating_types_.Has(type) || slow_types_.Has(type)) {
    322     DataTypeController* dtc = controllers_->find(type)->second.get();
    323     dtc->StartAssociating(
    324         base::Bind(&ModelAssociationManager::TypeStartCallback,
    325                    weak_ptr_factory_.GetWeakPtr(),
    326                    type, base::TimeTicks::Now()));
    327   }
    328 }
    329 
    330 void ModelAssociationManager::TypeStartCallback(
    331     syncer::ModelType type,
    332     base::TimeTicks type_start_time,
    333     DataTypeController::StartResult start_result,
    334     const syncer::SyncMergeResult& local_merge_result,
    335     const syncer::SyncMergeResult& syncer_merge_result) {
    336   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    337 
    338   // TODO(haitaol): temporary fix for 335606.
    339   if (slow_types_.Has(type))
    340     return;
    341 
    342   // This happens when slow associating type is disabled by new configuration.
    343   if (!desired_types_.Has(type))
    344     return;
    345 
    346   slow_types_.Remove(type);
    347 
    348   DCHECK(!associated_types_.Has(type));
    349   if (DataTypeController::IsSuccessfulResult(start_result)) {
    350     associated_types_.Put(type);
    351   } else if (state_ == IDLE) {
    352     // For type that failed in IDLE mode, simply stop the controller. Next
    353     // configuration will try to restart from scratch if the type is still
    354     // enabled.
    355     DataTypeController* dtc = controllers_->find(type)->second.get();
    356     if (dtc->state() != DataTypeController::NOT_RUNNING)
    357       dtc->Stop();
    358     loaded_types_.Remove(type);
    359   } else {
    360     // Record error in CONFIGURING or INITIALIZED_TO_CONFIGURE mode. The error
    361     // will be reported when data types association finishes.
    362     if (start_result == DataTypeController::NEEDS_CRYPTO) {
    363       DVLOG(1) << "ModelAssociationManager: Encountered an undecryptable type";
    364       needs_crypto_types_.Put(type);
    365     } else {
    366       DVLOG(1) << "ModelAssociationManager: Encountered a failed type";
    367       AppendToFailedDatatypesAndLogError(local_merge_result.error());
    368     }
    369   }
    370 
    371   if (state_ != CONFIGURING)
    372     return;
    373 
    374   TRACE_EVENT_ASYNC_END1("sync", "ModelAssociation",
    375                          controllers_->find(type)->second.get(),
    376                          "DataType",
    377                          ModelTypeToString(type));
    378 
    379   // Track the merge results if we succeeded or an association failure
    380   // occurred.
    381   if ((DataTypeController::IsSuccessfulResult(start_result) ||
    382        start_result == DataTypeController::ASSOCIATION_FAILED) &&
    383       syncer::ProtocolTypes().Has(type)) {
    384     base::TimeDelta association_wait_time =
    385         std::max(base::TimeDelta(), type_start_time - association_start_time_);
    386     base::TimeDelta association_time =
    387         base::TimeTicks::Now() - type_start_time;;
    388     syncer::DataTypeAssociationStats stats =
    389         BuildAssociationStatsFromMergeResults(local_merge_result,
    390                                               syncer_merge_result,
    391                                               association_wait_time,
    392                                               association_time);
    393     result_processor_->OnSingleDataTypeAssociationDone(type, stats);
    394   }
    395 
    396   // Update configuration result.
    397   if (configure_status_ == DataTypeManager::OK &&
    398       start_result == DataTypeController::ASSOCIATION_FAILED) {
    399     configure_status_ = DataTypeManager::PARTIAL_SUCCESS;
    400   }
    401   if (start_result == DataTypeController::UNRECOVERABLE_ERROR)
    402     configure_status_ = DataTypeManager::UNRECOVERABLE_ERROR;
    403 
    404   associating_types_.Remove(type);
    405 
    406   if (associating_types_.Empty())
    407     ModelAssociationDone();
    408 }
    409 
    410 void ModelAssociationManager::ModelAssociationDone() {
    411   CHECK_EQ(CONFIGURING, state_);
    412 
    413   timer_.Stop();
    414 
    415   slow_types_.PutAll(associating_types_);
    416 
    417   // TODO(haitaol): temporary fix for 335606.
    418   for (syncer::ModelTypeSet::Iterator it = associating_types_.First();
    419       it.Good(); it.Inc()) {
    420     AppendToFailedDatatypesAndLogError(
    421         syncer::SyncError(FROM_HERE, syncer::SyncError::DATATYPE_ERROR,
    422                           "Association timed out.", it.Get()));
    423   }
    424 
    425   // Stop controllers of failed types.
    426   StopDisabledTypes();
    427 
    428   if (configure_status_ == DataTypeManager::OK &&
    429       (!associating_types_.Empty() || !failed_data_types_info_.empty() ||
    430           !needs_crypto_types_.Empty())) {
    431     // We have not configured all types that we have been asked to configure.
    432     // Either we have failed types or types that have not completed loading
    433     // yet.
    434     DVLOG(1) << "ModelAssociationManager: setting partial success";
    435     configure_status_ = DataTypeManager::PARTIAL_SUCCESS;
    436   }
    437 
    438   DataTypeManager::ConfigureResult result(configure_status_,
    439                                           requested_types_,
    440                                           failed_data_types_info_,
    441                                           associating_types_,
    442                                           needs_crypto_types_);
    443 
    444   // Reset state before notifying |result_processor_| because that might
    445   // trigger a new round of configuration.
    446   ResetForNextAssociation();
    447   state_ = IDLE;
    448 
    449   result_processor_->OnModelAssociationDone(result);
    450 }
    451 
    452 base::OneShotTimer<ModelAssociationManager>*
    453     ModelAssociationManager::GetTimerForTesting() {
    454   return &timer_;
    455 }
    456 
    457 }  // namespace browser_sync
    458