| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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 entry. | 3 // found in the LICENSE entry. |
| 4 | 4 |
| 5 #include "chrome/browser/sync/engine/conflict_resolver.h" | 5 #include "chrome/browser/sync/engine/conflict_resolver.h" |
| 6 | 6 |
| 7 #include <map> | 7 #include <map> |
| 8 #include <set> | 8 #include <set> |
| 9 | 9 |
| 10 #include "chrome/browser/sync/engine/syncer.h" | 10 #include "chrome/browser/sync/engine/syncer.h" |
| 11 #include "chrome/browser/sync/engine/syncer_util.h" | 11 #include "chrome/browser/sync/engine/syncer_util.h" |
| 12 #include "chrome/browser/sync/protocol/service_constants.h" | 12 #include "chrome/browser/sync/protocol/service_constants.h" |
| 13 #include "chrome/browser/sync/sessions/status_controller.h" |
| 13 #include "chrome/browser/sync/syncable/directory_manager.h" | 14 #include "chrome/browser/sync/syncable/directory_manager.h" |
| 14 #include "chrome/browser/sync/syncable/syncable.h" | 15 #include "chrome/browser/sync/syncable/syncable.h" |
| 15 #include "chrome/browser/sync/util/character_set_converters.h" | 16 #include "chrome/browser/sync/util/character_set_converters.h" |
| 16 #include "chrome/browser/sync/util/event_sys-inl.h" | 17 #include "chrome/browser/sync/util/event_sys-inl.h" |
| 17 #include "chrome/browser/sync/util/path_helpers.h" | 18 #include "chrome/browser/sync/util/path_helpers.h" |
| 18 | 19 |
| 19 using std::map; | 20 using std::map; |
| 20 using std::set; | 21 using std::set; |
| 21 using syncable::BaseTransaction; | 22 using syncable::BaseTransaction; |
| 22 using syncable::Directory; | 23 using syncable::Directory; |
| 23 using syncable::Entry; | 24 using syncable::Entry; |
| 24 using syncable::Id; | 25 using syncable::Id; |
| 25 using syncable::MutableEntry; | 26 using syncable::MutableEntry; |
| 26 using syncable::ScopedDirLookup; | 27 using syncable::ScopedDirLookup; |
| 27 using syncable::WriteTransaction; | 28 using syncable::WriteTransaction; |
| 28 | 29 |
| 29 namespace browser_sync { | 30 namespace browser_sync { |
| 30 | 31 |
| 32 using sessions::ConflictProgress; |
| 33 using sessions::StatusController; |
| 34 |
| 31 const int SYNC_CYCLES_BEFORE_ADMITTING_DEFEAT = 8; | 35 const int SYNC_CYCLES_BEFORE_ADMITTING_DEFEAT = 8; |
| 32 | 36 |
| 33 ConflictResolver::ConflictResolver() { | 37 ConflictResolver::ConflictResolver() { |
| 34 } | 38 } |
| 35 | 39 |
| 36 ConflictResolver::~ConflictResolver() { | 40 ConflictResolver::~ConflictResolver() { |
| 37 } | 41 } |
| 38 | 42 |
| 39 void ConflictResolver::IgnoreLocalChanges(MutableEntry* entry) { | 43 void ConflictResolver::IgnoreLocalChanges(MutableEntry* entry) { |
| 40 // An update matches local actions, merge the changes. | 44 // An update matches local actions, merge the changes. |
| (...skipping 12 matching lines...) Expand all Loading... |
| 53 // made our local client changes. | 57 // made our local client changes. |
| 54 // TODO(chron): This is really a general property clobber. We clobber | 58 // TODO(chron): This is really a general property clobber. We clobber |
| 55 // the server side property. Perhaps we should actually do property merging. | 59 // the server side property. Perhaps we should actually do property merging. |
| 56 entry->Put(syncable::BASE_VERSION, entry->Get(syncable::SERVER_VERSION)); | 60 entry->Put(syncable::BASE_VERSION, entry->Get(syncable::SERVER_VERSION)); |
| 57 entry->Put(syncable::IS_UNAPPLIED_UPDATE, false); | 61 entry->Put(syncable::IS_UNAPPLIED_UPDATE, false); |
| 58 // METRIC conflict resolved by overwrite. | 62 // METRIC conflict resolved by overwrite. |
| 59 } | 63 } |
| 60 | 64 |
| 61 ConflictResolver::ProcessSimpleConflictResult | 65 ConflictResolver::ProcessSimpleConflictResult |
| 62 ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans, | 66 ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans, |
| 63 Id id, | 67 const Id& id) { |
| 64 SyncerSession* session) { | |
| 65 MutableEntry entry(trans, syncable::GET_BY_ID, id); | 68 MutableEntry entry(trans, syncable::GET_BY_ID, id); |
| 66 // Must be good as the entry won't have been cleaned up. | 69 // Must be good as the entry won't have been cleaned up. |
| 67 CHECK(entry.good()); | 70 CHECK(entry.good()); |
| 68 // If an update fails, locally we have to be in a set or unsynced. We're not | 71 // If an update fails, locally we have to be in a set or unsynced. We're not |
| 69 // in a set here, so we must be unsynced. | 72 // in a set here, so we must be unsynced. |
| 70 if (!entry.Get(syncable::IS_UNSYNCED)) { | 73 if (!entry.Get(syncable::IS_UNSYNCED)) { |
| 71 return NO_SYNC_PROGRESS; | 74 return NO_SYNC_PROGRESS; |
| 72 } | 75 } |
| 73 | 76 |
| 74 if (!entry.Get(syncable::IS_UNAPPLIED_UPDATE)) { | 77 if (!entry.Get(syncable::IS_UNAPPLIED_UPDATE)) { |
| (...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 338 return true; | 341 return true; |
| 339 } | 342 } |
| 340 return false; | 343 return false; |
| 341 } | 344 } |
| 342 | 345 |
| 343 } // namespace | 346 } // namespace |
| 344 | 347 |
| 345 // TODO(sync): Eliminate conflict sets. They're not necessary. | 348 // TODO(sync): Eliminate conflict sets. They're not necessary. |
| 346 bool ConflictResolver::ProcessConflictSet(WriteTransaction* trans, | 349 bool ConflictResolver::ProcessConflictSet(WriteTransaction* trans, |
| 347 ConflictSet* conflict_set, | 350 ConflictSet* conflict_set, |
| 348 int conflict_count, | 351 int conflict_count) { |
| 349 SyncerSession* session) { | |
| 350 int set_size = conflict_set->size(); | 352 int set_size = conflict_set->size(); |
| 351 if (set_size < 2) { | 353 if (set_size < 2) { |
| 352 LOG(WARNING) << "Skipping conflict set because it has size " << set_size; | 354 LOG(WARNING) << "Skipping conflict set because it has size " << set_size; |
| 353 // We can end up with sets of size one if we have a new item in a set that | 355 // We can end up with sets of size one if we have a new item in a set that |
| 354 // we tried to commit transactionally. This should not be a persistent | 356 // we tried to commit transactionally. This should not be a persistent |
| 355 // situation. | 357 // situation. |
| 356 return false; | 358 return false; |
| 357 } | 359 } |
| 358 if (conflict_count < 3) { | 360 if (conflict_count < 3) { |
| 359 // Avoid resolving sets that could be the result of transient conflicts. | 361 // Avoid resolving sets that could be the result of transient conflicts. |
| (...skipping 12 matching lines...) Expand all Loading... |
| 372 return true; | 374 return true; |
| 373 return false; | 375 return false; |
| 374 } | 376 } |
| 375 | 377 |
| 376 template <typename InputIt> | 378 template <typename InputIt> |
| 377 bool ConflictResolver::LogAndSignalIfConflictStuck( | 379 bool ConflictResolver::LogAndSignalIfConflictStuck( |
| 378 BaseTransaction* trans, | 380 BaseTransaction* trans, |
| 379 int attempt_count, | 381 int attempt_count, |
| 380 InputIt begin, | 382 InputIt begin, |
| 381 InputIt end, | 383 InputIt end, |
| 382 ConflictResolutionView* view) { | 384 StatusController* status) { |
| 383 if (attempt_count < SYNC_CYCLES_BEFORE_ADMITTING_DEFEAT) { | 385 if (attempt_count < SYNC_CYCLES_BEFORE_ADMITTING_DEFEAT) { |
| 384 return false; | 386 return false; |
| 385 } | 387 } |
| 386 | 388 |
| 387 // Don't signal stuck if we're not up to date. | 389 // Don't signal stuck if we're not up to date. |
| 388 if (view->num_server_changes_remaining() > 0) { | 390 if (status->change_progress().num_server_changes_remaining > 0) { |
| 389 return false; | 391 return false; |
| 390 } | 392 } |
| 391 | 393 |
| 392 LOG(ERROR) << "[BUG] Conflict set cannot be resolved, has " | 394 LOG(ERROR) << "[BUG] Conflict set cannot be resolved, has " |
| 393 << end - begin << " items:"; | 395 << end - begin << " items:"; |
| 394 for (InputIt i = begin ; i != end ; ++i) { | 396 for (InputIt i = begin ; i != end ; ++i) { |
| 395 Entry e(trans, syncable::GET_BY_ID, *i); | 397 Entry e(trans, syncable::GET_BY_ID, *i); |
| 396 if (e.good()) | 398 if (e.good()) |
| 397 LOG(ERROR) << " " << e; | 399 LOG(ERROR) << " " << e; |
| 398 else | 400 else |
| 399 LOG(ERROR) << " Bad ID:" << *i; | 401 LOG(ERROR) << " Bad ID:" << *i; |
| 400 } | 402 } |
| 401 | 403 |
| 402 view->set_syncer_stuck(true); | 404 status->set_syncer_stuck(true); |
| 403 | 405 |
| 404 return true; | 406 return true; |
| 405 // TODO(sync): If we're stuck for a while we need to alert the user, clear | 407 // TODO(sync): If we're stuck for a while we need to alert the user, clear |
| 406 // cache or reset syncing. At the very least we should stop trying something | 408 // cache or reset syncing. At the very least we should stop trying something |
| 407 // that's obviously not working. | 409 // that's obviously not working. |
| 408 } | 410 } |
| 409 | 411 |
| 410 bool ConflictResolver::ResolveSimpleConflicts(const ScopedDirLookup& dir, | 412 bool ConflictResolver::ResolveSimpleConflicts(const ScopedDirLookup& dir, |
| 411 ConflictResolutionView* view, | 413 StatusController* status) { |
| 412 SyncerSession* session) { | |
| 413 WriteTransaction trans(dir, syncable::SYNCER, __FILE__, __LINE__); | 414 WriteTransaction trans(dir, syncable::SYNCER, __FILE__, __LINE__); |
| 414 bool forward_progress = false; | 415 bool forward_progress = false; |
| 416 ConflictProgress const* progress = status->conflict_progress(); |
| 415 // First iterate over simple conflict items (those that belong to no set). | 417 // First iterate over simple conflict items (those that belong to no set). |
| 416 set<Id>::const_iterator conflicting_item_it; | 418 set<Id>::const_iterator conflicting_item_it; |
| 417 for (conflicting_item_it = view->CommitConflictsBegin(); | 419 for (conflicting_item_it = progress->ConflictingItemsBeginConst(); |
| 418 conflicting_item_it != view->CommitConflictsEnd(); | 420 conflicting_item_it != progress->ConflictingItemsEnd(); |
| 419 ++conflicting_item_it) { | 421 ++conflicting_item_it) { |
| 420 Id id = *conflicting_item_it; | 422 Id id = *conflicting_item_it; |
| 421 map<Id, ConflictSet*>::const_iterator item_set_it = | 423 map<Id, ConflictSet*>::const_iterator item_set_it = |
| 422 view->IdToConflictSetFind(id); | 424 progress->IdToConflictSetFind(id); |
| 423 if (item_set_it == view->IdToConflictSetEnd() || | 425 if (item_set_it == progress->IdToConflictSetEnd() || |
| 424 0 == item_set_it->second) { | 426 0 == item_set_it->second) { |
| 425 // We have a simple conflict. | 427 // We have a simple conflict. |
| 426 switch (ProcessSimpleConflict(&trans, id, session)) { | 428 switch (ProcessSimpleConflict(&trans, id)) { |
| 427 case NO_SYNC_PROGRESS: | 429 case NO_SYNC_PROGRESS: |
| 428 { | 430 { |
| 429 int conflict_count = (simple_conflict_count_map_[id] += 2); | 431 int conflict_count = (simple_conflict_count_map_[id] += 2); |
| 430 LogAndSignalIfConflictStuck(&trans, conflict_count, | 432 LogAndSignalIfConflictStuck(&trans, conflict_count, |
| 431 &id, &id + 1, view); | 433 &id, &id + 1, status); |
| 432 break; | 434 break; |
| 433 } | 435 } |
| 434 case SYNC_PROGRESS: | 436 case SYNC_PROGRESS: |
| 435 forward_progress = true; | 437 forward_progress = true; |
| 436 break; | 438 break; |
| 437 } | 439 } |
| 438 } | 440 } |
| 439 } | 441 } |
| 440 // Reduce the simple_conflict_count for each item currently tracked. | 442 // Reduce the simple_conflict_count for each item currently tracked. |
| 441 SimpleConflictCountMap::iterator i = simple_conflict_count_map_.begin(); | 443 SimpleConflictCountMap::iterator i = simple_conflict_count_map_.begin(); |
| 442 while (i != simple_conflict_count_map_.end()) { | 444 while (i != simple_conflict_count_map_.end()) { |
| 443 if (0 == --(i->second)) | 445 if (0 == --(i->second)) |
| 444 simple_conflict_count_map_.erase(i++); | 446 simple_conflict_count_map_.erase(i++); |
| 445 else | 447 else |
| 446 ++i; | 448 ++i; |
| 447 } | 449 } |
| 448 return forward_progress; | 450 return forward_progress; |
| 449 } | 451 } |
| 450 | 452 |
| 451 bool ConflictResolver::ResolveConflicts(const ScopedDirLookup& dir, | 453 bool ConflictResolver::ResolveConflicts(const ScopedDirLookup& dir, |
| 452 ConflictResolutionView* view, | 454 StatusController* status) { |
| 453 SyncerSession* session) { | 455 ConflictProgress const* progress = status->conflict_progress(); |
| 454 bool rv = false; | 456 bool rv = false; |
| 455 if (ResolveSimpleConflicts(dir, view, session)) | 457 if (ResolveSimpleConflicts(dir, status)) |
| 456 rv = true; | 458 rv = true; |
| 457 WriteTransaction trans(dir, syncable::SYNCER, __FILE__, __LINE__); | 459 WriteTransaction trans(dir, syncable::SYNCER, __FILE__, __LINE__); |
| 458 set<Id> children_of_dirs_merged_last_round; | 460 set<Id> children_of_dirs_merged_last_round; |
| 459 std::swap(children_of_merged_dirs_, children_of_dirs_merged_last_round); | 461 std::swap(children_of_merged_dirs_, children_of_dirs_merged_last_round); |
| 460 set<ConflictSet*>::const_iterator set_it; | 462 set<ConflictSet*>::const_iterator set_it; |
| 461 for (set_it = view->ConflictSetsBegin(); | 463 for (set_it = progress->ConflictSetsBegin(); |
| 462 set_it != view->ConflictSetsEnd(); | 464 set_it != progress->ConflictSetsEnd(); |
| 463 set_it++) { | 465 set_it++) { |
| 464 ConflictSet* conflict_set = *set_it; | 466 ConflictSet* conflict_set = *set_it; |
| 465 ConflictSetCountMapKey key = GetSetKey(conflict_set); | 467 ConflictSetCountMapKey key = GetSetKey(conflict_set); |
| 466 conflict_set_count_map_[key] += 2; | 468 conflict_set_count_map_[key] += 2; |
| 467 int conflict_count = conflict_set_count_map_[key]; | 469 int conflict_count = conflict_set_count_map_[key]; |
| 468 // Keep a metric for new sets. | 470 // Keep a metric for new sets. |
| 469 if (2 == conflict_count) { | 471 if (2 == conflict_count) { |
| 470 // METRIC conflict sets seen ++ | 472 // METRIC conflict sets seen ++ |
| 471 } | 473 } |
| 472 // See if this set contains entries whose parents were merged last round. | 474 // See if this set contains entries whose parents were merged last round. |
| 473 if (SortedCollectionsIntersect(children_of_dirs_merged_last_round.begin(), | 475 if (SortedCollectionsIntersect(children_of_dirs_merged_last_round.begin(), |
| 474 children_of_dirs_merged_last_round.end(), | 476 children_of_dirs_merged_last_round.end(), |
| 475 conflict_set->begin(), | 477 conflict_set->begin(), |
| 476 conflict_set->end())) { | 478 conflict_set->end())) { |
| 477 LOG(INFO) << "Accelerating resolution for hierarchical merge."; | 479 LOG(INFO) << "Accelerating resolution for hierarchical merge."; |
| 478 conflict_count += 2; | 480 conflict_count += 2; |
| 479 } | 481 } |
| 480 // See if we should process this set. | 482 // See if we should process this set. |
| 481 if (ProcessConflictSet(&trans, conflict_set, conflict_count, session)) { | 483 if (ProcessConflictSet(&trans, conflict_set, conflict_count)) { |
| 482 rv = true; | 484 rv = true; |
| 483 } | 485 } |
| 484 LogAndSignalIfConflictStuck(&trans, conflict_count, | 486 LogAndSignalIfConflictStuck(&trans, conflict_count, |
| 485 conflict_set->begin(), | 487 conflict_set->begin(), |
| 486 conflict_set->end(), view); | 488 conflict_set->end(), status); |
| 487 } | 489 } |
| 488 if (rv) { | 490 if (rv) { |
| 489 // This code means we don't signal that syncing is stuck when any conflict | 491 // This code means we don't signal that syncing is stuck when any conflict |
| 490 // resolution has occured. | 492 // resolution has occured. |
| 491 // TODO(sync): As this will also reduce our sensitivity to problem | 493 // TODO(sync): As this will also reduce our sensitivity to problem |
| 492 // conditions and increase the time for cascading resolutions we may have to | 494 // conditions and increase the time for cascading resolutions we may have to |
| 493 // revisit this code later, doing something more intelligent. | 495 // revisit this code later, doing something more intelligent. |
| 494 conflict_set_count_map_.clear(); | 496 conflict_set_count_map_.clear(); |
| 495 simple_conflict_count_map_.clear(); | 497 simple_conflict_count_map_.clear(); |
| 496 } | 498 } |
| 497 ConflictSetCountMap::iterator i = conflict_set_count_map_.begin(); | 499 ConflictSetCountMap::iterator i = conflict_set_count_map_.begin(); |
| 498 while (i != conflict_set_count_map_.end()) { | 500 while (i != conflict_set_count_map_.end()) { |
| 499 if (0 == --i->second) { | 501 if (0 == --i->second) { |
| 500 conflict_set_count_map_.erase(i++); | 502 conflict_set_count_map_.erase(i++); |
| 501 // METRIC self resolved conflict sets ++. | 503 // METRIC self resolved conflict sets ++. |
| 502 } else { | 504 } else { |
| 503 ++i; | 505 ++i; |
| 504 } | 506 } |
| 505 } | 507 } |
| 506 return rv; | 508 return rv; |
| 507 } | 509 } |
| 508 | 510 |
| 509 } // namespace browser_sync | 511 } // namespace browser_sync |
| OLD | NEW |