Home | History | Annotate | Download | only in sync_driver
      1 // Copyright 2014 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 "components/sync_driver/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 syncer::ModelTypeSet;
     17 
     18 namespace sync_driver {
     19 
     20 namespace {
     21 
     22 static const syncer::ModelType kStartOrder[] = {
     23   syncer::NIGORI,               //  Listed for completeness.
     24   syncer::DEVICE_INFO,          //  Listed for completeness.
     25   syncer::EXPERIMENTS,          //  Listed for completeness.
     26   syncer::PROXY_TABS,           //  Listed for completeness.
     27 
     28   // Kick off the association of the non-UI types first so they can associate
     29   // in parallel with the UI types.
     30   syncer::PASSWORDS,
     31   syncer::AUTOFILL,
     32   syncer::AUTOFILL_PROFILE,
     33   syncer::EXTENSION_SETTINGS,
     34   syncer::APP_SETTINGS,
     35   syncer::TYPED_URLS,
     36   syncer::HISTORY_DELETE_DIRECTIVES,
     37   syncer::SYNCED_NOTIFICATIONS,
     38   syncer::SYNCED_NOTIFICATION_APP_INFO,
     39 
     40   // UI thread data types.
     41   syncer::BOOKMARKS,
     42   syncer::SUPERVISED_USERS,     //  Syncing supervised users on initial login
     43                                 //  might block creating a new supervised user,
     44                                 //  so we 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::SUPERVISED_USER_SETTINGS,
     58   syncer::SUPERVISED_USER_SHARED_SETTINGS,
     59   syncer::ARTICLES,
     60 };
     61 
     62 COMPILE_ASSERT(arraysize(kStartOrder) ==
     63                syncer::MODEL_TYPE_COUNT - syncer::FIRST_REAL_MODEL_TYPE,
     64                kStartOrder_IncorrectSize);
     65 
     66 // The amount of time we wait for association to finish. If some types haven't
     67 // finished association by the time, DataTypeManager is notified of the
     68 // unfinished types.
     69 const int64 kAssociationTimeOutInSeconds = 600;
     70 
     71 syncer::DataTypeAssociationStats BuildAssociationStatsFromMergeResults(
     72     const syncer::SyncMergeResult& local_merge_result,
     73     const syncer::SyncMergeResult& syncer_merge_result,
     74     const base::TimeDelta& association_wait_time,
     75     const base::TimeDelta& association_time) {
     76   DCHECK_EQ(local_merge_result.model_type(), syncer_merge_result.model_type());
     77   syncer::DataTypeAssociationStats stats;
     78   stats.had_error = local_merge_result.error().IsSet() ||
     79                     syncer_merge_result.error().IsSet();
     80   stats.num_local_items_before_association =
     81       local_merge_result.num_items_before_association();
     82   stats.num_sync_items_before_association =
     83       syncer_merge_result.num_items_before_association();
     84   stats.num_local_items_after_association =
     85       local_merge_result.num_items_after_association();
     86   stats.num_sync_items_after_association =
     87       syncer_merge_result.num_items_after_association();
     88   stats.num_local_items_added =
     89       local_merge_result.num_items_added();
     90   stats.num_local_items_deleted =
     91       local_merge_result.num_items_deleted();
     92   stats.num_local_items_modified =
     93       local_merge_result.num_items_modified();
     94   stats.local_version_pre_association =
     95       local_merge_result.pre_association_version();
     96   stats.num_sync_items_added =
     97       syncer_merge_result.num_items_added();
     98   stats.num_sync_items_deleted =
     99       syncer_merge_result.num_items_deleted();
    100   stats.num_sync_items_modified =
    101       syncer_merge_result.num_items_modified();
    102   stats.sync_version_pre_association =
    103       syncer_merge_result.pre_association_version();
    104   stats.association_wait_time = association_wait_time;
    105   stats.association_time = association_time;
    106   return stats;
    107 }
    108 
    109 }  // namespace
    110 
    111 ModelAssociationManager::ModelAssociationManager(
    112     const DataTypeController::TypeMap* controllers,
    113     ModelAssociationManagerDelegate* processor)
    114     : state_(IDLE),
    115       controllers_(controllers),
    116       delegate_(processor),
    117       configure_status_(DataTypeManager::UNKNOWN),
    118       weak_ptr_factory_(this) {
    119   // Ensure all data type controllers are stopped.
    120   for (DataTypeController::TypeMap::const_iterator it = controllers_->begin();
    121        it != controllers_->end(); ++it) {
    122     DCHECK_EQ(DataTypeController::NOT_RUNNING, (*it).second->state());
    123   }
    124 }
    125 
    126 ModelAssociationManager::~ModelAssociationManager() {
    127 }
    128 
    129 void ModelAssociationManager::Initialize(syncer::ModelTypeSet desired_types) {
    130   // state_ can be INITIALIZED_TO_CONFIGURE if types are reconfigured when
    131   // data is being downloaded, so StartAssociationAsync() is never called for
    132   // the first configuration.
    133   DCHECK_NE(CONFIGURING, state_);
    134 
    135   // Only keep types that have controllers.
    136   desired_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::StopDatatype(
    153     const syncer::SyncError& error,
    154     DataTypeController* dtc) {
    155   loaded_types_.Remove(dtc->type());
    156   associated_types_.Remove(dtc->type());
    157   associating_types_.Remove(dtc->type());
    158 
    159   if (error.IsSet() || dtc->state() != DataTypeController::NOT_RUNNING) {
    160     // If an error was set, the delegate must be informed of the error.
    161     delegate_->OnSingleDataTypeWillStop(dtc->type(), error);
    162     dtc->Stop();
    163   }
    164 }
    165 
    166 void ModelAssociationManager::StopDisabledTypes() {
    167   DVLOG(1) << "ModelAssociationManager: Stopping disabled types.";
    168   for (DataTypeController::TypeMap::const_iterator it = controllers_->begin();
    169        it != controllers_->end(); ++it) {
    170     DataTypeController* dtc = (*it).second.get();
    171     if (dtc->state() != DataTypeController::NOT_RUNNING &&
    172         !desired_types_.Has(dtc->type())) {
    173       DVLOG(1) << "ModelTypeToString: stop " << dtc->name();
    174       StopDatatype(syncer::SyncError(), dtc);
    175     }
    176   }
    177 }
    178 
    179 void ModelAssociationManager::LoadEnabledTypes() {
    180   // Load in kStartOrder.
    181   for (size_t i = 0; i < arraysize(kStartOrder); i++) {
    182     syncer::ModelType type = kStartOrder[i];
    183     if (!desired_types_.Has(type))
    184       continue;
    185 
    186     DCHECK(controllers_->find(type) != controllers_->end());
    187     DataTypeController* dtc = controllers_->find(type)->second.get();
    188     if (dtc->state() == DataTypeController::NOT_RUNNING) {
    189       DCHECK(!loaded_types_.Has(dtc->type()));
    190       DCHECK(!associated_types_.Has(dtc->type()));
    191       dtc->LoadModels(base::Bind(&ModelAssociationManager::ModelLoadCallback,
    192                                  weak_ptr_factory_.GetWeakPtr()));
    193     }
    194   }
    195 }
    196 
    197 void ModelAssociationManager::StartAssociationAsync(
    198     const syncer::ModelTypeSet& types_to_associate) {
    199   DCHECK_NE(CONFIGURING, state_);
    200   state_ = CONFIGURING;
    201 
    202   association_start_time_ = base::TimeTicks::Now();
    203 
    204   requested_types_ = types_to_associate;
    205 
    206   associating_types_ = types_to_associate;
    207   associating_types_.RetainAll(desired_types_);
    208   associating_types_.RemoveAll(associated_types_);
    209 
    210   // Assume success.
    211   configure_status_ = DataTypeManager::OK;
    212 
    213   // Done if no types to associate.
    214   if (associating_types_.Empty()) {
    215     ModelAssociationDone();
    216     return;
    217   }
    218 
    219   timer_.Start(FROM_HERE,
    220                base::TimeDelta::FromSeconds(kAssociationTimeOutInSeconds),
    221                this,
    222                &ModelAssociationManager::ModelAssociationDone);
    223 
    224   // Start association of types that are loaded in specified order.
    225   for (size_t i = 0; i < arraysize(kStartOrder); i++) {
    226     syncer::ModelType type = kStartOrder[i];
    227     if (!associating_types_.Has(type) || !loaded_types_.Has(type))
    228       continue;
    229 
    230     DataTypeController* dtc = controllers_->find(type)->second.get();
    231     DCHECK(DataTypeController::MODEL_LOADED == dtc->state() ||
    232            DataTypeController::ASSOCIATING == dtc->state());
    233     if (dtc->state() == DataTypeController::MODEL_LOADED) {
    234       TRACE_EVENT_ASYNC_BEGIN1("sync", "ModelAssociation",
    235                                dtc,
    236                                "DataType",
    237                                ModelTypeToString(type));
    238 
    239       dtc->StartAssociating(
    240           base::Bind(&ModelAssociationManager::TypeStartCallback,
    241                      weak_ptr_factory_.GetWeakPtr(),
    242                      type, base::TimeTicks::Now()));
    243     }
    244   }
    245 }
    246 
    247 void ModelAssociationManager::ResetForNextAssociation() {
    248   DVLOG(1) << "ModelAssociationManager: Reseting for next configuration";
    249   // |loaded_types_| and |associated_types_| are not cleared. So
    250   // reconfiguration won't restart types that are already started.
    251   requested_types_.Clear();
    252   associating_types_.Clear();
    253 }
    254 
    255 void ModelAssociationManager::Stop() {
    256   // Ignore callbacks from controllers.
    257   weak_ptr_factory_.InvalidateWeakPtrs();
    258 
    259   desired_types_.Clear();
    260   loaded_types_.Clear();
    261   associated_types_.Clear();
    262   associating_types_.Clear();
    263 
    264   // Stop started data types.
    265   for (DataTypeController::TypeMap::const_iterator it = controllers_->begin();
    266        it != controllers_->end(); ++it) {
    267     DataTypeController* dtc = (*it).second.get();
    268     if (dtc->state() != DataTypeController::NOT_RUNNING) {
    269       StopDatatype(syncer::SyncError(), dtc);
    270       DVLOG(1) << "ModelAssociationManager: Stopped " << dtc->name();
    271     }
    272   }
    273 
    274   if (state_ == CONFIGURING) {
    275     if (configure_status_ == DataTypeManager::OK)
    276       configure_status_ = DataTypeManager::ABORTED;
    277     DVLOG(1) << "ModelAssociationManager: Calling OnModelAssociationDone";
    278     ModelAssociationDone();
    279   }
    280 
    281   ResetForNextAssociation();
    282 
    283   state_ = IDLE;
    284 }
    285 
    286 void ModelAssociationManager::ModelLoadCallback(syncer::ModelType type,
    287                                                 syncer::SyncError error) {
    288   DVLOG(1) << "ModelAssociationManager: ModelLoadCallback for "
    289       << syncer::ModelTypeToString(type);
    290 
    291   if (error.IsSet()) {
    292     syncer::SyncMergeResult local_merge_result(type);
    293     local_merge_result.set_error(error);
    294     TypeStartCallback(type,
    295                       base::TimeTicks::Now(),
    296                       DataTypeController::ASSOCIATION_FAILED,
    297                       local_merge_result,
    298                       syncer::SyncMergeResult(type));
    299     return;
    300   }
    301 
    302   // This happens when slow loading type is disabled by new configuration.
    303   if (!desired_types_.Has(type))
    304     return;
    305 
    306   DCHECK(!loaded_types_.Has(type));
    307   loaded_types_.Put(type);
    308   if (associating_types_.Has(type)) {
    309     DataTypeController* dtc = controllers_->find(type)->second.get();
    310     dtc->StartAssociating(
    311         base::Bind(&ModelAssociationManager::TypeStartCallback,
    312                    weak_ptr_factory_.GetWeakPtr(),
    313                    type, base::TimeTicks::Now()));
    314   }
    315 }
    316 
    317 void ModelAssociationManager::TypeStartCallback(
    318     syncer::ModelType type,
    319     base::TimeTicks type_start_time,
    320     DataTypeController::ConfigureResult start_result,
    321     const syncer::SyncMergeResult& local_merge_result,
    322     const syncer::SyncMergeResult& syncer_merge_result) {
    323   if (desired_types_.Has(type) &&
    324       !DataTypeController::IsSuccessfulResult(start_result)) {
    325     DVLOG(1) << "ModelAssociationManager: Type encountered an error.";
    326     desired_types_.Remove(type);
    327     DataTypeController* dtc = controllers_->find(type)->second.get();
    328     StopDatatype(local_merge_result.error(), dtc);
    329 
    330     // Update configuration result.
    331     if (start_result == DataTypeController::UNRECOVERABLE_ERROR)
    332       configure_status_ = DataTypeManager::UNRECOVERABLE_ERROR;
    333   }
    334 
    335   // This happens when a slow associating type is disabled or if a type
    336   // disables itself after initial configuration.
    337   if (!desired_types_.Has(type)) {
    338       // It's possible all types failed to associate, in which case association
    339       // is complete.
    340       if (state_ == CONFIGURING && associating_types_.Empty())
    341         ModelAssociationDone();
    342       return;
    343   }
    344 
    345   DCHECK(!associated_types_.Has(type));
    346   DCHECK(DataTypeController::IsSuccessfulResult(start_result));
    347   associated_types_.Put(type);
    348 
    349   if (state_ != CONFIGURING)
    350     return;
    351 
    352   TRACE_EVENT_ASYNC_END1("sync", "ModelAssociation",
    353                          controllers_->find(type)->second.get(),
    354                          "DataType",
    355                          ModelTypeToString(type));
    356 
    357   // Track the merge results if we succeeded or an association failure
    358   // occurred.
    359   if (syncer::ProtocolTypes().Has(type)) {
    360     base::TimeDelta association_wait_time =
    361         std::max(base::TimeDelta(), type_start_time - association_start_time_);
    362     base::TimeDelta association_time =
    363         base::TimeTicks::Now() - type_start_time;;
    364     syncer::DataTypeAssociationStats stats =
    365         BuildAssociationStatsFromMergeResults(local_merge_result,
    366                                               syncer_merge_result,
    367                                               association_wait_time,
    368                                               association_time);
    369     delegate_->OnSingleDataTypeAssociationDone(type, stats);
    370   }
    371 
    372   associating_types_.Remove(type);
    373 
    374   if (associating_types_.Empty())
    375     ModelAssociationDone();
    376 }
    377 
    378 void ModelAssociationManager::ModelAssociationDone() {
    379   CHECK_EQ(CONFIGURING, state_);
    380 
    381   timer_.Stop();
    382 
    383   // Treat any unfinished types as having errors.
    384   desired_types_.RemoveAll(associating_types_);
    385   for (DataTypeController::TypeMap::const_iterator it = controllers_->begin();
    386        it != controllers_->end(); ++it) {
    387     DataTypeController* dtc = (*it).second.get();
    388     if (associating_types_.Has(dtc->type()) &&
    389         dtc->state() != DataTypeController::NOT_RUNNING) {
    390       UMA_HISTOGRAM_ENUMERATION("Sync.ConfigureFailed",
    391                                 ModelTypeToHistogramInt(dtc->type()),
    392                                 syncer::MODEL_TYPE_COUNT);
    393       StopDatatype(syncer::SyncError(FROM_HERE,
    394                                      syncer::SyncError::DATATYPE_ERROR,
    395                                      "Association timed out.",
    396                                      dtc->type()),
    397                    dtc);
    398     }
    399   }
    400 
    401   DataTypeManager::ConfigureResult result(configure_status_,
    402                                           requested_types_);
    403 
    404   // Reset state before notifying |delegate_| because that might
    405   // trigger a new round of configuration.
    406   ResetForNextAssociation();
    407   state_ = IDLE;
    408 
    409   delegate_->OnModelAssociationDone(result);
    410 }
    411 
    412 base::OneShotTimer<ModelAssociationManager>*
    413     ModelAssociationManager::GetTimerForTesting() {
    414   return &timer_;
    415 }
    416 
    417 }  // namespace sync_driver
    418