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