| 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/conflict_resolver.h" | 5 #include "sync/engine/conflict_resolver.h" |
| 6 | 6 |
| 7 #include <list> | 7 #include <list> |
| 8 #include <set> | 8 #include <set> |
| 9 #include <string> | 9 #include <string> |
| 10 | 10 |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 88 // be nice if we could route this back to ModelAssociator code to pick one | 88 // be nice if we could route this back to ModelAssociator code to pick one |
| 89 // of three options: CLIENT, SERVER, or MERGE. Some datatypes (autofill) | 89 // of three options: CLIENT, SERVER, or MERGE. Some datatypes (autofill) |
| 90 // are easily mergeable. | 90 // are easily mergeable. |
| 91 // See http://crbug.com/77339. | 91 // See http://crbug.com/77339. |
| 92 bool name_matches = entry.Get(syncable::NON_UNIQUE_NAME) == | 92 bool name_matches = entry.Get(syncable::NON_UNIQUE_NAME) == |
| 93 entry.Get(syncable::SERVER_NON_UNIQUE_NAME); | 93 entry.Get(syncable::SERVER_NON_UNIQUE_NAME); |
| 94 bool parent_matches = entry.Get(syncable::PARENT_ID) == | 94 bool parent_matches = entry.Get(syncable::PARENT_ID) == |
| 95 entry.Get(syncable::SERVER_PARENT_ID); | 95 entry.Get(syncable::SERVER_PARENT_ID); |
| 96 bool entry_deleted = entry.Get(syncable::IS_DEL); | 96 bool entry_deleted = entry.Get(syncable::IS_DEL); |
| 97 | 97 |
| 98 // This positional check is meant to be necessary but not sufficient. As a | 98 // The position check might fail spuriously if one of the positions was |
| 99 // result, it may be false even when the position hasn't changed, possibly | 99 // based on a legacy random suffix, rather than a deterministic one based on |
| 100 // resulting in unnecessary commits, but if it's true the position has | 100 // originator_cache_guid and originator_item_id. If an item is being |
| 101 // definitely not changed. The check works by verifying that the prev id | 101 // modified regularly, it shouldn't take long for the suffix and position to |
| 102 // as calculated from the server position (which will ignore any | 102 // be updated, so such false failures shouldn't be a problem for long. |
| 103 // unsynced/unapplied predecessors and be root for non-bookmark datatypes) | 103 bool position_matches = parent_matches && |
| 104 // matches the client prev id. Because we traverse chains of conflicting | 104 entry.Get(syncable::SERVER_UNIQUE_POSITION).Equals( |
| 105 // items in predecessor -> successor order, we don't need to also verify the | 105 entry.Get(syncable::UNIQUE_POSITION)); |
| 106 // successor matches (If it's in conflict, we'll verify it next. If it's | |
| 107 // not, then it should be taken into account already in the | |
| 108 // ComputePrevIdFromServerPosition calculation). This works even when there | |
| 109 // are chains of conflicting items. | |
| 110 // | |
| 111 // Example: Original sequence was abcde. Server changes to aCDbe, while | |
| 112 // client changes to aDCbe (C and D are in conflict). Locally, D's prev id | |
| 113 // is a, while C's prev id is D. On the other hand, the server prev id will | |
| 114 // ignore unsynced/unapplied items, so D's server prev id will also be a, | |
| 115 // just like C's. Because we traverse in client predecessor->successor | |
| 116 // order, we evaluate D first. Since prev id and server id match, we | |
| 117 // consider the position to have remained the same for D, and will unset | |
| 118 // it's UNSYNCED/UNAPPLIED bits. When we evaluate C though, we'll see that | |
| 119 // the prev id is D locally while the server's prev id is a. C will | |
| 120 // therefore count as a positional conflict (and the local data will be | |
| 121 // overwritten by the server data typically). The final result will be | |
| 122 // aCDbe (the same as the server's view). Even though both C and D were | |
| 123 // modified, only one counted as being in actual conflict and was resolved | |
| 124 // with local/server wins. | |
| 125 // | |
| 126 // In general, when there are chains of positional conflicts, only the first | |
| 127 // item in chain (based on the clients point of view) will have both its | |
| 128 // server prev id and local prev id match. For all the rest the server prev | |
| 129 // id will be the predecessor of the first item in the chain, and therefore | |
| 130 // not match the local prev id. | |
| 131 // | |
| 132 // Similarly, chains of conflicts where the server and client info are the | |
| 133 // same are supported due to the predecessor->successor ordering. In this | |
| 134 // case, from the first item onward, we unset the UNSYNCED/UNAPPLIED bits as | |
| 135 // we decide that nothing changed. The subsequent item's server prev id will | |
| 136 // accurately match the local prev id because the predecessor is no longer | |
| 137 // UNSYNCED/UNAPPLIED. | |
| 138 // TODO(zea): simplify all this once we can directly compare server position | |
| 139 // to client position. | |
| 140 syncable::Id server_prev_id = entry.ComputePrevIdFromServerPosition( | |
| 141 entry.Get(syncable::SERVER_PARENT_ID)); | |
| 142 bool needs_reinsertion = !parent_matches || | |
| 143 server_prev_id != entry.Get(syncable::PREV_ID); | |
| 144 DVLOG_IF(1, needs_reinsertion) << "Insertion needed, server prev id " | |
| 145 << " is " << server_prev_id << ", local prev id is " | |
| 146 << entry.Get(syncable::PREV_ID); | |
| 147 const sync_pb::EntitySpecifics& specifics = | 106 const sync_pb::EntitySpecifics& specifics = |
| 148 entry.Get(syncable::SPECIFICS); | 107 entry.Get(syncable::SPECIFICS); |
| 149 const sync_pb::EntitySpecifics& server_specifics = | 108 const sync_pb::EntitySpecifics& server_specifics = |
| 150 entry.Get(syncable::SERVER_SPECIFICS); | 109 entry.Get(syncable::SERVER_SPECIFICS); |
| 151 const sync_pb::EntitySpecifics& base_server_specifics = | 110 const sync_pb::EntitySpecifics& base_server_specifics = |
| 152 entry.Get(syncable::BASE_SERVER_SPECIFICS); | 111 entry.Get(syncable::BASE_SERVER_SPECIFICS); |
| 153 std::string decrypted_specifics, decrypted_server_specifics; | 112 std::string decrypted_specifics, decrypted_server_specifics; |
| 154 bool specifics_match = false; | 113 bool specifics_match = false; |
| 155 bool server_encrypted_with_default_key = false; | 114 bool server_encrypted_with_default_key = false; |
| 156 if (specifics.has_encrypted()) { | 115 if (specifics.has_encrypted()) { |
| (...skipping 25 matching lines...) Expand all Loading... |
| 182 base_server_specifics.SerializeAsString(); | 141 base_server_specifics.SerializeAsString(); |
| 183 } else { | 142 } else { |
| 184 decrypted_base_server_specifics = cryptographer->DecryptToString( | 143 decrypted_base_server_specifics = cryptographer->DecryptToString( |
| 185 base_server_specifics.encrypted()); | 144 base_server_specifics.encrypted()); |
| 186 } | 145 } |
| 187 if (decrypted_server_specifics == decrypted_base_server_specifics) | 146 if (decrypted_server_specifics == decrypted_base_server_specifics) |
| 188 base_server_specifics_match = true; | 147 base_server_specifics_match = true; |
| 189 } | 148 } |
| 190 | 149 |
| 191 if (!entry_deleted && name_matches && parent_matches && specifics_match && | 150 if (!entry_deleted && name_matches && parent_matches && specifics_match && |
| 192 !needs_reinsertion) { | 151 position_matches) { |
| 193 DVLOG(1) << "Resolving simple conflict, everything matches, ignoring " | 152 DVLOG(1) << "Resolving simple conflict, everything matches, ignoring " |
| 194 << "changes for: " << entry; | 153 << "changes for: " << entry; |
| 195 conflict_util::IgnoreConflict(&entry); | 154 conflict_util::IgnoreConflict(&entry); |
| 196 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | 155 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
| 197 CHANGES_MATCH, | 156 CHANGES_MATCH, |
| 198 CONFLICT_RESOLUTION_SIZE); | 157 CONFLICT_RESOLUTION_SIZE); |
| 199 } else if (base_server_specifics_match) { | 158 } else if (base_server_specifics_match) { |
| 200 DVLOG(1) << "Resolving simple conflict, ignoring server encryption " | 159 DVLOG(1) << "Resolving simple conflict, ignoring server encryption " |
| 201 << " changes for: " << entry; | 160 << " changes for: " << entry; |
| 202 status->increment_num_server_overwrites(); | 161 status->increment_num_server_overwrites(); |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 253 } | 212 } |
| 254 } | 213 } |
| 255 | 214 |
| 256 void ConflictResolver::ResolveConflicts( | 215 void ConflictResolver::ResolveConflicts( |
| 257 syncable::WriteTransaction* trans, | 216 syncable::WriteTransaction* trans, |
| 258 const Cryptographer* cryptographer, | 217 const Cryptographer* cryptographer, |
| 259 const std::set<syncable::Id>& simple_conflict_ids, | 218 const std::set<syncable::Id>& simple_conflict_ids, |
| 260 sessions::StatusController* status) { | 219 sessions::StatusController* status) { |
| 261 // Iterate over simple conflict items. | 220 // Iterate over simple conflict items. |
| 262 set<Id>::const_iterator conflicting_item_it; | 221 set<Id>::const_iterator conflicting_item_it; |
| 263 set<Id> processed_items; | |
| 264 for (conflicting_item_it = simple_conflict_ids.begin(); | 222 for (conflicting_item_it = simple_conflict_ids.begin(); |
| 265 conflicting_item_it != simple_conflict_ids.end(); | 223 conflicting_item_it != simple_conflict_ids.end(); |
| 266 ++conflicting_item_it) { | 224 ++conflicting_item_it) { |
| 267 Id id = *conflicting_item_it; | 225 Id id = *conflicting_item_it; |
| 268 if (processed_items.count(id) > 0) | |
| 269 continue; | |
| 270 | 226 |
| 271 // We don't resolve conflicts for control types here. | 227 // We don't resolve conflicts for control types here. |
| 272 Entry conflicting_node(trans, syncable::GET_BY_ID, id); | 228 Entry conflicting_node(trans, syncable::GET_BY_ID, id); |
| 273 CHECK(conflicting_node.good()); | 229 CHECK(conflicting_node.good()); |
| 274 if (IsControlType( | 230 if (IsControlType( |
| 275 GetModelTypeFromSpecifics(conflicting_node.Get(syncable::SPECIFICS)))) { | 231 GetModelTypeFromSpecifics(conflicting_node.Get(syncable::SPECIFICS)))) { |
| 276 continue; | 232 continue; |
| 277 } | 233 } |
| 278 | 234 |
| 279 // We have a simple conflict. In order check if positions have changed, | 235 ProcessSimpleConflict(trans, id, cryptographer, status); |
| 280 // we need to process conflicting predecessors before successors. Traverse | |
| 281 // backwards through all continuous conflicting predecessors, building a | |
| 282 // stack of items to resolve in predecessor->successor order, then process | |
| 283 // each item individually. | |
| 284 list<Id> predecessors; | |
| 285 Id prev_id = id; | |
| 286 do { | |
| 287 predecessors.push_back(prev_id); | |
| 288 Entry entry(trans, syncable::GET_BY_ID, prev_id); | |
| 289 // Any entry in conflict must be valid. | |
| 290 CHECK(entry.good()); | |
| 291 Id new_prev_id = entry.Get(syncable::PREV_ID); | |
| 292 if (new_prev_id == prev_id) | |
| 293 break; | |
| 294 prev_id = new_prev_id; | |
| 295 } while (processed_items.count(prev_id) == 0 && | |
| 296 simple_conflict_ids.count(prev_id) > 0); // Excludes root. | |
| 297 while (!predecessors.empty()) { | |
| 298 id = predecessors.back(); | |
| 299 predecessors.pop_back(); | |
| 300 ProcessSimpleConflict(trans, id, cryptographer, status); | |
| 301 processed_items.insert(id); | |
| 302 } | |
| 303 } | 236 } |
| 304 return; | 237 return; |
| 305 } | 238 } |
| 306 | 239 |
| 307 } // namespace syncer | 240 } // namespace syncer |
| OLD | NEW |