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