model_association_manager.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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 16using syncer::ModelTypeSet; 17 18namespace sync_driver { 19 20namespace { 21 22static 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 62COMPILE_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. 69const int64 kAssociationTimeOutInSeconds = 600; 70 71syncer::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 111ModelAssociationManager::ModelAssociationManager( 112 const DataTypeController::TypeMap* controllers, 113 ModelAssociationManagerDelegate* processor) 114 : state_(IDLE), 115 controllers_(controllers), 116 delegate_(processor), 117 weak_ptr_factory_(this), 118 configure_status_(DataTypeManager::UNKNOWN) { 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 126ModelAssociationManager::~ModelAssociationManager() { 127} 128 129void 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 152void 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 166void 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 179void 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 197void 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 247void 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 255void 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 286void ModelAssociationManager::ModelLoadCallback(syncer::ModelType type, 287 syncer::SyncError error) { 288 DVLOG(1) << "ModelAssociationManager: ModelLoadCallback for " 289 << syncer::ModelTypeToString(type); 290 291 // This happens when slow loading type is disabled by new configuration. 292 if (!desired_types_.Has(type)) 293 return; 294 295 DCHECK(!loaded_types_.Has(type)); 296 if (error.IsSet()) { 297 syncer::SyncMergeResult local_merge_result(type); 298 local_merge_result.set_error(error); 299 TypeStartCallback(type, 300 base::TimeTicks::Now(), 301 DataTypeController::ASSOCIATION_FAILED, 302 local_merge_result, 303 syncer::SyncMergeResult(type)); 304 return; 305 } 306 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 317void 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 378void 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 412base::OneShotTimer<ModelAssociationManager>* 413 ModelAssociationManager::GetTimerForTesting() { 414 return &timer_; 415} 416 417} // namespace sync_driver 418