| OLD | NEW | 
|    1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |    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 |    2 // Use of this source code is governed by a BSD-style license that can be | 
|    3 // found in the LICENSE file. |    3 // found in the LICENSE file. | 
|    4  |    4  | 
|    5 #include "sync/engine/update_applicator.h" |    5 #include "sync/engine/update_applicator.h" | 
|    6  |    6  | 
|    7 #include <vector> |    7 #include <vector> | 
|    8  |    8  | 
|    9 #include "base/logging.h" |    9 #include "base/logging.h" | 
|   10 #include "sync/engine/syncer_util.h" |   10 #include "sync/engine/syncer_util.h" | 
|   11 #include "sync/sessions/session_state.h" |  | 
|   12 #include "sync/syncable/entry.h" |   11 #include "sync/syncable/entry.h" | 
|   13 #include "sync/syncable/mutable_entry.h" |   12 #include "sync/syncable/mutable_entry.h" | 
|   14 #include "sync/syncable/syncable_id.h" |   13 #include "sync/syncable/syncable_id.h" | 
|   15 #include "sync/syncable/write_transaction.h" |   14 #include "sync/syncable/write_transaction.h" | 
|   16  |   15  | 
|   17 using std::vector; |   16 using std::vector; | 
|   18  |   17  | 
|   19 namespace syncer { |   18 namespace syncer { | 
|   20  |   19  | 
|   21 UpdateApplicator::UpdateApplicator(ConflictResolver* resolver, |   20 using syncable::ID; | 
|   22                                    Cryptographer* cryptographer, |   21  | 
|   23                                    const UpdateIterator& begin, |   22 UpdateApplicator::UpdateApplicator(Cryptographer* cryptographer, | 
|   24                                    const UpdateIterator& end, |  | 
|   25                                    const ModelSafeRoutingInfo& routes, |   23                                    const ModelSafeRoutingInfo& routes, | 
|   26                                    ModelSafeGroup group_filter) |   24                                    ModelSafeGroup group_filter) | 
|   27     : resolver_(resolver), |   25     : cryptographer_(cryptographer), | 
|   28       cryptographer_(cryptographer), |  | 
|   29       begin_(begin), |  | 
|   30       end_(end), |  | 
|   31       pointer_(begin), |  | 
|   32       group_filter_(group_filter), |   26       group_filter_(group_filter), | 
|   33       progress_(false), |   27       routing_info_(routes) { | 
|   34       routing_info_(routes), |  | 
|   35       application_results_(end - begin) { |  | 
|   36   size_t item_count = end - begin; |  | 
|   37   DVLOG(1) << "UpdateApplicator created for " << item_count << " items."; |  | 
|   38 } |   28 } | 
|   39  |   29  | 
|   40 UpdateApplicator::~UpdateApplicator() { |   30 UpdateApplicator::~UpdateApplicator() { | 
|   41 } |   31 } | 
|   42  |   32  | 
|   43 // Returns true if there's more to do. |   33 // Attempt to apply all updates, using multiple passes if necessary. | 
|   44 bool UpdateApplicator::AttemptOneApplication( |   34 // | 
|   45     syncable::WriteTransaction* trans) { |   35 // Some updates must be applied in order.  For example, children must be created | 
|   46   // If there are no updates left to consider, we're done. |   36 // after their parent folder is created.  This function runs an O(n^2) algorithm | 
|   47   if (end_ == begin_) |   37 // that will keep trying until there is nothing left to apply, or it stops | 
|   48     return false; |   38 // making progress, which would indicate that the hierarchy is invalid. | 
|   49   if (pointer_ == end_) { |   39 // | 
|   50     if (!progress_) |   40 // The update applicator also has to deal with simple conflicts, which occur | 
|   51       return false; |   41 // when an item is modified on both the server and the local model, and | 
 |   42 // encryption conflicts.  There's not much we can do about them here, so we | 
 |   43 // don't bother re-processing them on subsequent passes. | 
 |   44 void UpdateApplicator::AttemptApplications( | 
 |   45     syncable::WriteTransaction* trans, | 
 |   46     std::vector<int64> to_apply, | 
 |   47     std::set<syncable::Id>* simple_conflict_ids) { | 
|   52  |   48  | 
|   53     DVLOG(1) << "UpdateApplicator doing additional pass."; |   49   // This function may be called more than once.  If that happens, we will | 
|   54     pointer_ = begin_; |   50   // re-visit old encryption and hierarchy conflicts.  We clear those counts | 
|   55     progress_ = false; |   51   // in advance to avoid double-counting them. | 
 |   52   encryption_conflicts_ = 0; | 
 |   53   hierarchy_conflicts_ = 0; | 
|   56  |   54  | 
|   57     // Clear the tracked failures to avoid double-counting. |   55   DVLOG(1) << "UpdateApplicator running over " << to_apply.size() << " items."; | 
|   58     application_results_.ClearConflicts(); |   56   while (!to_apply.empty()) { | 
 |   57     std::vector<int64> to_reapply; | 
 |   58  | 
 |   59     for (UpdateIterator i = to_apply.begin(); i != to_apply.end(); ++i) { | 
 |   60       syncable::Entry read_entry(trans, syncable::GET_BY_HANDLE, *i); | 
 |   61       if (SkipUpdate(read_entry)) { | 
 |   62         continue; | 
 |   63       } | 
 |   64  | 
 |   65       syncable::MutableEntry entry(trans, syncable::GET_BY_HANDLE, *i); | 
 |   66       UpdateAttemptResponse result = AttemptToUpdateEntry( | 
 |   67           trans, &entry, cryptographer_); | 
 |   68  | 
 |   69       switch (result) { | 
 |   70         case SUCCESS: | 
 |   71           updates_applied_++; | 
 |   72           break; | 
 |   73         case CONFLICT_SIMPLE: | 
 |   74           simple_conflict_ids->insert(entry.Get(ID)); | 
 |   75           break; | 
 |   76         case CONFLICT_ENCRYPTION: | 
 |   77           encryption_conflicts_++; | 
 |   78           break; | 
 |   79         case CONFLICT_HIERARCHY: | 
 |   80           // It may be a real hierarchy conflict, but odds are we just tried to | 
 |   81           // apply it in the wrong order. | 
 |   82           to_reapply.push_back(*i); | 
 |   83           break; | 
 |   84         default: | 
 |   85           NOTREACHED(); | 
 |   86           break; | 
 |   87       } | 
 |   88     } | 
 |   89  | 
 |   90     if (to_reapply.size() == to_apply.size()) { | 
 |   91       // We made no progress.  Must be stubborn hierarchy conflicts. | 
 |   92       hierarchy_conflicts_ = to_apply.size(); | 
 |   93       break; | 
 |   94     } | 
 |   95  | 
 |   96     to_apply = to_reapply; | 
|   59   } |   97   } | 
|   60  |  | 
|   61   syncable::Entry read_only(trans, syncable::GET_BY_HANDLE, *pointer_); |  | 
|   62   if (SkipUpdate(read_only)) { |  | 
|   63     Advance(); |  | 
|   64     return true; |  | 
|   65   } |  | 
|   66  |  | 
|   67   syncable::MutableEntry entry(trans, syncable::GET_BY_HANDLE, *pointer_); |  | 
|   68   UpdateAttemptResponse updateResponse = AttemptToUpdateEntry( |  | 
|   69       trans, &entry, resolver_, cryptographer_); |  | 
|   70   switch (updateResponse) { |  | 
|   71     case SUCCESS: |  | 
|   72       Advance(); |  | 
|   73       progress_ = true; |  | 
|   74       application_results_.AddSuccess(entry.Get(syncable::ID)); |  | 
|   75       break; |  | 
|   76     case CONFLICT_SIMPLE: |  | 
|   77       pointer_++; |  | 
|   78       application_results_.AddSimpleConflict(entry.Get(syncable::ID)); |  | 
|   79       break; |  | 
|   80     case CONFLICT_ENCRYPTION: |  | 
|   81       pointer_++; |  | 
|   82       application_results_.AddEncryptionConflict(entry.Get(syncable::ID)); |  | 
|   83       break; |  | 
|   84     case CONFLICT_HIERARCHY: |  | 
|   85       pointer_++; |  | 
|   86       application_results_.AddHierarchyConflict(entry.Get(syncable::ID)); |  | 
|   87       break; |  | 
|   88     default: |  | 
|   89       NOTREACHED(); |  | 
|   90       break; |  | 
|   91   } |  | 
|   92   DVLOG(1) << "Apply Status for " << entry.Get(syncable::META_HANDLE) |  | 
|   93            << " is " << updateResponse; |  | 
|   94  |  | 
|   95   return true; |  | 
|   96 } |  | 
|   97  |  | 
|   98 void UpdateApplicator::Advance() { |  | 
|   99   --end_; |  | 
|  100   *pointer_ = *end_; |  | 
|  101 } |   98 } | 
|  102  |   99  | 
|  103 bool UpdateApplicator::SkipUpdate(const syncable::Entry& entry) { |  100 bool UpdateApplicator::SkipUpdate(const syncable::Entry& entry) { | 
|  104   ModelType type = entry.GetServerModelType(); |  101   ModelType type = entry.GetServerModelType(); | 
|  105   ModelSafeGroup g = GetGroupForModelType(type, routing_info_); |  102   ModelSafeGroup g = GetGroupForModelType(type, routing_info_); | 
|  106   // The set of updates passed to the UpdateApplicator should already |  103   // The set of updates passed to the UpdateApplicator should already | 
|  107   // be group-filtered. |  104   // be group-filtered. | 
|  108   if (g != group_filter_) { |  105   if (g != group_filter_) { | 
|  109     NOTREACHED(); |  106     NOTREACHED(); | 
|  110     return true; |  107     return true; | 
|  111   } |  108   } | 
|  112   if (g == GROUP_PASSIVE && |  109   if (g == GROUP_PASSIVE && | 
|  113       !routing_info_.count(type) && |  110       !routing_info_.count(type) && | 
|  114       type != UNSPECIFIED && |  111       type != UNSPECIFIED && | 
|  115       type != TOP_LEVEL_FOLDER) { |  112       type != TOP_LEVEL_FOLDER) { | 
|  116     DVLOG(1) << "Skipping update application, type not permitted."; |  113     DVLOG(1) << "Skipping update application, type not permitted."; | 
|  117     return true; |  114     return true; | 
|  118   } |  115   } | 
|  119   return false; |  116   return false; | 
|  120 } |  117 } | 
|  121  |  118  | 
|  122 bool UpdateApplicator::AllUpdatesApplied() const { |  119 void UpdateApplicator::SaveStats(sessions::StatusController* status) { | 
|  123   return application_results_.no_conflicts() && begin_ == end_; |  120   status->increment_num_encryption_conflicts_by(encryption_conflicts_); | 
|  124 } |  121   status->increment_num_hierarchy_conflicts_by(hierarchy_conflicts_); | 
 |  122   status->increment_num_updates_applied_by(updates_applied_); | 
|  125  |  123  | 
|  126 void UpdateApplicator::SaveProgressIntoSessionState( |  | 
|  127     sessions::ConflictProgress* conflict_progress, |  | 
|  128     sessions::UpdateProgress* update_progress) { |  | 
|  129   DCHECK(begin_ == end_ || ((pointer_ == end_) && !progress_)) |  | 
|  130       << "SaveProgress called before updates exhausted."; |  | 
|  131  |  | 
|  132   application_results_.SaveProgress(conflict_progress, update_progress); |  | 
|  133 } |  | 
|  134  |  | 
|  135 UpdateApplicator::ResultTracker::ResultTracker(size_t num_results) { |  | 
|  136   successful_ids_.reserve(num_results); |  | 
|  137 } |  | 
|  138  |  | 
|  139 UpdateApplicator::ResultTracker::~ResultTracker() { |  | 
|  140 } |  | 
|  141  |  | 
|  142 void UpdateApplicator::ResultTracker::AddSimpleConflict(syncable::Id id) { |  | 
|  143   conflicting_ids_.push_back(id); |  | 
|  144 } |  | 
|  145  |  | 
|  146 void UpdateApplicator::ResultTracker::AddEncryptionConflict(syncable::Id id) { |  | 
|  147   encryption_conflict_ids_.push_back(id); |  | 
|  148 } |  | 
|  149  |  | 
|  150 void UpdateApplicator::ResultTracker::AddHierarchyConflict(syncable::Id id) { |  | 
|  151   hierarchy_conflict_ids_.push_back(id); |  | 
|  152 } |  | 
|  153  |  | 
|  154 void UpdateApplicator::ResultTracker::AddSuccess(syncable::Id id) { |  | 
|  155   successful_ids_.push_back(id); |  | 
|  156 } |  | 
|  157  |  | 
|  158 void UpdateApplicator::ResultTracker::SaveProgress( |  | 
|  159     sessions::ConflictProgress* conflict_progress, |  | 
|  160     sessions::UpdateProgress* update_progress) { |  | 
|  161   vector<syncable::Id>::const_iterator i; |  | 
|  162   for (i = conflicting_ids_.begin(); i != conflicting_ids_.end(); ++i) { |  | 
|  163     conflict_progress->AddSimpleConflictingItemById(*i); |  | 
|  164     update_progress->AddAppliedUpdate(CONFLICT_SIMPLE, *i); |  | 
|  165   } |  | 
|  166   for (i = encryption_conflict_ids_.begin(); |  | 
|  167        i != encryption_conflict_ids_.end(); ++i) { |  | 
|  168     conflict_progress->AddEncryptionConflictingItemById(*i); |  | 
|  169     update_progress->AddAppliedUpdate(CONFLICT_ENCRYPTION, *i); |  | 
|  170   } |  | 
|  171   for (i = hierarchy_conflict_ids_.begin(); |  | 
|  172        i != hierarchy_conflict_ids_.end(); ++i) { |  | 
|  173     conflict_progress->AddHierarchyConflictingItemById(*i); |  | 
|  174     update_progress->AddAppliedUpdate(CONFLICT_HIERARCHY, *i); |  | 
|  175   } |  | 
|  176   for (i = successful_ids_.begin(); i != successful_ids_.end(); ++i) { |  | 
|  177     conflict_progress->EraseSimpleConflictingItemById(*i); |  | 
|  178     update_progress->AddAppliedUpdate(SUCCESS, *i); |  | 
|  179   } |  | 
|  180 } |  | 
|  181  |  | 
|  182 void UpdateApplicator::ResultTracker::ClearConflicts() { |  | 
|  183   conflicting_ids_.clear(); |  | 
|  184   encryption_conflict_ids_.clear(); |  | 
|  185   hierarchy_conflict_ids_.clear(); |  | 
|  186 } |  | 
|  187  |  | 
|  188 bool UpdateApplicator::ResultTracker::no_conflicts() const { |  | 
|  189   return conflicting_ids_.empty(); |  | 
|  190 } |  124 } | 
|  191  |  125  | 
|  192 }  // namespace syncer |  126 }  // namespace syncer | 
| OLD | NEW |