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.
5 #include "components/sync_driver/data_type_manager_impl.h"
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/callback.h"
13 #include "base/compiler_specific.h"
14 #include "base/debug/trace_event.h"
15 #include "base/logging.h"
16 #include "base/metrics/histogram.h"
17 #include "base/strings/stringprintf.h"
18 #include "components/sync_driver/data_type_controller.h"
19 #include "components/sync_driver/data_type_encryption_handler.h"
20 #include "components/sync_driver/data_type_manager_observer.h"
21 #include "components/sync_driver/failed_data_types_handler.h"
22 #include "sync/internal_api/public/data_type_debug_info_listener.h"
24 namespace sync_driver {
28 FailedDataTypesHandler::TypeErrorMap
29 GenerateCryptoErrorsForTypes(syncer::ModelTypeSet encrypted_types) {
30 FailedDataTypesHandler::TypeErrorMap crypto_errors;
31 for (syncer::ModelTypeSet::Iterator iter = encrypted_types.First();
32 iter.Good(); iter.Inc()) {
33 crypto_errors[iter.Get()] = syncer::SyncError(
35 syncer::SyncError::CRYPTO_ERROR,
44 DataTypeManagerImpl::AssociationTypesInfo::AssociationTypesInfo() {}
45 DataTypeManagerImpl::AssociationTypesInfo::~AssociationTypesInfo() {}
47 DataTypeManagerImpl::DataTypeManagerImpl(
48 const base::Closure& unrecoverable_error_method,
49 const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&
51 const DataTypeController::TypeMap* controllers,
52 const DataTypeEncryptionHandler* encryption_handler,
53 BackendDataTypeConfigurer* configurer,
54 DataTypeManagerObserver* observer,
55 FailedDataTypesHandler* failed_data_types_handler)
56 : configurer_(configurer),
57 controllers_(controllers),
58 state_(DataTypeManager::STOPPED),
59 needs_reconfigure_(false),
60 last_configure_reason_(syncer::CONFIGURE_REASON_UNKNOWN),
61 debug_info_listener_(debug_info_listener),
62 model_association_manager_(controllers, this),
64 failed_data_types_handler_(failed_data_types_handler),
65 encryption_handler_(encryption_handler),
66 unrecoverable_error_method_(unrecoverable_error_method),
67 weak_ptr_factory_(this) {
68 DCHECK(failed_data_types_handler_);
73 DataTypeManagerImpl::~DataTypeManagerImpl() {}
75 void DataTypeManagerImpl::Configure(syncer::ModelTypeSet desired_types,
76 syncer::ConfigureReason reason) {
77 if (reason == syncer::CONFIGURE_REASON_BACKUP_ROLLBACK)
78 desired_types.PutAll(syncer::ControlTypes());
80 desired_types.PutAll(syncer::CoreTypes());
82 // Only allow control types and types that have controllers.
83 syncer::ModelTypeSet filtered_desired_types;
84 for (syncer::ModelTypeSet::Iterator type = desired_types.First();
85 type.Good(); type.Inc()) {
86 DataTypeController::TypeMap::const_iterator iter =
87 controllers_->find(type.Get());
88 if (syncer::IsControlType(type.Get()) ||
89 iter != controllers_->end()) {
90 if (iter != controllers_->end()) {
91 if (!iter->second->ReadyForStart() &&
92 !failed_data_types_handler_->GetUnreadyErrorTypes().Has(
94 // Add the type to the unready types set to prevent purging it. It's
95 // up to the datatype controller to, if necessary, explicitly
96 // mark the type as broken to trigger a purge.
97 syncer::SyncError error(FROM_HERE,
98 syncer::SyncError::UNREADY_ERROR,
99 "Datatype not ready at config time.",
101 std::map<syncer::ModelType, syncer::SyncError> errors;
102 errors[type.Get()] = error;
103 failed_data_types_handler_->UpdateFailedDataTypes(errors);
104 } else if (iter->second->ReadyForStart()) {
105 failed_data_types_handler_->ResetUnreadyErrorFor(type.Get());
108 filtered_desired_types.Put(type.Get());
111 ConfigureImpl(filtered_desired_types, reason);
114 void DataTypeManagerImpl::PurgeForMigration(
115 syncer::ModelTypeSet undesired_types,
116 syncer::ConfigureReason reason) {
117 syncer::ModelTypeSet remainder = Difference(last_requested_types_,
119 ConfigureImpl(remainder, reason);
122 void DataTypeManagerImpl::ConfigureImpl(
123 syncer::ModelTypeSet desired_types,
124 syncer::ConfigureReason reason) {
125 DCHECK_NE(reason, syncer::CONFIGURE_REASON_UNKNOWN);
126 DVLOG(1) << "Configuring for " << syncer::ModelTypeSetToString(desired_types)
127 << " with reason " << reason;
128 if (state_ == STOPPING) {
129 // You can not set a configuration while stopping.
130 LOG(ERROR) << "Configuration set while stopping.";
134 // TODO(zea): consider not performing a full configuration once there's a
135 // reliable way to determine if the requested set of enabled types matches the
138 last_requested_types_ = desired_types;
139 last_configure_reason_ = reason;
140 // Only proceed if we're in a steady state or retrying.
141 if (state_ != STOPPED && state_ != CONFIGURED && state_ != RETRYING) {
142 DVLOG(1) << "Received configure request while configuration in flight. "
143 << "Postponing until current configuration complete.";
144 needs_reconfigure_ = true;
151 BackendDataTypeConfigurer::DataTypeConfigStateMap
152 DataTypeManagerImpl::BuildDataTypeConfigStateMap(
153 const syncer::ModelTypeSet& types_being_configured) const {
154 // 1. Get the failed types (due to fatal, crypto, and unready errors).
155 // 2. Add the difference between last_requested_types_ and the failed types
156 // as CONFIGURE_INACTIVE.
157 // 3. Flip |types_being_configured| to CONFIGURE_ACTIVE.
158 // 4. Set non-enabled user types as DISABLED.
159 // 5. Set the fatal, crypto, and unready types to their respective states.
160 syncer::ModelTypeSet error_types =
161 failed_data_types_handler_->GetFailedTypes();
162 syncer::ModelTypeSet fatal_types =
163 failed_data_types_handler_->GetFatalErrorTypes();
164 syncer::ModelTypeSet crypto_types =
165 failed_data_types_handler_->GetCryptoErrorTypes();
166 syncer::ModelTypeSet unready_types=
167 failed_data_types_handler_->GetUnreadyErrorTypes();
169 // Types with persistence errors are only purged/resynced when they're
170 // actively being configured.
171 syncer::ModelTypeSet persistence_types =
172 failed_data_types_handler_->GetPersistenceErrorTypes();
173 persistence_types.RetainAll(types_being_configured);
175 // Types with unready errors do not count as unready if they've been disabled.
176 unready_types.RetainAll(last_requested_types_);
178 syncer::ModelTypeSet enabled_types = last_requested_types_;
179 enabled_types.RemoveAll(error_types);
180 syncer::ModelTypeSet disabled_types =
182 syncer::Union(syncer::UserTypes(), syncer::ControlTypes()),
184 syncer::ModelTypeSet to_configure = syncer::Intersection(
185 enabled_types, types_being_configured);
186 DVLOG(1) << "Enabling: " << syncer::ModelTypeSetToString(enabled_types);
187 DVLOG(1) << "Configuring: " << syncer::ModelTypeSetToString(to_configure);
188 DVLOG(1) << "Disabling: " << syncer::ModelTypeSetToString(disabled_types);
190 BackendDataTypeConfigurer::DataTypeConfigStateMap config_state_map;
191 BackendDataTypeConfigurer::SetDataTypesState(
192 BackendDataTypeConfigurer::CONFIGURE_INACTIVE, enabled_types,
194 BackendDataTypeConfigurer::SetDataTypesState(
195 BackendDataTypeConfigurer::CONFIGURE_ACTIVE, to_configure,
197 BackendDataTypeConfigurer::SetDataTypesState(
198 BackendDataTypeConfigurer::CONFIGURE_CLEAN, persistence_types,
200 BackendDataTypeConfigurer::SetDataTypesState(
201 BackendDataTypeConfigurer::DISABLED, disabled_types,
203 BackendDataTypeConfigurer::SetDataTypesState(
204 BackendDataTypeConfigurer::FATAL, fatal_types,
206 BackendDataTypeConfigurer::SetDataTypesState(
207 BackendDataTypeConfigurer::CRYPTO, crypto_types,
209 BackendDataTypeConfigurer::SetDataTypesState(
210 BackendDataTypeConfigurer::UNREADY, unready_types,
212 return config_state_map;
215 void DataTypeManagerImpl::Restart(syncer::ConfigureReason reason) {
216 DVLOG(1) << "Restarting...";
218 // Check for new or resolved data type crypto errors.
219 if (encryption_handler_->IsPassphraseRequired()) {
220 syncer::ModelTypeSet encrypted_types =
221 encryption_handler_->GetEncryptedDataTypes();
222 encrypted_types.RetainAll(last_requested_types_);
223 encrypted_types.RemoveAll(
224 failed_data_types_handler_->GetCryptoErrorTypes());
225 FailedDataTypesHandler::TypeErrorMap crypto_errors =
226 GenerateCryptoErrorsForTypes(encrypted_types);
227 failed_data_types_handler_->UpdateFailedDataTypes(crypto_errors);
229 failed_data_types_handler_->ResetCryptoErrors();
232 syncer::ModelTypeSet failed_types =
233 failed_data_types_handler_->GetFailedTypes();
234 syncer::ModelTypeSet enabled_types =
235 syncer::Difference(last_requested_types_, failed_types);
237 last_restart_time_ = base::Time::Now();
238 configuration_stats_.clear();
240 DCHECK(state_ == STOPPED || state_ == CONFIGURED || state_ == RETRYING);
242 // Starting from a "steady state" (stopped or configured) state
243 // should send a start notification.
244 if (state_ == STOPPED || state_ == CONFIGURED)
247 model_association_manager_.Initialize(enabled_types);
249 download_types_queue_ = PrioritizeTypes(enabled_types);
250 association_types_queue_ = std::queue<AssociationTypesInfo>();
252 // Tell the backend about the new set of data types we wish to sync.
253 // The task will be invoked when updates are downloaded.
254 state_ = DOWNLOAD_PENDING;
255 configurer_->ConfigureDataTypes(
257 BuildDataTypeConfigStateMap(download_types_queue_.front()),
258 base::Bind(&DataTypeManagerImpl::DownloadReady,
259 weak_ptr_factory_.GetWeakPtr(),
261 download_types_queue_.front(),
262 syncer::ModelTypeSet()),
263 base::Bind(&DataTypeManagerImpl::OnDownloadRetry,
264 weak_ptr_factory_.GetWeakPtr()));
267 syncer::ModelTypeSet DataTypeManagerImpl::GetPriorityTypes() const {
268 syncer::ModelTypeSet high_priority_types;
269 high_priority_types.PutAll(syncer::PriorityCoreTypes());
270 high_priority_types.PutAll(syncer::PriorityUserTypes());
271 return high_priority_types;
274 TypeSetPriorityList DataTypeManagerImpl::PrioritizeTypes(
275 const syncer::ModelTypeSet& types) {
276 syncer::ModelTypeSet high_priority_types = GetPriorityTypes();
277 high_priority_types.RetainAll(types);
279 syncer::ModelTypeSet low_priority_types =
280 syncer::Difference(types, high_priority_types);
282 TypeSetPriorityList result;
283 if (!high_priority_types.Empty())
284 result.push(high_priority_types);
285 if (!low_priority_types.Empty())
286 result.push(low_priority_types);
288 // Could be empty in case of purging for migration, sync nothing, etc.
289 // Configure empty set to purge data from backend.
291 result.push(syncer::ModelTypeSet());
296 void DataTypeManagerImpl::ProcessReconfigure() {
297 DCHECK(needs_reconfigure_);
299 // Wait for current download and association to finish.
300 if (!(download_types_queue_.empty() && association_types_queue_.empty()))
303 // An attempt was made to reconfigure while we were already configuring.
304 // This can be because a passphrase was accepted or the user changed the
305 // set of desired types. Either way, |last_requested_types_| will contain
306 // the most recent set of desired types, so we just call configure.
307 // Note: we do this whether or not GetControllersNeedingStart is true,
308 // because we may need to stop datatypes.
309 DVLOG(1) << "Reconfiguring due to previous configure attempt occuring while"
312 // Note: ConfigureImpl is called directly, rather than posted, in order to
313 // ensure that any purging/unapplying/journaling happens while the set of
314 // failed types is still up to date. If stack unwinding were to be done
315 // via PostTask, the failed data types may be reset before the purging was
318 needs_reconfigure_ = false;
319 ConfigureImpl(last_requested_types_, last_configure_reason_);
322 void DataTypeManagerImpl::OnDownloadRetry() {
323 DCHECK(state_ == DOWNLOAD_PENDING || state_ == CONFIGURING);
324 observer_->OnConfigureRetry();
327 void DataTypeManagerImpl::DownloadReady(
328 base::Time download_start_time,
329 syncer::ModelTypeSet types_to_download,
330 syncer::ModelTypeSet high_priority_types_before,
331 syncer::ModelTypeSet first_sync_types,
332 syncer::ModelTypeSet failed_configuration_types) {
333 DCHECK(state_ == DOWNLOAD_PENDING || state_ == CONFIGURING);
335 // Persistence errors are reset after each backend configuration attempt
336 // during which they would have been purged.
337 failed_data_types_handler_->ResetPersistenceErrorsFrom(types_to_download);
339 // Ignore |failed_configuration_types| if we need to reconfigure
341 if (needs_reconfigure_) {
342 download_types_queue_ = TypeSetPriorityList();
343 ProcessReconfigure();
347 if (!failed_configuration_types.Empty()) {
348 if (!unrecoverable_error_method_.is_null())
349 unrecoverable_error_method_.Run();
350 FailedDataTypesHandler::TypeErrorMap errors;
351 for (syncer::ModelTypeSet::Iterator iter =
352 failed_configuration_types.First(); iter.Good(); iter.Inc()) {
353 syncer::SyncError error(
355 syncer::SyncError::UNRECOVERABLE_ERROR,
356 "Backend failed to download type.",
358 errors[iter.Get()] = error;
360 failed_data_types_handler_->UpdateFailedDataTypes(errors);
361 Abort(UNRECOVERABLE_ERROR);
365 state_ = CONFIGURING;
367 // Pop and associate download-ready types.
368 syncer::ModelTypeSet ready_types = types_to_download;
369 download_types_queue_.pop();
370 syncer::ModelTypeSet new_types_to_download;
371 if (!download_types_queue_.empty())
372 new_types_to_download = download_types_queue_.front();
374 AssociationTypesInfo association_info;
375 association_info.types = ready_types;
376 association_info.first_sync_types = first_sync_types;
377 association_info.download_start_time = download_start_time;
378 association_info.download_ready_time = base::Time::Now();
379 association_info.high_priority_types_before = high_priority_types_before;
380 association_types_queue_.push(association_info);
381 if (association_types_queue_.size() == 1u)
382 StartNextAssociation();
384 // Download types of low priority while configuring types of high priority.
385 if (!new_types_to_download.Empty()) {
386 configurer_->ConfigureDataTypes(
387 last_configure_reason_,
388 BuildDataTypeConfigStateMap(new_types_to_download),
389 base::Bind(&DataTypeManagerImpl::DownloadReady,
390 weak_ptr_factory_.GetWeakPtr(),
392 new_types_to_download,
393 syncer::Union(ready_types, high_priority_types_before)),
394 base::Bind(&DataTypeManagerImpl::OnDownloadRetry,
395 weak_ptr_factory_.GetWeakPtr()));
399 void DataTypeManagerImpl::StartNextAssociation() {
400 CHECK(!association_types_queue_.empty());
402 association_types_queue_.front().association_request_time =
404 model_association_manager_.StartAssociationAsync(
405 association_types_queue_.front().types);
408 void DataTypeManagerImpl::OnSingleDataTypeWillStop(
409 syncer::ModelType type,
410 const syncer::SyncError& error) {
411 configurer_->DeactivateDataType(type);
413 FailedDataTypesHandler::TypeErrorMap failed_types;
414 failed_types[type] = error;
415 failed_data_types_handler_->UpdateFailedDataTypes(
418 // Unrecoverable errors will shut down the entire backend, so no need to
420 if (error.error_type() != syncer::SyncError::UNRECOVERABLE_ERROR) {
421 needs_reconfigure_ = true;
422 ProcessReconfigure();
424 DCHECK_EQ(state_, CONFIGURING);
429 void DataTypeManagerImpl::OnSingleDataTypeAssociationDone(
430 syncer::ModelType type,
431 const syncer::DataTypeAssociationStats& association_stats) {
432 DCHECK(!association_types_queue_.empty());
433 DataTypeController::TypeMap::const_iterator c_it = controllers_->find(type);
434 DCHECK(c_it != controllers_->end());
435 if (c_it->second->state() == DataTypeController::RUNNING) {
436 // Tell the backend about the change processor for this type so it can
437 // begin routing changes to it.
438 configurer_->ActivateDataType(type, c_it->second->model_safe_group(),
439 c_it->second->GetChangeProcessor());
442 if (!debug_info_listener_.IsInitialized())
445 AssociationTypesInfo& info = association_types_queue_.front();
446 configuration_stats_.push_back(syncer::DataTypeConfigurationStats());
447 configuration_stats_.back().model_type = type;
448 configuration_stats_.back().association_stats = association_stats;
449 if (info.types.Has(type)) {
450 // Times in |info| only apply to non-slow types.
451 configuration_stats_.back().download_wait_time =
452 info.download_start_time - last_restart_time_;
453 if (info.first_sync_types.Has(type)) {
454 configuration_stats_.back().download_time =
455 info.download_ready_time - info.download_start_time;
457 configuration_stats_.back().association_wait_time_for_high_priority =
458 info.association_request_time - info.download_ready_time;
459 configuration_stats_.back().high_priority_types_configured_before =
460 info.high_priority_types_before;
461 configuration_stats_.back().same_priority_types_configured_before =
462 info.configured_types;
463 info.configured_types.Put(type);
467 void DataTypeManagerImpl::OnModelAssociationDone(
468 const DataTypeManager::ConfigureResult& result) {
469 DCHECK(state_ == STOPPING || state_ == CONFIGURING);
471 if (state_ == STOPPING)
474 // Ignore abort/unrecoverable error if we need to reconfigure anyways.
475 if (needs_reconfigure_) {
476 association_types_queue_ = std::queue<AssociationTypesInfo>();
477 ProcessReconfigure();
481 if (result.status == ABORTED || result.status == UNRECOVERABLE_ERROR) {
482 Abort(result.status);
486 DCHECK(result.status == OK);
488 association_types_queue_.pop();
489 if (!association_types_queue_.empty()) {
490 StartNextAssociation();
491 } else if (download_types_queue_.empty()) {
497 void DataTypeManagerImpl::Stop() {
498 if (state_ == STOPPED)
501 bool need_to_notify =
502 state_ == DOWNLOAD_PENDING || state_ == CONFIGURING;
505 if (need_to_notify) {
506 ConfigureResult result(ABORTED,
507 last_requested_types_);
512 void DataTypeManagerImpl::Abort(ConfigureStatus status) {
513 DCHECK(state_ == DOWNLOAD_PENDING || state_ == CONFIGURING);
517 DCHECK_NE(OK, status);
518 ConfigureResult result(status,
519 last_requested_types_);
523 void DataTypeManagerImpl::StopImpl() {
526 // Invalidate weak pointer to drop download callbacks.
527 weak_ptr_factory_.InvalidateWeakPtrs();
529 // Stop all data types. This may trigger association callback but the
530 // callback will do nothing because state is set to STOPPING above.
531 model_association_manager_.Stop();
536 void DataTypeManagerImpl::NotifyStart() {
537 observer_->OnConfigureStart();
540 void DataTypeManagerImpl::NotifyDone(const ConfigureResult& result) {
541 AddToConfigureTime();
543 DVLOG(1) << "Total time spent configuring: "
544 << configure_time_delta_.InSecondsF() << "s";
545 switch (result.status) {
546 case DataTypeManager::OK:
547 DVLOG(1) << "NotifyDone called with result: OK";
548 UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.OK",
549 configure_time_delta_);
550 if (debug_info_listener_.IsInitialized() &&
551 !configuration_stats_.empty()) {
552 debug_info_listener_.Call(
554 &syncer::DataTypeDebugInfoListener::OnDataTypeConfigureComplete,
555 configuration_stats_);
557 configuration_stats_.clear();
559 case DataTypeManager::ABORTED:
560 DVLOG(1) << "NotifyDone called with result: ABORTED";
561 UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.ABORTED",
562 configure_time_delta_);
564 case DataTypeManager::UNRECOVERABLE_ERROR:
565 DVLOG(1) << "NotifyDone called with result: UNRECOVERABLE_ERROR";
566 UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.UNRECOVERABLE_ERROR",
567 configure_time_delta_);
569 case DataTypeManager::UNKNOWN:
573 observer_->OnConfigureDone(result);
576 DataTypeManager::State DataTypeManagerImpl::state() const {
580 void DataTypeManagerImpl::AddToConfigureTime() {
581 DCHECK(!last_restart_time_.is_null());
582 configure_time_delta_ += (base::Time::Now() - last_restart_time_);
585 } // namespace sync_driver