Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(397)

Unified Diff: chrome/browser/sync/engine/conflict_resolver.cc

Issue 8922015: [Sync] Don't commit items with predecessors/parents in conflict. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Ensure we clear BASE_SERVER_SPECIFICS Created 9 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/sync/engine/conflict_resolver.cc
diff --git a/chrome/browser/sync/engine/conflict_resolver.cc b/chrome/browser/sync/engine/conflict_resolver.cc
index cca0c31d8939b42b18b9cd0db87ec8e9c1230eb8..b596a9a7204565e27d87bee444a1689449f7df87 100644
--- a/chrome/browser/sync/engine/conflict_resolver.cc
+++ b/chrome/browser/sync/engine/conflict_resolver.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/sync/engine/conflict_resolver.h"
#include <algorithm>
+#include <list>
#include <map>
#include <set>
@@ -19,6 +20,7 @@
#include "chrome/browser/sync/syncable/syncable.h"
#include "chrome/browser/sync/util/cryptographer.h"
+using std::list;
using std::map;
using std::set;
using syncable::BaseTransaction;
@@ -110,7 +112,7 @@ ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans,
// b) All unsynced changes have been re-encrypted with the default key (
// occurs either in AttemptToUpdateEntry, SetPassphrase, or
// RefreshEncryption).
- // c) Prev_server_specifics having a valid datatype means that we received
+ // c) Base_server_specifics having a valid datatype means that we received
// an undecryptable update that only changed specifics, and since then have
// not received any further non-specifics-only or decryptable updates.
// d) If the server_specifics match specifics, server_specifics are
@@ -123,9 +125,6 @@ ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans,
// f) Otherwise, it's in general safer to ignore local changes, with the
// exception of deletion conflicts (choose to undelete) and conflicts
// where the non_unique_name or parent don't match.
- // TODO(sync): We can't compare position here without performing the
- // NEXT_ID/PREV_ID -> position_in_parent interpolation. Fix that, and take it
- // into account.
if (!entry.Get(syncable::SERVER_IS_DEL)) {
// TODO(nick): The current logic is arbitrary; instead, it ought to be made
// consistent with the ModelAssociator behavior for a datatype. It would
@@ -136,8 +135,22 @@ ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans,
bool name_matches = entry.Get(syncable::NON_UNIQUE_NAME) ==
entry.Get(syncable::SERVER_NON_UNIQUE_NAME);
bool parent_matches = entry.Get(syncable::PARENT_ID) ==
- entry.Get(syncable::SERVER_PARENT_ID);
+ entry.Get(syncable::SERVER_PARENT_ID);
bool entry_deleted = entry.Get(syncable::IS_DEL);
+ // This positional check is conservative. It will likely be false if the
+ // predecessor is unsynced/unapplied. For this reason, we ensure conflicting
+ // predecessors are resolved before their successors. Being
+ // conservative results in being less likely to lose local positioning
+ // changes, with the cost of occasional unnecessary commits.
+ // TODO(zea): change this once we can directly compare server position to
+ // client position.
+ syncable::Id server_prev_id = entry.ComputePrevIdFromServerPosition(
rlarocque 2012/01/10 23:39:51 ComputePrevIdFromServerPosition() will not return
Nicolas Zea 2012/01/18 02:54:39 See my updated comments. Basically, this situation
rlarocque 2012/01/18 20:21:55 It sounds like you're expecting this to return the
Nicolas Zea 2012/01/18 20:40:42 No, I expect it to return the first fully synced i
+ entry.Get(syncable::SERVER_PARENT_ID));
rlarocque 2012/01/10 23:39:51 SERVER_PARENT_ID? Did you mean SERVER_PREV_ID?
Nicolas Zea 2012/01/18 02:54:39 No, you pass the parent to ComputePrevIdFromServer
+ bool position_matches = parent_matches &&
+ (server_prev_id == entry.Get(syncable::PREV_ID));
ncarter (slow) 2012/01/04 21:37:59 The comment about 'conservative' could be read to
Nicolas Zea 2012/01/18 02:54:39 Upon further reflection, the code as stands actual
+ DVLOG_IF(1, !position_matches) << "Position doesn't match, server prev id "
+ << " is " << server_prev_id << ", local prev id is "
+ << entry.Get(syncable::PREV_ID);
const sync_pb::EntitySpecifics& specifics =
entry.Get(syncable::SPECIFICS);
const sync_pb::EntitySpecifics& server_specifics =
@@ -218,15 +231,14 @@ ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans,
NIGORI_MERGE,
CONFLICT_RESOLUTION_SIZE);
} else if (!entry_deleted && name_matches && parent_matches &&
- specifics_match) {
+ position_matches && specifics_match) {
DVLOG(1) << "Resolving simple conflict, everything matches, ignoring "
<< "changes for: " << entry;
// This unsets both IS_UNSYNCED and IS_UNAPPLIED_UPDATE, and sets the
// BASE_VERSION to match the SERVER_VERSION. If we didn't also unset
- // IS_UNAPPLIED_UPDATE, then we would lose unsynced positional data
- // when the server update gets applied and the item is re-inserted into
- // the PREV_ID/NEXT_ID linked list (see above TODO about comparing
- // positional info).
+ // IS_UNAPPLIED_UPDATE, then we would lose unsynced positional data from
+ // adjacent entries when the server update gets applied and the item is
+ // re-inserted into the PREV_ID/NEXT_ID linked list.
OverwriteServerChanges(trans, &entry);
IgnoreLocalChanges(&entry);
UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
@@ -240,9 +252,6 @@ ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans,
UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
IGNORE_ENCRYPTION,
CONFLICT_RESOLUTION_SIZE);
- // Now that we've resolved the conflict, clear the prev server
- // specifics.
- entry.Put(syncable::BASE_SERVER_SPECIFICS, sync_pb::EntitySpecifics());
} else if (entry_deleted || !name_matches || !parent_matches) {
rlarocque 2012/01/10 23:39:51 Assertion: If this condition is true, then we woul
Nicolas Zea 2012/01/18 02:54:39 I'm not sure I understand this assertion. The if s
OverwriteServerChanges(trans, &entry);
status->increment_num_server_overwrites();
@@ -260,6 +269,9 @@ ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans,
OVERWRITE_LOCAL,
CONFLICT_RESOLUTION_SIZE);
}
+ // Now that we've resolved the conflict, clear the prev server
+ // specifics.
+ entry.Put(syncable::BASE_SERVER_SPECIFICS, sync_pb::EntitySpecifics());
return SYNC_PROGRESS;
} else { // SERVER_IS_DEL is true
// If a server deleted folder has local contents we should be in a set.
@@ -318,21 +330,46 @@ bool ConflictResolver::ResolveSimpleConflicts(
bool forward_progress = false;
// First iterate over simple conflict items (those that belong to no set).
set<Id>::const_iterator conflicting_item_it;
+ set<Id> processed_items;
for (conflicting_item_it = progress.ConflictingItemsBegin();
conflicting_item_it != progress.ConflictingItemsEnd();
++conflicting_item_it) {
Id id = *conflicting_item_it;
+ if (processed_items.count(id) > 0)
+ continue;
map<Id, ConflictSet*>::const_iterator item_set_it =
progress.IdToConflictSetFind(id);
if (item_set_it == progress.IdToConflictSetEnd() ||
0 == item_set_it->second) {
- // We have a simple conflict.
- switch (ProcessSimpleConflict(trans, id, cryptographer, status)) {
- case NO_SYNC_PROGRESS:
- break;
- case SYNC_PROGRESS:
- forward_progress = true;
+ // We have a simple conflict. In order check if positions have changed,
rlarocque 2012/01/18 20:21:55 I don't understand why this is necessary. I think
Nicolas Zea 2012/01/18 20:40:42 You're right, we don't modify anything except IS_
+ // we need to process conflicting predecessors before successors. Traverse
+ // backwards through all continuous conflicting predecessors, building a
+ // stack of items to resolve in predecessor->successor order, then process
+ // each item individually.
+ list<Id> predecessors;
+ Id prev_id = id;
+ do {
+ predecessors.push_back(prev_id);
+ Entry entry(trans, syncable::GET_BY_ID, prev_id);
+ // Any entry in conflict must be valid.
+ CHECK(entry.good());
+ Id new_prev_id = entry.Get(syncable::PREV_ID);
+ if (new_prev_id == prev_id)
break;
+ prev_id = new_prev_id;
+ } while (processed_items.count(prev_id) == 0 &&
+ progress.HasSimpleConflictItem(prev_id)); // Excludes root.
+ while (!predecessors.empty()) {
+ id = predecessors.back();
+ predecessors.pop_back();
+ switch (ProcessSimpleConflict(trans, id, cryptographer, status)) {
+ case NO_SYNC_PROGRESS:
+ break;
+ case SYNC_PROGRESS:
+ forward_progress = true;
+ break;
+ }
+ processed_items.insert(id);
}
}
}
« no previous file with comments | « chrome/browser/sync/engine/build_commit_command.cc ('k') | chrome/browser/sync/engine/get_commit_ids_command.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698