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

Side by Side Diff: chrome/browser/sync/engine/conflict_resolver.cc

Issue 371029: Remove unique naming. (Closed)
Patch Set: Ready and about to go in! Created 11 years, 1 month 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 unified diff | Download patch
OLDNEW
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/syncable/directory_manager.h" 13 #include "chrome/browser/sync/syncable/directory_manager.h"
14 #include "chrome/browser/sync/syncable/syncable.h" 14 #include "chrome/browser/sync/syncable/syncable.h"
15 #include "chrome/browser/sync/util/character_set_converters.h" 15 #include "chrome/browser/sync/util/character_set_converters.h"
16 #include "chrome/browser/sync/util/event_sys-inl.h" 16 #include "chrome/browser/sync/util/event_sys-inl.h"
17 #include "chrome/browser/sync/util/path_helpers.h" 17 #include "chrome/browser/sync/util/path_helpers.h"
18 18
19 using std::map; 19 using std::map;
20 using std::set; 20 using std::set;
21 using syncable::BaseTransaction; 21 using syncable::BaseTransaction;
22 using syncable::Directory; 22 using syncable::Directory;
23 using syncable::Entry; 23 using syncable::Entry;
24 using syncable::Id; 24 using syncable::Id;
25 using syncable::MutableEntry; 25 using syncable::MutableEntry;
26 using syncable::Name;
27 using syncable::ScopedDirLookup; 26 using syncable::ScopedDirLookup;
28 using syncable::SyncName;
29 using syncable::WriteTransaction; 27 using syncable::WriteTransaction;
30 28
31 namespace browser_sync { 29 namespace browser_sync {
32 30
33 const int SYNC_CYCLES_BEFORE_ADMITTING_DEFEAT = 8; 31 const int SYNC_CYCLES_BEFORE_ADMITTING_DEFEAT = 8;
34 32
35 ConflictResolver::ConflictResolver() { 33 ConflictResolver::ConflictResolver() {
36 } 34 }
37 35
38 ConflictResolver::~ConflictResolver() { 36 ConflictResolver::~ConflictResolver() {
39 } 37 }
40 38
41 namespace {
42 // TODO(ncarter): Remove title/path conflicts and the code to resolve them.
43 // This is historical cruft that seems to be actually reached by some users.
44 inline PathString GetConflictPathnameBase(PathString base) {
45 time_t time_since = time(NULL);
46 struct tm* now = localtime(&time_since);
47 // Use a fixed format as the locale's format may include '/' characters or
48 // other illegal characters.
49 PathString date = IntToPathString(now->tm_year + 1900);
50 date.append(PSTR("-"));
51 ++now->tm_mon; // tm_mon is 0-based.
52 if (now->tm_mon < 10)
53 date.append(PSTR("0"));
54 date.append(IntToPathString(now->tm_mon));
55 date.append(PSTR("-"));
56 if (now->tm_mday < 10)
57 date.append(PSTR("0"));
58 date.append(IntToPathString(now->tm_mday));
59 return base + PSTR(" (Edited on ") + date + PSTR(")");
60 }
61
62 // TODO(ncarter): Remove title/path conflicts and the code to resolve them.
63 Name FindNewName(BaseTransaction* trans,
64 Id parent_id,
65 const SyncName& original_name) {
66 const PathString name = original_name.value();
67 // 255 is defined in our spec.
68 const size_t allowed_length = kSyncProtocolMaxNameLengthBytes;
69 // TODO(sync): How do we get length on other platforms? The limit is
70 // checked in java on the server, so it's not the number of glyphs its the
71 // number of 16 bit characters in the UTF-16 representation.
72
73 // 10 characters for 32 bit numbers + 2 characters for brackets means 12
74 // characters should be more than enough for the name. Doubling this ensures
75 // that we will have enough space.
76 COMPILE_ASSERT(kSyncProtocolMaxNameLengthBytes >= 24,
77 maximum_name_too_short);
78 CHECK(name.length() <= allowed_length);
79
80 if (!Entry(trans,
81 syncable::GET_BY_PARENTID_AND_DBNAME,
82 parent_id,
83 name).good())
84 return Name::FromSyncName(original_name);
85 PathString base = name;
86 PathString ext;
87 PathString::size_type ext_index = name.rfind('.');
88 if (PathString::npos != ext_index && 0 != ext_index &&
89 name.length() - ext_index < allowed_length / 2) {
90 base = name.substr(0, ext_index);
91 ext = name.substr(ext_index);
92 }
93
94 PathString name_base = GetConflictPathnameBase(base);
95 if (name_base.length() + ext.length() > allowed_length) {
96 name_base.resize(allowed_length - ext.length());
97 TrimPathStringToValidCharacter(&name_base);
98 }
99 PathString new_name = name_base + ext;
100 int n = 2;
101 while (Entry(trans,
102 syncable::GET_BY_PARENTID_AND_DBNAME,
103 parent_id,
104 new_name).good()) {
105 PathString local_ext = PSTR("(");
106 local_ext.append(IntToPathString(n));
107 local_ext.append(PSTR(")"));
108 local_ext.append(ext);
109 if (name_base.length() + local_ext.length() > allowed_length) {
110 name_base.resize(allowed_length - local_ext.length());
111 TrimPathStringToValidCharacter(&name_base);
112 }
113 new_name = name_base + local_ext;
114 n++;
115 }
116
117 CHECK(new_name.length() <= kSyncProtocolMaxNameLengthBytes);
118 return Name(new_name);
119 }
120
121 } // namespace
122
123 void ConflictResolver::IgnoreLocalChanges(MutableEntry* entry) { 39 void ConflictResolver::IgnoreLocalChanges(MutableEntry* entry) {
124 // An update matches local actions, merge the changes. 40 // An update matches local actions, merge the changes.
125 // This is a little fishy because we don't actually merge them. 41 // This is a little fishy because we don't actually merge them.
126 // In the future we should do a 3-way merge. 42 // In the future we should do a 3-way merge.
127 LOG(INFO) << "Server and local changes match, merging:" << entry; 43 LOG(INFO) << "Server and local changes match, merging:" << entry;
128 // With IS_UNSYNCED false, changes should be merged. 44 // With IS_UNSYNCED false, changes should be merged.
129 // METRIC simple conflict resolved by merge. 45 // METRIC simple conflict resolved by merge.
130 entry->Put(syncable::IS_UNSYNCED, false); 46 entry->Put(syncable::IS_UNSYNCED, false);
131 } 47 }
132 48
(...skipping 11 matching lines...) Expand all
144 60
145 ConflictResolver::ProcessSimpleConflictResult 61 ConflictResolver::ProcessSimpleConflictResult
146 ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans, 62 ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans,
147 Id id, 63 Id id,
148 SyncerSession* session) { 64 SyncerSession* session) {
149 MutableEntry entry(trans, syncable::GET_BY_ID, id); 65 MutableEntry entry(trans, syncable::GET_BY_ID, id);
150 // Must be good as the entry won't have been cleaned up. 66 // Must be good as the entry won't have been cleaned up.
151 CHECK(entry.good()); 67 CHECK(entry.good());
152 // If an update fails, locally we have to be in a set or unsynced. We're not 68 // If an update fails, locally we have to be in a set or unsynced. We're not
153 // in a set here, so we must be unsynced. 69 // in a set here, so we must be unsynced.
154 if (!entry.Get(syncable::IS_UNSYNCED)) 70 if (!entry.Get(syncable::IS_UNSYNCED)) {
155 return NO_SYNC_PROGRESS; 71 return NO_SYNC_PROGRESS;
72 }
73
156 if (!entry.Get(syncable::IS_UNAPPLIED_UPDATE)) { 74 if (!entry.Get(syncable::IS_UNAPPLIED_UPDATE)) {
157 if (!entry.Get(syncable::PARENT_ID).ServerKnows()) { 75 if (!entry.Get(syncable::PARENT_ID).ServerKnows()) {
158 LOG(INFO) << "Item conflicting because its parent not yet committed. " 76 LOG(INFO) << "Item conflicting because its parent not yet committed. "
159 "Id: "<< id; 77 "Id: "<< id;
160 } else { 78 } else {
161 LOG(INFO) << "No set for conflicting entry id " << id << ". There should " 79 LOG(INFO) << "No set for conflicting entry id " << id << ". There should "
162 "be an update/commit that will fix this soon. This message should " 80 "be an update/commit that will fix this soon. This message should "
163 "not repeat."; 81 "not repeat.";
164 } 82 }
165 return NO_SYNC_PROGRESS; 83 return NO_SYNC_PROGRESS;
166 } 84 }
85
167 if (entry.Get(syncable::IS_DEL) && entry.Get(syncable::SERVER_IS_DEL)) { 86 if (entry.Get(syncable::IS_DEL) && entry.Get(syncable::SERVER_IS_DEL)) {
168 // we've both deleted it, so lets just drop the need to commit/update this 87 // we've both deleted it, so lets just drop the need to commit/update this
169 // entry. 88 // entry.
170 entry.Put(syncable::IS_UNSYNCED, false); 89 entry.Put(syncable::IS_UNSYNCED, false);
171 entry.Put(syncable::IS_UNAPPLIED_UPDATE, false); 90 entry.Put(syncable::IS_UNAPPLIED_UPDATE, false);
172 // we've made changes, but they won't help syncing progress. 91 // we've made changes, but they won't help syncing progress.
173 // METRIC simple conflict resolved by merge. 92 // METRIC simple conflict resolved by merge.
174 return NO_SYNC_PROGRESS; 93 return NO_SYNC_PROGRESS;
175 } 94 }
176 95
177 if (!entry.Get(syncable::SERVER_IS_DEL)) { 96 if (!entry.Get(syncable::SERVER_IS_DEL)) {
178 // TODO(chron): Should we check more fields? Since IS_UNSYNCED is 97 // TODO(chron): Should we check more fields? Since IS_UNSYNCED is
179 // turned on, this is really probably enough as fields will be overwritten. 98 // turned on, this is really probably enough as fields will be overwritten.
180 // Check if there's no changes. 99 // Check if there's no changes.
181 100
182 // Verbose but easier to debug. 101 // Verbose but easier to debug.
183 bool name_matches = entry.SyncNameMatchesServerName(); 102 bool name_matches = entry.Get(syncable::NON_UNIQUE_NAME) ==
103 entry.Get(syncable::SERVER_NON_UNIQUE_NAME);
184 bool parent_matches = entry.Get(syncable::PARENT_ID) == 104 bool parent_matches = entry.Get(syncable::PARENT_ID) ==
185 entry.Get(syncable::SERVER_PARENT_ID); 105 entry.Get(syncable::SERVER_PARENT_ID);
186 bool entry_deleted = entry.Get(syncable::IS_DEL); 106 bool entry_deleted = entry.Get(syncable::IS_DEL);
187 107
188 if (!entry_deleted && name_matches && parent_matches) { 108 if (!entry_deleted && name_matches && parent_matches) {
189 LOG(INFO) << "Resolving simple conflict, ignoring local changes for:" 109 LOG(INFO) << "Resolving simple conflict, ignoring local changes for:"
190 << entry; 110 << entry;
191 IgnoreLocalChanges(&entry); 111 IgnoreLocalChanges(&entry);
192 } else { 112 } else {
193 LOG(INFO) << "Resolving simple conflict, overwriting server" 113 LOG(INFO) << "Resolving simple conflict, overwriting server"
194 " changes for:" << entry; 114 " changes for:" << entry;
195 OverwriteServerChanges(trans, &entry); 115 OverwriteServerChanges(trans, &entry);
196 } 116 }
197 return SYNC_PROGRESS; 117 return SYNC_PROGRESS;
198 } else { // SERVER_IS_DEL is true 118 } else { // SERVER_IS_DEL is true
199 // If a server deleted folder has local contents we should be in a set. 119 // If a server deleted folder has local contents we should be in a set.
200 if (entry.Get(syncable::IS_DIR)) { 120 if (entry.Get(syncable::IS_DIR)) {
201 Directory::ChildHandles children; 121 Directory::ChildHandles children;
202 trans->directory()->GetChildHandles(trans, 122 trans->directory()->GetChildHandles(trans,
203 entry.Get(syncable::ID), 123 entry.Get(syncable::ID),
204 &children); 124 &children);
205 if (0 != children.size()) { 125 if (0 != children.size()) {
206 LOG(INFO) << "Entry is a server deleted directory with local contents, " 126 LOG(INFO) << "Entry is a server deleted directory with local contents, "
207 "should be in a set. (race condition)."; 127 "should be in a set. (race condition).";
208 return NO_SYNC_PROGRESS; 128 return NO_SYNC_PROGRESS;
209 } 129 }
210 } 130 }
211 // METRIC conflict resolved by entry split;
212 131
213 // If the entry's deleted on the server, we can have a directory here. 132 // If the entry's deleted on the server, we can have a directory here.
214 entry.Put(syncable::IS_UNSYNCED, true); 133 entry.Put(syncable::IS_UNSYNCED, true);
215 134
216 SyncerUtil::SplitServerInformationIntoNewEntry(trans, &entry); 135 SyncerUtil::SplitServerInformationIntoNewEntry(trans, &entry);
217 136
218 MutableEntry server_update(trans, syncable::GET_BY_ID, id); 137 MutableEntry server_update(trans, syncable::GET_BY_ID, id);
219 CHECK(server_update.good()); 138 CHECK(server_update.good());
220 CHECK(server_update.Get(syncable::META_HANDLE) != 139 CHECK(server_update.Get(syncable::META_HANDLE) !=
221 entry.Get(syncable::META_HANDLE)) 140 entry.Get(syncable::META_HANDLE))
222 << server_update << entry; 141 << server_update << entry;
223 142
224 return SYNC_PROGRESS; 143 return SYNC_PROGRESS;
225 } 144 }
226 } 145 }
227 146
228 namespace {
229
230 bool NamesCollideWithChildrenOfFolder(BaseTransaction* trans,
231 const Directory::ChildHandles& children,
232 Id folder_id) {
233 Directory::ChildHandles::const_iterator i = children.begin();
234 while (i != children.end()) {
235 Entry child(trans, syncable::GET_BY_HANDLE, *i);
236 CHECK(child.good());
237 if (Entry(trans,
238 syncable::GET_BY_PARENTID_AND_DBNAME,
239 folder_id,
240 child.GetName().db_value()).good())
241 return true;
242 ++i;
243 }
244 return false;
245 }
246
247 void GiveEntryNewName(WriteTransaction* trans, MutableEntry* entry) {
248 using namespace syncable;
249 Name new_name =
250 FindNewName(trans, entry->Get(syncable::PARENT_ID), entry->GetName());
251 LOG(INFO) << "Resolving name clash, renaming " << *entry << " to "
252 << new_name.db_value();
253 entry->PutName(new_name);
254 CHECK(entry->Get(syncable::IS_UNSYNCED));
255 }
256
257 } // namespace
258
259 bool ConflictResolver::AttemptItemMerge(WriteTransaction* trans,
260 MutableEntry* locally_named,
261 MutableEntry* server_named) {
262 // To avoid complications we only merge new entries with server entries.
263 if (locally_named->Get(syncable::IS_DIR) !=
264 server_named->Get(syncable::SERVER_IS_DIR) ||
265 locally_named->Get(syncable::ID).ServerKnows() ||
266 locally_named->Get(syncable::IS_UNAPPLIED_UPDATE) ||
267 server_named->Get(syncable::IS_UNSYNCED))
268 return false;
269 Id local_id = locally_named->Get(syncable::ID);
270 Id desired_id = server_named->Get(syncable::ID);
271 if (locally_named->Get(syncable::IS_DIR)) {
272 // Extra work for directory name clash. We have to make sure we don't have
273 // clashing child items, and update the parent id the children of the new
274 // entry.
275 Directory::ChildHandles children;
276 trans->directory()->GetChildHandles(trans, local_id, &children);
277 if (NamesCollideWithChildrenOfFolder(trans, children, desired_id))
278 return false;
279
280 LOG(INFO) << "Merging local changes to: " << desired_id << ". "
281 << *locally_named;
282
283 server_named->Put(syncable::ID, trans->directory()->NextId());
284 Directory::ChildHandles::iterator i;
285 for (i = children.begin() ; i != children.end() ; ++i) {
286 MutableEntry child_entry(trans, syncable::GET_BY_HANDLE, *i);
287 CHECK(child_entry.good());
288 CHECK(child_entry.Put(syncable::PARENT_ID, desired_id));
289 CHECK(child_entry.Put(syncable::IS_UNSYNCED, true));
290 Id id = child_entry.Get(syncable::ID);
291 // We only note new entries for quicker merging next round.
292 if (!id.ServerKnows())
293 children_of_merged_dirs_.insert(id);
294 }
295 } else {
296 if (!server_named->Get(syncable::IS_DEL))
297 return false;
298 }
299
300 LOG(INFO) << "Identical client and server items merging server changes. " <<
301 *locally_named << " server: " << *server_named;
302
303 // Clear server_named's server data and mark it deleted so it goes away
304 // quietly because it's now identical to a deleted local entry.
305 // locally_named takes on the ID of the server entry.
306 server_named->Put(syncable::ID, trans->directory()->NextId());
307 locally_named->Put(syncable::ID, desired_id);
308 locally_named->Put(syncable::IS_UNSYNCED, false);
309 CopyServerFields(server_named, locally_named);
310 ClearServerData(server_named);
311 server_named->Put(syncable::IS_DEL, true);
312 server_named->Put(syncable::BASE_VERSION, 0);
313 CHECK(SUCCESS ==
314 SyncerUtil::AttemptToUpdateEntryWithoutMerge(
315 trans, locally_named, NULL));
316 return true;
317 }
318
319 ConflictResolver::ServerClientNameClashReturn
320 ConflictResolver::ProcessServerClientNameClash(WriteTransaction* trans,
321 MutableEntry* locally_named,
322 MutableEntry* server_named,
323 SyncerSession* session) {
324 if (!locally_named->ExistsOnClientBecauseDatabaseNameIsNonEmpty())
325 return NO_CLASH; // Locally_named is a server update.
326 if (locally_named->Get(syncable::IS_DEL) ||
327 server_named->Get(syncable::SERVER_IS_DEL)) {
328 return NO_CLASH;
329 }
330 if (locally_named->Get(syncable::PARENT_ID) !=
331 server_named->Get(syncable::SERVER_PARENT_ID)) {
332 return NO_CLASH; // Different parents.
333 }
334
335 PathString name = locally_named->GetSyncNameValue();
336 if (0 != syncable::ComparePathNames(name,
337 server_named->Get(syncable::SERVER_NAME))) {
338 return NO_CLASH; // Different names.
339 }
340
341 // First try to merge.
342 if (AttemptItemMerge(trans, locally_named, server_named)) {
343 // METRIC conflict resolved by merge
344 return SOLVED;
345 }
346 // We need to rename.
347 if (!locally_named->Get(syncable::IS_UNSYNCED)) {
348 LOG(ERROR) << "Locally named part of a name conflict not unsynced?";
349 locally_named->Put(syncable::IS_UNSYNCED, true);
350 }
351 if (!server_named->Get(syncable::IS_UNAPPLIED_UPDATE)) {
352 LOG(ERROR) << "Server named part of a name conflict not an update?";
353 }
354 GiveEntryNewName(trans, locally_named);
355
356 // METRIC conflict resolved by rename
357 return SOLVED;
358 }
359
360 ConflictResolver::ServerClientNameClashReturn
361 ConflictResolver::ProcessNameClashesInSet(WriteTransaction* trans,
362 ConflictSet* conflict_set,
363 SyncerSession* session) {
364 ConflictSet::const_iterator i,j;
365 for (i = conflict_set->begin() ; i != conflict_set->end() ; ++i) {
366 MutableEntry entryi(trans, syncable::GET_BY_ID, *i);
367 if (!entryi.Get(syncable::IS_UNSYNCED) &&
368 !entryi.Get(syncable::IS_UNAPPLIED_UPDATE))
369 // This set is broken / doesn't make sense, this may be transient.
370 return BOGUS_SET;
371 for (j = conflict_set->begin() ; *i != *j ; ++j) {
372 MutableEntry entryj(trans, syncable::GET_BY_ID, *j);
373 ServerClientNameClashReturn rv =
374 ProcessServerClientNameClash(trans, &entryi, &entryj, session);
375 if (NO_CLASH == rv)
376 rv = ProcessServerClientNameClash(trans, &entryj, &entryi, session);
377 if (NO_CLASH != rv)
378 return rv;
379 }
380 }
381 return NO_CLASH;
382 }
383
384 ConflictResolver::ConflictSetCountMapKey ConflictResolver::GetSetKey( 147 ConflictResolver::ConflictSetCountMapKey ConflictResolver::GetSetKey(
385 ConflictSet* set) { 148 ConflictSet* set) {
386 // TODO(sync): Come up with a better scheme for set hashing. This scheme 149 // TODO(sync): Come up with a better scheme for set hashing. This scheme
387 // will make debugging easy. 150 // will make debugging easy.
388 // If this call to sort is removed, we need to add one before we use 151 // If this call to sort is removed, we need to add one before we use
389 // binary_search in ProcessConflictSet. 152 // binary_search in ProcessConflictSet.
390 sort(set->begin(), set->end()); 153 sort(set->begin(), set->end());
391 std::stringstream rv; 154 std::stringstream rv;
392 for (ConflictSet::iterator i = set->begin() ; i != set->end() ; ++i ) 155 for (ConflictSet::iterator i = set->begin() ; i != set->end() ; ++i )
393 rv << *i << "."; 156 rv << *i << ".";
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
474 SyncerUtil::ChangeEntryIDAndUpdateChildren(trans, &parent, 237 SyncerUtil::ChangeEntryIDAndUpdateChildren(trans, &parent,
475 trans->directory()->NextId()); 238 trans->directory()->NextId());
476 parent.Put(syncable::BASE_VERSION, 0); 239 parent.Put(syncable::BASE_VERSION, 0);
477 parent.Put(syncable::IS_UNSYNCED, true); 240 parent.Put(syncable::IS_UNSYNCED, true);
478 id = parent.Get(syncable::PARENT_ID); 241 id = parent.Get(syncable::PARENT_ID);
479 // METRIC conflict resolved by recreating dir tree. 242 // METRIC conflict resolved by recreating dir tree.
480 } 243 }
481 return true; 244 return true;
482 } 245 }
483 246
247
248 // TODO(chron): needs unit test badly
484 bool AttemptToFixUpdateEntryInDeletedLocalTree(WriteTransaction* trans, 249 bool AttemptToFixUpdateEntryInDeletedLocalTree(WriteTransaction* trans,
485 ConflictSet* conflict_set, 250 ConflictSet* conflict_set,
486 const Entry& entry) { 251 const Entry& entry) {
487 if (!entry.Get(syncable::IS_UNAPPLIED_UPDATE) || 252 if (!entry.Get(syncable::IS_UNAPPLIED_UPDATE) ||
488 entry.Get(syncable::SERVER_IS_DEL)) 253 entry.Get(syncable::SERVER_IS_DEL))
489 return false; 254 return false;
490 Id parent_id = entry.Get(syncable::SERVER_PARENT_ID); 255 Id parent_id = entry.Get(syncable::SERVER_PARENT_ID);
491 MutableEntry parent(trans, syncable::GET_BY_ID, parent_id); 256 MutableEntry parent(trans, syncable::GET_BY_ID, parent_id);
492 if (!parent.good() || !parent.Get(syncable::IS_DEL) || 257 if (!parent.good() || !parent.Get(syncable::IS_DEL) ||
493 !binary_search(conflict_set->begin(), conflict_set->end(), parent_id)) { 258 !binary_search(conflict_set->begin(), conflict_set->end(), parent_id)) {
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
533 // If we find we've been looping we re-root the hierarchy. 298 // If we find we've been looping we re-root the hierarchy.
534 if (loop_detection < 0) { 299 if (loop_detection < 0) {
535 if (id == entry.Get(syncable::ID)) 300 if (id == entry.Get(syncable::ID))
536 reroot_id = entry.Get(syncable::PARENT_ID); 301 reroot_id = entry.Get(syncable::PARENT_ID);
537 else 302 else
538 reroot_id = id; 303 reroot_id = id;
539 } 304 }
540 // Now we fix things up by undeleting all the folders in the item's path. 305 // Now we fix things up by undeleting all the folders in the item's path.
541 id = parent_id; 306 id = parent_id;
542 while (!id.IsRoot() && id != reroot_id) { 307 while (!id.IsRoot() && id != reroot_id) {
543 if (!binary_search(conflict_set->begin(), conflict_set->end(), id)) 308 if (!binary_search(conflict_set->begin(), conflict_set->end(), id)) {
544 break; 309 break;
310 }
545 MutableEntry entry(trans, syncable::GET_BY_ID, id); 311 MutableEntry entry(trans, syncable::GET_BY_ID, id);
312
313 LOG(INFO) << "Undoing our deletion of " << entry
314 << ", will have name " << entry.Get(syncable::NON_UNIQUE_NAME);
315
546 Id parent_id = entry.Get(syncable::PARENT_ID); 316 Id parent_id = entry.Get(syncable::PARENT_ID);
547 if (parent_id == reroot_id) 317 if (parent_id == reroot_id) {
548 parent_id = trans->root_id(); 318 parent_id = trans->root_id();
549 Name old_name = entry.GetName(); 319 }
550 Name new_name = FindNewName(trans, parent_id, old_name); 320 entry.Put(syncable::PARENT_ID, parent_id);
551 LOG(INFO) << "Undoing our deletion of " << entry <<
552 ", will have name " << new_name.db_value();
553 if (new_name != old_name || parent_id != entry.Get(syncable::PARENT_ID))
554 CHECK(entry.PutParentIdAndName(parent_id, new_name));
555 entry.Put(syncable::IS_DEL, false); 321 entry.Put(syncable::IS_DEL, false);
556 id = entry.Get(syncable::PARENT_ID); 322 id = entry.Get(syncable::PARENT_ID);
557 // METRIC conflict resolved by recreating dir tree. 323 // METRIC conflict resolved by recreating dir tree.
558 } 324 }
559 return true; 325 return true;
560 } 326 }
561 327
562 bool AttemptToFixRemovedDirectoriesWithContent(WriteTransaction* trans, 328 bool AttemptToFixRemovedDirectoriesWithContent(WriteTransaction* trans,
563 ConflictSet* conflict_set) { 329 ConflictSet* conflict_set) {
564 ConflictSet::const_iterator i,j; 330 ConflictSet::const_iterator i,j;
565 for (i = conflict_set->begin() ; i != conflict_set->end() ; ++i) { 331 for (i = conflict_set->begin() ; i != conflict_set->end() ; ++i) {
566 Entry entry(trans, syncable::GET_BY_ID, *i); 332 Entry entry(trans, syncable::GET_BY_ID, *i);
567 if (AttemptToFixUnsyncedEntryInDeletedServerTree(trans, 333 if (AttemptToFixUnsyncedEntryInDeletedServerTree(trans,
568 conflict_set, entry)) { 334 conflict_set, entry)) {
569 return true; 335 return true;
570 } 336 }
571 if (AttemptToFixUpdateEntryInDeletedLocalTree(trans, conflict_set, entry)) 337 if (AttemptToFixUpdateEntryInDeletedLocalTree(trans, conflict_set, entry))
572 return true; 338 return true;
573 } 339 }
574 return false; 340 return false;
575 } 341 }
576 342
577 } // namespace 343 } // namespace
578 344
345 // TODO(sync): Eliminate conflict sets. They're not necessary.
579 bool ConflictResolver::ProcessConflictSet(WriteTransaction* trans, 346 bool ConflictResolver::ProcessConflictSet(WriteTransaction* trans,
580 ConflictSet* conflict_set, 347 ConflictSet* conflict_set,
581 int conflict_count, 348 int conflict_count,
582 SyncerSession* session) { 349 SyncerSession* session) {
583 int set_size = conflict_set->size(); 350 int set_size = conflict_set->size();
584 if (set_size < 2) { 351 if (set_size < 2) {
585 LOG(WARNING) << "Skipping conflict set because it has size " << set_size; 352 LOG(WARNING) << "Skipping conflict set because it has size " << set_size;
586 // We can end up with sets of size one if we have a new item in a set that 353 // We can end up with sets of size one if we have a new item in a set that
587 // we tried to commit transactionally. This should not be a persistent 354 // we tried to commit transactionally. This should not be a persistent
588 // situation. 355 // situation.
589 return false; 356 return false;
590 } 357 }
591 if (conflict_count < 3) { 358 if (conflict_count < 3) {
592 // Avoid resolving sets that could be the result of transient conflicts. 359 // Avoid resolving sets that could be the result of transient conflicts.
593 // Transient conflicts can occur because the client or server can be 360 // Transient conflicts can occur because the client or server can be
594 // slightly out of date. 361 // slightly out of date.
595 return false; 362 return false;
596 } 363 }
597 364
598 LOG(INFO) << "Fixing a set containing " << set_size << " items"; 365 LOG(INFO) << "Fixing a set containing " << set_size << " items";
599 366
600 ServerClientNameClashReturn rv = ProcessNameClashesInSet(trans, conflict_set,
601 session);
602 if (SOLVED == rv)
603 return true;
604 if (NO_CLASH != rv)
605 return false;
606
607 // Fix circular conflicts. 367 // Fix circular conflicts.
608 if (AttemptToFixCircularConflict(trans, conflict_set)) 368 if (AttemptToFixCircularConflict(trans, conflict_set))
609 return true; 369 return true;
610 // Check for problems involving contents of removed folders. 370 // Check for problems involving contents of removed folders.
611 if (AttemptToFixRemovedDirectoriesWithContent(trans, conflict_set)) 371 if (AttemptToFixRemovedDirectoriesWithContent(trans, conflict_set))
612 return true; 372 return true;
613 return false; 373 return false;
614 } 374 }
615 375
616 template <typename InputIt> 376 template <typename InputIt>
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
740 conflict_set_count_map_.erase(i++); 500 conflict_set_count_map_.erase(i++);
741 // METRIC self resolved conflict sets ++. 501 // METRIC self resolved conflict sets ++.
742 } else { 502 } else {
743 ++i; 503 ++i;
744 } 504 }
745 } 505 }
746 return rv; 506 return rv;
747 } 507 }
748 508
749 } // namespace browser_sync 509 } // namespace browser_sync
OLDNEW
« no previous file with comments | « chrome/browser/sync/engine/conflict_resolver.h ('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