Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / sync / glue / model_association_manager.cc
blob6c75c53eca851b301f439e6cc20adbb34cf8e772
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.
5 #include "chrome/browser/sync/glue/model_association_manager.h"
7 #include <algorithm>
8 #include <functional>
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"
16 using content::BrowserThread;
17 using syncer::ModelTypeSet;
19 namespace browser_sync {
21 namespace {
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.
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,
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::MANAGED_USER_SHARED_SETTINGS,
59 syncer::ARTICLES,
62 COMPILE_ASSERT(arraysize(kStartOrder) ==
63 syncer::MODEL_TYPE_COUNT - syncer::FIRST_REAL_MODEL_TYPE,
64 kStartOrder_IncorrectSize);
66 // The amount of time we wait for association to finish. If some types haven't
67 // finished association by the time, configuration result will be
68 // PARTIAL_SUCCESS and DataTypeManager is notified of the unfinished types.
69 const int64 kAssociationTimeOutInSeconds = 600;
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;
109 } // namespace
111 ModelAssociationManager::ModelAssociationManager(
112 const DataTypeController::TypeMap* controllers,
113 ModelAssociationResultProcessor* processor)
114 : state_(IDLE),
115 controllers_(controllers),
116 result_processor_(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());
126 ModelAssociationManager::~ModelAssociationManager() {
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_);
135 // Only keep types that have controllers.
136 desired_types_.Clear();
137 slow_types_.Clear();
138 for (syncer::ModelTypeSet::Iterator it = desired_types.First();
139 it.Good(); it.Inc()) {
140 if (controllers_->find(it.Get()) != controllers_->end())
141 desired_types_.Put(it.Get());
144 DVLOG(1) << "ModelAssociationManager: Initializing for "
145 << syncer::ModelTypeSetToString(desired_types_);
147 state_ = INITIALIZED_TO_CONFIGURE;
149 StopDisabledTypes();
150 LoadEnabledTypes();
153 void ModelAssociationManager::StopDisabledTypes() {
154 DVLOG(1) << "ModelAssociationManager: Stopping disabled types.";
155 for (DataTypeController::TypeMap::const_iterator it = controllers_->begin();
156 it != controllers_->end(); ++it) {
157 DataTypeController* dtc = (*it).second.get();
158 if (dtc->state() != DataTypeController::NOT_RUNNING &&
159 (!desired_types_.Has(dtc->type()) ||
160 failed_data_types_info_.count(dtc->type()) > 0)) {
161 DVLOG(1) << "ModelTypeToString: stop " << dtc->name();
162 dtc->Stop();
164 loaded_types_.Remove(dtc->type());
165 associated_types_.Remove(dtc->type());
170 void ModelAssociationManager::LoadEnabledTypes() {
171 // Load in kStartOrder.
172 for (size_t i = 0; i < arraysize(kStartOrder); i++) {
173 syncer::ModelType type = kStartOrder[i];
174 if (!desired_types_.Has(type))
175 continue;
177 DCHECK(controllers_->find(type) != controllers_->end());
178 DataTypeController* dtc = controllers_->find(type)->second.get();
179 if (dtc->state() == DataTypeController::NOT_RUNNING) {
180 DCHECK(!loaded_types_.Has(dtc->type()));
181 DCHECK(!associated_types_.Has(dtc->type()));
182 dtc->LoadModels(base::Bind(&ModelAssociationManager::ModelLoadCallback,
183 weak_ptr_factory_.GetWeakPtr()));
188 void ModelAssociationManager::StartAssociationAsync(
189 const syncer::ModelTypeSet& types_to_associate) {
190 DCHECK_NE(CONFIGURING, state_);
191 state_ = CONFIGURING;
193 association_start_time_ = base::TimeTicks::Now();
195 requested_types_ = types_to_associate;
197 associating_types_ = types_to_associate;
198 associating_types_.RetainAll(desired_types_);
199 associating_types_.RemoveAll(associated_types_);
201 // Assume success.
202 configure_status_ = DataTypeManager::OK;
204 // Remove types that already failed.
205 for (std::map<syncer::ModelType, syncer::SyncError>::const_iterator it =
206 failed_data_types_info_.begin();
207 it != failed_data_types_info_.end(); ++it) {
208 associating_types_.Remove(it->first);
211 // Done if no types to associate.
212 if (associating_types_.Empty()) {
213 ModelAssociationDone();
214 return;
217 timer_.Start(FROM_HERE,
218 base::TimeDelta::FromSeconds(kAssociationTimeOutInSeconds),
219 this,
220 &ModelAssociationManager::ModelAssociationDone);
222 // Start association of types that are loaded in specified order.
223 for (size_t i = 0; i < arraysize(kStartOrder); i++) {
224 syncer::ModelType type = kStartOrder[i];
225 if (!associating_types_.Has(type) || !loaded_types_.Has(type))
226 continue;
228 DataTypeController* dtc = controllers_->find(type)->second.get();
229 DCHECK(DataTypeController::MODEL_LOADED == dtc->state() ||
230 DataTypeController::ASSOCIATING == dtc->state());
231 if (dtc->state() == DataTypeController::MODEL_LOADED) {
232 TRACE_EVENT_ASYNC_BEGIN1("sync", "ModelAssociation",
233 dtc,
234 "DataType",
235 ModelTypeToString(type));
237 dtc->StartAssociating(
238 base::Bind(&ModelAssociationManager::TypeStartCallback,
239 weak_ptr_factory_.GetWeakPtr(),
240 type, base::TimeTicks::Now()));
245 void ModelAssociationManager::ResetForNextAssociation() {
246 DVLOG(1) << "ModelAssociationManager: Reseting for next configuration";
247 // |loaded_types_| and |associated_types_| are not cleared. So
248 // reconfiguration won't restart types that are already started.
249 requested_types_.Clear();
250 failed_data_types_info_.clear();
251 associating_types_.Clear();
252 needs_crypto_types_.Clear();
255 void ModelAssociationManager::Stop() {
256 // Ignore callbacks from controllers.
257 weak_ptr_factory_.InvalidateWeakPtrs();
259 // Stop started data types.
260 for (DataTypeController::TypeMap::const_iterator it = controllers_->begin();
261 it != controllers_->end(); ++it) {
262 DataTypeController* dtc = (*it).second.get();
263 if (dtc->state() != DataTypeController::NOT_RUNNING) {
264 dtc->Stop();
265 DVLOG(1) << "ModelAssociationManager: Stopped " << dtc->name();
269 desired_types_.Clear();
270 loaded_types_.Clear();
271 associated_types_.Clear();
272 slow_types_.Clear();
274 if (state_ == CONFIGURING) {
275 if (configure_status_ == DataTypeManager::OK)
276 configure_status_ = DataTypeManager::ABORTED;
277 DVLOG(1) << "ModelAssociationManager: Calling OnModelAssociationDone";
278 ModelAssociationDone();
281 ResetForNextAssociation();
283 state_ = IDLE;
286 void ModelAssociationManager::AppendToFailedDatatypesAndLogError(
287 const syncer::SyncError& error) {
288 failed_data_types_info_[error.model_type()] = error;
289 LOG(ERROR) << "Failed to associate models for "
290 << syncer::ModelTypeToString(error.model_type());
291 UMA_HISTOGRAM_ENUMERATION("Sync.ConfigureFailed",
292 ModelTypeToHistogramInt(error.model_type()),
293 syncer::MODEL_TYPE_COUNT);
296 void ModelAssociationManager::ModelLoadCallback(syncer::ModelType type,
297 syncer::SyncError error) {
298 DVLOG(1) << "ModelAssociationManager: ModelLoadCallback for "
299 << syncer::ModelTypeToString(type);
301 // This happens when slow loading type is disabled by new configuration.
302 if (!desired_types_.Has(type))
303 return;
305 DCHECK(!loaded_types_.Has(type));
306 if (error.IsSet()) {
307 syncer::SyncMergeResult local_merge_result(type);
308 local_merge_result.set_error(error);
309 TypeStartCallback(type,
310 base::TimeTicks::Now(),
311 DataTypeController::ASSOCIATION_FAILED,
312 local_merge_result,
313 syncer::SyncMergeResult(type));
314 return;
317 loaded_types_.Put(type);
318 if (associating_types_.Has(type) || slow_types_.Has(type)) {
319 DataTypeController* dtc = controllers_->find(type)->second.get();
320 dtc->StartAssociating(
321 base::Bind(&ModelAssociationManager::TypeStartCallback,
322 weak_ptr_factory_.GetWeakPtr(),
323 type, base::TimeTicks::Now()));
327 void ModelAssociationManager::TypeStartCallback(
328 syncer::ModelType type,
329 base::TimeTicks type_start_time,
330 DataTypeController::StartResult start_result,
331 const syncer::SyncMergeResult& local_merge_result,
332 const syncer::SyncMergeResult& syncer_merge_result) {
333 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
335 // This happens when slow associating type is disabled by new configuration.
336 if (!desired_types_.Has(type))
337 return;
339 slow_types_.Remove(type);
341 DCHECK(!associated_types_.Has(type));
342 if (DataTypeController::IsSuccessfulResult(start_result)) {
343 associated_types_.Put(type);
344 } else if (state_ == IDLE) {
345 // For type that failed in IDLE mode, simply stop the controller. Next
346 // configuration will try to restart from scratch if the type is still
347 // enabled.
348 DataTypeController* dtc = controllers_->find(type)->second.get();
349 if (dtc->state() != DataTypeController::NOT_RUNNING)
350 dtc->Stop();
351 loaded_types_.Remove(type);
352 } else {
353 // Record error in CONFIGURING or INITIALIZED_TO_CONFIGURE mode. The error
354 // will be reported when data types association finishes.
355 if (start_result == DataTypeController::NEEDS_CRYPTO) {
356 DVLOG(1) << "ModelAssociationManager: Encountered an undecryptable type";
357 needs_crypto_types_.Put(type);
358 } else {
359 DVLOG(1) << "ModelAssociationManager: Encountered a failed type";
360 AppendToFailedDatatypesAndLogError(local_merge_result.error());
364 if (state_ != CONFIGURING)
365 return;
367 TRACE_EVENT_ASYNC_END1("sync", "ModelAssociation",
368 controllers_->find(type)->second.get(),
369 "DataType",
370 ModelTypeToString(type));
372 // Track the merge results if we succeeded or an association failure
373 // occurred.
374 if ((DataTypeController::IsSuccessfulResult(start_result) ||
375 start_result == DataTypeController::ASSOCIATION_FAILED) &&
376 syncer::ProtocolTypes().Has(type)) {
377 base::TimeDelta association_wait_time =
378 std::max(base::TimeDelta(), type_start_time - association_start_time_);
379 base::TimeDelta association_time =
380 base::TimeTicks::Now() - type_start_time;;
381 syncer::DataTypeAssociationStats stats =
382 BuildAssociationStatsFromMergeResults(local_merge_result,
383 syncer_merge_result,
384 association_wait_time,
385 association_time);
386 result_processor_->OnSingleDataTypeAssociationDone(type, stats);
389 // Update configuration result.
390 if (configure_status_ == DataTypeManager::OK &&
391 start_result == DataTypeController::ASSOCIATION_FAILED) {
392 configure_status_ = DataTypeManager::PARTIAL_SUCCESS;
394 if (start_result == DataTypeController::UNRECOVERABLE_ERROR)
395 configure_status_ = DataTypeManager::UNRECOVERABLE_ERROR;
397 associating_types_.Remove(type);
399 if (associating_types_.Empty())
400 ModelAssociationDone();
403 void ModelAssociationManager::ModelAssociationDone() {
404 CHECK_EQ(CONFIGURING, state_);
406 timer_.Stop();
408 // Stop controllers of failed types.
409 StopDisabledTypes();
411 if (configure_status_ == DataTypeManager::OK &&
412 (!associating_types_.Empty() || !failed_data_types_info_.empty() ||
413 !needs_crypto_types_.Empty())) {
414 // We have not configured all types that we have been asked to configure.
415 // Either we have failed types or types that have not completed loading
416 // yet.
417 DVLOG(1) << "ModelAssociationManager: setting partial success";
418 configure_status_ = DataTypeManager::PARTIAL_SUCCESS;
421 slow_types_.PutAll(associating_types_);
423 DataTypeManager::ConfigureResult result(configure_status_,
424 requested_types_,
425 failed_data_types_info_,
426 associating_types_,
427 needs_crypto_types_);
429 // Reset state before notifying |result_processor_| because that might
430 // trigger a new round of configuration.
431 ResetForNextAssociation();
432 state_ = IDLE;
434 result_processor_->OnModelAssociationDone(result);
437 base::OneShotTimer<ModelAssociationManager>*
438 ModelAssociationManager::GetTimerForTesting() {
439 return &timer_;
442 } // namespace browser_sync