| 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 17 matching lines...) Expand all Loading... |
| 28 using syncable::Id; | 28 using syncable::Id; |
| 29 using syncable::MutableEntry; | 29 using syncable::MutableEntry; |
| 30 using syncable::WriteTransaction; | 30 using syncable::WriteTransaction; |
| 31 | 31 |
| 32 ConflictResolver::ConflictResolver() { | 32 ConflictResolver::ConflictResolver() { |
| 33 } | 33 } |
| 34 | 34 |
| 35 ConflictResolver::~ConflictResolver() { | 35 ConflictResolver::~ConflictResolver() { |
| 36 } | 36 } |
| 37 | 37 |
| 38 ConflictResolver::ProcessSimpleConflictResult | 38 void ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans, |
| 39 ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans, | 39 const Id& id, |
| 40 const Id& id, | 40 const Cryptographer* cryptographer, |
| 41 const Cryptographer* cryptographer, | 41 StatusController* status) { |
| 42 StatusController* status) { | |
| 43 MutableEntry entry(trans, syncable::GET_BY_ID, id); | 42 MutableEntry entry(trans, syncable::GET_BY_ID, id); |
| 44 // Must be good as the entry won't have been cleaned up. | 43 // Must be good as the entry won't have been cleaned up. |
| 45 CHECK(entry.good()); | 44 CHECK(entry.good()); |
| 46 | 45 |
| 47 // This function can only resolve simple conflicts. Simple conflicts have | 46 // This function can only resolve simple conflicts. Simple conflicts have |
| 48 // both IS_UNSYNCED and IS_UNAPPLIED_UDPATE set. | 47 // both IS_UNSYNCED and IS_UNAPPLIED_UDPATE set. |
| 49 if (!entry.Get(syncable::IS_UNAPPLIED_UPDATE) || | 48 if (!entry.Get(syncable::IS_UNAPPLIED_UPDATE) || |
| 50 !entry.Get(syncable::IS_UNSYNCED)) { | 49 !entry.Get(syncable::IS_UNSYNCED)) { |
| 51 // This is very unusual, but it can happen in tests. We may be able to | 50 // This is very unusual, but it can happen in tests. We may be able to |
| 52 // assert NOTREACHED() here when those tests are updated. | 51 // assert NOTREACHED() here when those tests are updated. |
| 53 return NO_SYNC_PROGRESS; | 52 return; |
| 54 } | 53 } |
| 55 | 54 |
| 56 if (entry.Get(syncable::IS_DEL) && entry.Get(syncable::SERVER_IS_DEL)) { | 55 if (entry.Get(syncable::IS_DEL) && entry.Get(syncable::SERVER_IS_DEL)) { |
| 57 // we've both deleted it, so lets just drop the need to commit/update this | 56 // we've both deleted it, so lets just drop the need to commit/update this |
| 58 // entry. | 57 // entry. |
| 59 entry.Put(syncable::IS_UNSYNCED, false); | 58 entry.Put(syncable::IS_UNSYNCED, false); |
| 60 entry.Put(syncable::IS_UNAPPLIED_UPDATE, false); | 59 entry.Put(syncable::IS_UNAPPLIED_UPDATE, false); |
| 61 // we've made changes, but they won't help syncing progress. | 60 // we've made changes, but they won't help syncing progress. |
| 62 // METRIC simple conflict resolved by merge. | 61 // METRIC simple conflict resolved by merge. |
| 63 return NO_SYNC_PROGRESS; | 62 return; |
| 64 } | 63 } |
| 65 | 64 |
| 66 // This logic determines "client wins" vs. "server wins" strategy picking. | 65 // This logic determines "client wins" vs. "server wins" strategy picking. |
| 67 // By the time we get to this point, we rely on the following to be true: | 66 // By the time we get to this point, we rely on the following to be true: |
| 68 // a) We can decrypt both the local and server data (else we'd be in | 67 // a) We can decrypt both the local and server data (else we'd be in |
| 69 // conflict encryption and not attempting to resolve). | 68 // conflict encryption and not attempting to resolve). |
| 70 // b) All unsynced changes have been re-encrypted with the default key ( | 69 // b) All unsynced changes have been re-encrypted with the default key ( |
| 71 // occurs either in AttemptToUpdateEntry, SetEncryptionPassphrase, | 70 // occurs either in AttemptToUpdateEntry, SetEncryptionPassphrase, |
| 72 // SetDecryptionPassphrase, or RefreshEncryption). | 71 // SetDecryptionPassphrase, or RefreshEncryption). |
| 73 // c) Base_server_specifics having a valid datatype means that we received | 72 // c) Base_server_specifics having a valid datatype means that we received |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 199 CONFLICT_RESOLUTION_SIZE); | 198 CONFLICT_RESOLUTION_SIZE); |
| 200 } else if (base_server_specifics_match) { | 199 } else if (base_server_specifics_match) { |
| 201 DVLOG(1) << "Resolving simple conflict, ignoring server encryption " | 200 DVLOG(1) << "Resolving simple conflict, ignoring server encryption " |
| 202 << " changes for: " << entry; | 201 << " changes for: " << entry; |
| 203 status->increment_num_server_overwrites(); | 202 status->increment_num_server_overwrites(); |
| 204 OverwriteServerChanges(&entry); | 203 OverwriteServerChanges(&entry); |
| 205 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | 204 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
| 206 IGNORE_ENCRYPTION, | 205 IGNORE_ENCRYPTION, |
| 207 CONFLICT_RESOLUTION_SIZE); | 206 CONFLICT_RESOLUTION_SIZE); |
| 208 } else if (entry_deleted || !name_matches || !parent_matches) { | 207 } else if (entry_deleted || !name_matches || !parent_matches) { |
| 208 // NOTE: The update application logic assumes that conflict resolution |
| 209 // will never result in changes to the local hierarchy. The entry_deleted |
| 210 // and !paremt_matches cases here are critical to maintaining that |
| 211 // assumption. |
| 209 OverwriteServerChanges(&entry); | 212 OverwriteServerChanges(&entry); |
| 210 status->increment_num_server_overwrites(); | 213 status->increment_num_server_overwrites(); |
| 211 DVLOG(1) << "Resolving simple conflict, overwriting server changes " | 214 DVLOG(1) << "Resolving simple conflict, overwriting server changes " |
| 212 << "for: " << entry; | 215 << "for: " << entry; |
| 213 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | 216 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
| 214 OVERWRITE_SERVER, | 217 OVERWRITE_SERVER, |
| 215 CONFLICT_RESOLUTION_SIZE); | 218 CONFLICT_RESOLUTION_SIZE); |
| 216 } else { | 219 } else { |
| 217 DVLOG(1) << "Resolving simple conflict, ignoring local changes for: " | 220 DVLOG(1) << "Resolving simple conflict, ignoring local changes for: " |
| 218 << entry; | 221 << entry; |
| 219 IgnoreLocalChanges(&entry); | 222 IgnoreLocalChanges(&entry); |
| 220 status->increment_num_local_overwrites(); | 223 status->increment_num_local_overwrites(); |
| 221 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | 224 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
| 222 OVERWRITE_LOCAL, | 225 OVERWRITE_LOCAL, |
| 223 CONFLICT_RESOLUTION_SIZE); | 226 CONFLICT_RESOLUTION_SIZE); |
| 224 } | 227 } |
| 225 // Now that we've resolved the conflict, clear the prev server | 228 // Now that we've resolved the conflict, clear the prev server |
| 226 // specifics. | 229 // specifics. |
| 227 entry.Put(syncable::BASE_SERVER_SPECIFICS, sync_pb::EntitySpecifics()); | 230 entry.Put(syncable::BASE_SERVER_SPECIFICS, sync_pb::EntitySpecifics()); |
| 228 return SYNC_PROGRESS; | |
| 229 } else { // SERVER_IS_DEL is true | 231 } else { // SERVER_IS_DEL is true |
| 230 // If a server deleted folder has local contents it should be a hierarchy | |
| 231 // conflict. Hierarchy conflicts should not be processed by this function. | |
| 232 // We could end up here if a change was made since we last tried to detect | |
| 233 // conflicts, which was during update application. | |
| 234 if (entry.Get(syncable::IS_DIR)) { | 232 if (entry.Get(syncable::IS_DIR)) { |
| 235 Directory::ChildHandles children; | 233 Directory::ChildHandles children; |
| 236 trans->directory()->GetChildHandlesById(trans, | 234 trans->directory()->GetChildHandlesById(trans, |
| 237 entry.Get(syncable::ID), | 235 entry.Get(syncable::ID), |
| 238 &children); | 236 &children); |
| 239 if (0 != children.size()) { | 237 // If a server deleted folder has local contents it should be a hierarchy |
| 240 DVLOG(1) << "Entry is a server deleted directory with local contents, " | 238 // conflict. Hierarchy conflicts should not be processed by this |
| 241 << "should be a hierarchy conflict. (race condition)."; | 239 // function. |
| 242 return NO_SYNC_PROGRESS; | 240 DCHECK(children.empty()); |
| 243 } | |
| 244 } | 241 } |
| 245 | 242 |
| 246 // The entry is deleted on the server but still exists locally. | 243 // The entry is deleted on the server but still exists locally. |
| 247 if (!entry.Get(syncable::UNIQUE_CLIENT_TAG).empty()) { | 244 if (!entry.Get(syncable::UNIQUE_CLIENT_TAG).empty()) { |
| 248 // If we've got a client-unique tag, we can undelete while retaining | 245 // If we've got a client-unique tag, we can undelete while retaining |
| 249 // our present ID. | 246 // our present ID. |
| 250 DCHECK_EQ(entry.Get(syncable::SERVER_VERSION), 0) << "For the server to " | 247 DCHECK_EQ(entry.Get(syncable::SERVER_VERSION), 0) << "For the server to " |
| 251 "know to re-create, client-tagged items should revert to version 0 " | 248 "know to re-create, client-tagged items should revert to version 0 " |
| 252 "when server-deleted."; | 249 "when server-deleted."; |
| 253 OverwriteServerChanges(&entry); | 250 OverwriteServerChanges(&entry); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 267 | 264 |
| 268 MutableEntry server_update(trans, syncable::GET_BY_ID, id); | 265 MutableEntry server_update(trans, syncable::GET_BY_ID, id); |
| 269 CHECK(server_update.good()); | 266 CHECK(server_update.good()); |
| 270 CHECK(server_update.Get(syncable::META_HANDLE) != | 267 CHECK(server_update.Get(syncable::META_HANDLE) != |
| 271 entry.Get(syncable::META_HANDLE)) | 268 entry.Get(syncable::META_HANDLE)) |
| 272 << server_update << entry; | 269 << server_update << entry; |
| 273 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | 270 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
| 274 UNDELETE, | 271 UNDELETE, |
| 275 CONFLICT_RESOLUTION_SIZE); | 272 CONFLICT_RESOLUTION_SIZE); |
| 276 } | 273 } |
| 277 return SYNC_PROGRESS; | |
| 278 } | 274 } |
| 279 } | 275 } |
| 280 | 276 |
| 281 bool ConflictResolver::ResolveConflicts( | 277 void ConflictResolver::ResolveConflicts( |
| 282 syncable::WriteTransaction* trans, | 278 syncable::WriteTransaction* trans, |
| 283 const Cryptographer* cryptographer, | 279 const Cryptographer* cryptographer, |
| 284 const std::set<syncable::Id>& simple_conflict_ids, | 280 const std::set<syncable::Id>& simple_conflict_ids, |
| 285 sessions::StatusController* status) { | 281 sessions::StatusController* status) { |
| 286 bool forward_progress = false; | |
| 287 // Iterate over simple conflict items. | 282 // Iterate over simple conflict items. |
| 288 set<Id>::const_iterator conflicting_item_it; | 283 set<Id>::const_iterator conflicting_item_it; |
| 289 set<Id> processed_items; | 284 set<Id> processed_items; |
| 290 for (conflicting_item_it = simple_conflict_ids.begin(); | 285 for (conflicting_item_it = simple_conflict_ids.begin(); |
| 291 conflicting_item_it != simple_conflict_ids.end(); | 286 conflicting_item_it != simple_conflict_ids.end(); |
| 292 ++conflicting_item_it) { | 287 ++conflicting_item_it) { |
| 293 Id id = *conflicting_item_it; | 288 Id id = *conflicting_item_it; |
| 294 if (processed_items.count(id) > 0) | 289 if (processed_items.count(id) > 0) |
| 295 continue; | 290 continue; |
| 296 | 291 |
| (...skipping 19 matching lines...) Expand all Loading... |
| 316 CHECK(entry.good()); | 311 CHECK(entry.good()); |
| 317 Id new_prev_id = entry.Get(syncable::PREV_ID); | 312 Id new_prev_id = entry.Get(syncable::PREV_ID); |
| 318 if (new_prev_id == prev_id) | 313 if (new_prev_id == prev_id) |
| 319 break; | 314 break; |
| 320 prev_id = new_prev_id; | 315 prev_id = new_prev_id; |
| 321 } while (processed_items.count(prev_id) == 0 && | 316 } while (processed_items.count(prev_id) == 0 && |
| 322 simple_conflict_ids.count(prev_id) > 0); // Excludes root. | 317 simple_conflict_ids.count(prev_id) > 0); // Excludes root. |
| 323 while (!predecessors.empty()) { | 318 while (!predecessors.empty()) { |
| 324 id = predecessors.back(); | 319 id = predecessors.back(); |
| 325 predecessors.pop_back(); | 320 predecessors.pop_back(); |
| 326 switch (ProcessSimpleConflict(trans, id, cryptographer, status)) { | 321 ProcessSimpleConflict(trans, id, cryptographer, status); |
| 327 case NO_SYNC_PROGRESS: | |
| 328 break; | |
| 329 case SYNC_PROGRESS: | |
| 330 forward_progress = true; | |
| 331 break; | |
| 332 } | |
| 333 processed_items.insert(id); | 322 processed_items.insert(id); |
| 334 } | 323 } |
| 335 } | 324 } |
| 336 return forward_progress; | 325 return; |
| 337 } | 326 } |
| 338 | 327 |
| 339 } // namespace syncer | 328 } // namespace syncer |
| OLD | NEW |