1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/sync/engine/build_and_process_conflict_sets_command.h" 6 7 #include <set> 8 #include <string> 9 #include <sstream> 10 #include <vector> 11 12 #include "base/basictypes.h" 13 #include "base/format_macros.h" 14 #include "chrome/browser/sync/engine/syncer_util.h" 15 #include "chrome/browser/sync/engine/update_applicator.h" 16 #include "chrome/browser/sync/sessions/sync_session.h" 17 #include "chrome/browser/sync/syncable/directory_manager.h" 18 19 namespace browser_sync { 20 21 using sessions::ConflictProgress; 22 using sessions::StatusController; 23 using sessions::SyncSession; 24 using sessions::UpdateProgress; 25 using std::set; 26 using std::string; 27 using std::vector; 28 29 BuildAndProcessConflictSetsCommand::BuildAndProcessConflictSetsCommand() {} 30 BuildAndProcessConflictSetsCommand::~BuildAndProcessConflictSetsCommand() {} 31 32 void BuildAndProcessConflictSetsCommand::ModelChangingExecuteImpl( 33 SyncSession* session) { 34 session->status_controller()->update_conflict_sets_built( 35 BuildAndProcessConflictSets(session)); 36 } 37 38 bool BuildAndProcessConflictSetsCommand::BuildAndProcessConflictSets( 39 SyncSession* session) { 40 syncable::ScopedDirLookup dir(session->context()->directory_manager(), 41 session->context()->account_name()); 42 if (!dir.good()) 43 return false; 44 bool had_single_direction_sets = false; 45 { // Scope for transaction. 46 syncable::WriteTransaction trans(dir, syncable::SYNCER, __FILE__, __LINE__); 47 BuildConflictSets(&trans, 48 session->status_controller()->mutable_conflict_progress()); 49 had_single_direction_sets = ProcessSingleDirectionConflictSets(&trans, 50 session->context()->resolver(), 51 session->context()->directory_manager()->GetCryptographer(&trans), 52 session->status_controller(), session->routing_info()); 53 // We applied some updates transactionally, lets try syncing again. 54 if (had_single_direction_sets) 55 return true; 56 } 57 return false; 58 } 59 60 bool BuildAndProcessConflictSetsCommand::ProcessSingleDirectionConflictSets( 61 syncable::WriteTransaction* trans, ConflictResolver* resolver, 62 Cryptographer* cryptographer, StatusController* status, 63 const ModelSafeRoutingInfo& routes) { 64 bool rv = false; 65 set<ConflictSet*>::const_iterator all_sets_iterator; 66 for (all_sets_iterator = status->conflict_progress().ConflictSetsBegin(); 67 all_sets_iterator != status->conflict_progress().ConflictSetsEnd();) { 68 const ConflictSet* conflict_set = *all_sets_iterator; 69 CHECK_GE(conflict_set->size(), 2U); 70 // We scan the set to see if it consists of changes of only one type. 71 ConflictSet::const_iterator i; 72 size_t unsynced_count = 0, unapplied_count = 0; 73 for (i = conflict_set->begin(); i != conflict_set->end(); ++i) { 74 syncable::Entry entry(trans, syncable::GET_BY_ID, *i); 75 CHECK(entry.good()); 76 if (entry.Get(syncable::IS_UNSYNCED)) 77 unsynced_count++; 78 if (entry.Get(syncable::IS_UNAPPLIED_UPDATE)) 79 unapplied_count++; 80 } 81 if (conflict_set->size() == unsynced_count && 0 == unapplied_count) { 82 VLOG(1) << "Skipped transactional commit attempt."; 83 } else if (conflict_set->size() == unapplied_count && 0 == unsynced_count && 84 ApplyUpdatesTransactionally(trans, conflict_set, resolver, 85 cryptographer, routes, status)) { 86 rv = true; 87 } 88 ++all_sets_iterator; 89 } 90 return rv; 91 } 92 93 namespace { 94 95 void StoreLocalDataForUpdateRollback(syncable::Entry* entry, 96 syncable::EntryKernel* backup) { 97 CHECK(!entry->Get(syncable::IS_UNSYNCED)) << " Storing Rollback data for " 98 "entry that's unsynced." << *entry; 99 CHECK(entry->Get(syncable::IS_UNAPPLIED_UPDATE)) << " Storing Rollback data " 100 "for entry that's not an unapplied update." << *entry; 101 *backup = entry->GetKernelCopy(); 102 } 103 104 105 bool RollbackEntry(syncable::WriteTransaction* trans, 106 syncable::EntryKernel* backup) { 107 syncable::MutableEntry entry(trans, syncable::GET_BY_HANDLE, 108 backup->ref(syncable::META_HANDLE)); 109 CHECK(entry.good()); 110 111 if (!entry.Put(syncable::IS_DEL, backup->ref(syncable::IS_DEL))) 112 return false; 113 114 entry.Put(syncable::NON_UNIQUE_NAME, backup->ref(syncable::NON_UNIQUE_NAME)); 115 entry.Put(syncable::PARENT_ID, backup->ref(syncable::PARENT_ID)); 116 117 if (!backup->ref(syncable::IS_DEL)) { 118 if (!entry.PutPredecessor(backup->ref(syncable::PREV_ID))) 119 return false; 120 } 121 122 if (backup->ref(syncable::PREV_ID) != entry.Get(syncable::PREV_ID)) 123 return false; 124 125 entry.Put(syncable::CTIME, backup->ref(syncable::CTIME)); 126 entry.Put(syncable::MTIME, backup->ref(syncable::MTIME)); 127 entry.Put(syncable::BASE_VERSION, backup->ref(syncable::BASE_VERSION)); 128 entry.Put(syncable::IS_DIR, backup->ref(syncable::IS_DIR)); 129 entry.Put(syncable::IS_DEL, backup->ref(syncable::IS_DEL)); 130 entry.Put(syncable::ID, backup->ref(syncable::ID)); 131 entry.Put(syncable::IS_UNAPPLIED_UPDATE, 132 backup->ref(syncable::IS_UNAPPLIED_UPDATE)); 133 return true; 134 } 135 136 void PlaceEntriesAtRoot(syncable::WriteTransaction* trans, 137 const vector<syncable::Id>* ids) { 138 vector<syncable::Id>::const_iterator it; 139 for (it = ids->begin(); it != ids->end(); ++it) { 140 syncable::MutableEntry entry(trans, syncable::GET_BY_ID, *it); 141 entry.Put(syncable::PARENT_ID, trans->root_id()); 142 } 143 } 144 145 } // namespace 146 147 bool BuildAndProcessConflictSetsCommand::ApplyUpdatesTransactionally( 148 syncable::WriteTransaction* trans, 149 const vector<syncable::Id>* const update_set, 150 ConflictResolver* resolver, 151 Cryptographer* cryptographer, 152 const ModelSafeRoutingInfo& routes, 153 StatusController* status) { 154 // The handles in the |update_set| order. 155 vector<int64> handles; 156 157 // Holds the same Ids as update_set, but sorted so that runs of adjacent 158 // nodes appear in order. 159 vector<syncable::Id> rollback_ids; 160 rollback_ids.reserve(update_set->size()); 161 162 // Tracks what's added to |rollback_ids|. 163 syncable::MetahandleSet rollback_ids_inserted_items; 164 vector<syncable::Id>::const_iterator it; 165 166 // 1. Build |rollback_ids| in the order required for successful rollback. 167 // Specifically, for positions to come out right, restoring an item 168 // requires that its predecessor in the sibling order is properly 169 // restored first. 170 // 2. Build |handles|, the list of handles for ApplyUpdates. 171 for (it = update_set->begin(); it != update_set->end(); ++it) { 172 syncable::Entry entry(trans, syncable::GET_BY_ID, *it); 173 SyncerUtil::AddPredecessorsThenItem(trans, &entry, 174 syncable::IS_UNAPPLIED_UPDATE, &rollback_ids_inserted_items, 175 &rollback_ids); 176 handles.push_back(entry.Get(syncable::META_HANDLE)); 177 } 178 DCHECK_EQ(rollback_ids.size(), update_set->size()); 179 DCHECK_EQ(rollback_ids_inserted_items.size(), update_set->size()); 180 181 // 3. Store the information needed to rollback if the transaction fails. 182 // Do this before modifying anything to keep the next/prev values intact. 183 vector<syncable::EntryKernel> rollback_data(rollback_ids.size()); 184 for (size_t i = 0; i < rollback_ids.size(); ++i) { 185 syncable::Entry entry(trans, syncable::GET_BY_ID, rollback_ids[i]); 186 StoreLocalDataForUpdateRollback(&entry, &rollback_data[i]); 187 } 188 189 // 4. Use the preparer to move things to an initial starting state where 190 // nothing in the set is a child of anything else. If 191 // we've correctly calculated the set, the server tree is valid and no 192 // changes have occurred locally we should be able to apply updates from this 193 // state. 194 PlaceEntriesAtRoot(trans, update_set); 195 196 // 5. Use the usual apply updates from the special start state we've just 197 // prepared. 198 UpdateApplicator applicator(resolver, cryptographer, 199 handles.begin(), handles.end(), 200 routes, status->group_restriction()); 201 while (applicator.AttemptOneApplication(trans)) { 202 // Keep going till all updates are applied. 203 } 204 if (!applicator.AllUpdatesApplied()) { 205 LOG(ERROR) << "Transactional Apply Failed, Rolling back."; 206 // We have to move entries into the temp dir again. e.g. if a swap was in a 207 // set with other failing updates, the swap may have gone through, meaning 208 // the roll back needs to be transactional. But as we're going to a known 209 // good state we should always succeed. 210 PlaceEntriesAtRoot(trans, update_set); 211 212 // Rollback all entries. 213 for (size_t i = 0; i < rollback_data.size(); ++i) { 214 CHECK(RollbackEntry(trans, &rollback_data[i])); 215 } 216 return false; // Don't save progress -- we just undid it. 217 } 218 applicator.SaveProgressIntoSessionState(status->mutable_conflict_progress(), 219 status->mutable_update_progress()); 220 return true; 221 } 222 223 void BuildAndProcessConflictSetsCommand::BuildConflictSets( 224 syncable::BaseTransaction* trans, 225 ConflictProgress* conflict_progress) { 226 conflict_progress->CleanupSets(); 227 set<syncable::Id>::iterator i = conflict_progress->ConflictingItemsBegin(); 228 while (i != conflict_progress->ConflictingItemsEnd()) { 229 syncable::Entry entry(trans, syncable::GET_BY_ID, *i); 230 if (!entry.good() || 231 (!entry.Get(syncable::IS_UNSYNCED) && 232 !entry.Get(syncable::IS_UNAPPLIED_UPDATE))) { 233 // This can happen very rarely. It means we had a simply conflicting item 234 // that randomly committed; its ID could have changed during the commit. 235 // We drop the entry as it's no longer conflicting. 236 conflict_progress->EraseConflictingItemById(*(i++)); 237 continue; 238 } 239 if (entry.ExistsOnClientBecauseNameIsNonEmpty() && 240 (entry.Get(syncable::IS_DEL) || entry.Get(syncable::SERVER_IS_DEL))) { 241 // If we're deleted on client or server we can't be in a complex set. 242 ++i; 243 continue; 244 } 245 bool new_parent = 246 entry.Get(syncable::PARENT_ID) != entry.Get(syncable::SERVER_PARENT_ID); 247 if (new_parent) 248 MergeSetsForIntroducedLoops(trans, &entry, conflict_progress); 249 MergeSetsForNonEmptyDirectories(trans, &entry, conflict_progress); 250 ++i; 251 } 252 } 253 254 void BuildAndProcessConflictSetsCommand::MergeSetsForIntroducedLoops( 255 syncable::BaseTransaction* trans, syncable::Entry* entry, 256 ConflictProgress* conflict_progress) { 257 // This code crawls up from the item in question until it gets to the root 258 // or itself. If it gets to the root it does nothing. If it finds a loop all 259 // moved unsynced entries in the list of crawled entries have their sets 260 // merged with the entry. 261 // TODO(sync): Build test cases to cover this function when the argument list 262 // has settled. 263 syncable::Id parent_id = entry->Get(syncable::SERVER_PARENT_ID); 264 syncable::Entry parent(trans, syncable::GET_BY_ID, parent_id); 265 if (!parent.good()) { 266 return; 267 } 268 // Don't check for loop if the server parent is deleted. 269 if (parent.Get(syncable::IS_DEL)) 270 return; 271 vector<syncable::Id> conflicting_entries; 272 while (!parent_id.IsRoot()) { 273 syncable::Entry parent(trans, syncable::GET_BY_ID, parent_id); 274 if (!parent.good()) { 275 VLOG(1) << "Bad parent in loop check, skipping. Bad parent id: " 276 << parent_id << " entry: " << *entry; 277 return; 278 } 279 if (parent.Get(syncable::IS_UNSYNCED) && 280 entry->Get(syncable::PARENT_ID) != 281 entry->Get(syncable::SERVER_PARENT_ID)) 282 conflicting_entries.push_back(parent_id); 283 parent_id = parent.Get(syncable::PARENT_ID); 284 if (parent_id == entry->Get(syncable::ID)) 285 break; 286 } 287 if (parent_id.IsRoot()) 288 return; 289 for (size_t i = 0; i < conflicting_entries.size(); i++) { 290 conflict_progress->MergeSets(entry->Get(syncable::ID), 291 conflicting_entries[i]); 292 } 293 } 294 295 namespace { 296 297 class ServerDeletedPathChecker { 298 public: 299 static bool CausingConflict(const syncable::Entry& e, 300 const syncable::Entry& log_entry) { 301 CHECK(e.good()) << "Missing parent in path of: " << log_entry; 302 if (e.Get(syncable::IS_UNAPPLIED_UPDATE) && 303 e.Get(syncable::SERVER_IS_DEL)) { 304 CHECK(!e.Get(syncable::IS_DEL)) << " Inconsistency in local tree. " 305 "syncable::Entry: " << e << " Leaf: " << log_entry; 306 return true; 307 } else { 308 CHECK(!e.Get(syncable::IS_DEL)) << " Deleted entry has children. " 309 "syncable::Entry: " << e << " Leaf: " << log_entry; 310 return false; 311 } 312 } 313 314 // returns 0 if we should stop investigating the path. 315 static syncable::Id GetAndExamineParent(syncable::BaseTransaction* trans, 316 const syncable::Id& id, 317 const syncable::Id& check_id, 318 const syncable::Entry& log_entry) { 319 syncable::Entry parent(trans, syncable::GET_BY_ID, id); 320 CHECK(parent.good()) << "Tree inconsitency, missing id" << id << " " 321 << log_entry; 322 syncable::Id parent_id = parent.Get(syncable::PARENT_ID); 323 CHECK(parent_id != check_id) << "Loop in dir tree! " 324 << log_entry << " " << parent; 325 return parent_id; 326 } 327 }; 328 329 class LocallyDeletedPathChecker { 330 public: 331 static bool CausingConflict(const syncable::Entry& e, 332 const syncable::Entry& log_entry) { 333 return e.good() && e.Get(syncable::IS_DEL) && e.Get(syncable::IS_UNSYNCED); 334 } 335 336 // returns 0 if we should stop investigating the path. 337 static syncable::Id GetAndExamineParent(syncable::BaseTransaction* trans, 338 const syncable::Id& id, 339 const syncable::Id& check_id, 340 const syncable::Entry& log_entry) { 341 syncable::Entry parent(trans, syncable::GET_BY_ID, id); 342 if (!parent.good()) 343 return syncable::kNullId; 344 syncable::Id parent_id = parent.Get(syncable::PARENT_ID); 345 if (parent_id == check_id) 346 return syncable::kNullId; 347 return parent_id; 348 } 349 }; 350 351 template <typename Checker> 352 void CrawlDeletedTreeMergingSets(syncable::BaseTransaction* trans, 353 const syncable::Entry& entry, 354 ConflictProgress* conflict_progress, 355 Checker checker) { 356 syncable::Id parent_id = entry.Get(syncable::PARENT_ID); 357 syncable::Id double_step_parent_id = parent_id; 358 // This block builds sets where we've got an entry in a directory the server 359 // wants to delete. 360 // 361 // Here we're walking up the tree to find all entries that the pass checks 362 // deleted. We can be extremely strict here as anything unexpected means 363 // invariants in the local hierarchy have been broken. 364 while (!parent_id.IsRoot()) { 365 if (!double_step_parent_id.IsRoot()) { 366 // Checks to ensure we don't loop. 367 double_step_parent_id = checker.GetAndExamineParent( 368 trans, double_step_parent_id, parent_id, entry); 369 double_step_parent_id = checker.GetAndExamineParent( 370 trans, double_step_parent_id, parent_id, entry); 371 } 372 syncable::Entry parent(trans, syncable::GET_BY_ID, parent_id); 373 if (checker.CausingConflict(parent, entry)) { 374 conflict_progress->MergeSets(entry.Get(syncable::ID), 375 parent.Get(syncable::ID)); 376 } else { 377 break; 378 } 379 parent_id = parent.Get(syncable::PARENT_ID); 380 } 381 } 382 383 } // namespace 384 385 void BuildAndProcessConflictSetsCommand::MergeSetsForNonEmptyDirectories( 386 syncable::BaseTransaction* trans, syncable::Entry* entry, 387 ConflictProgress* conflict_progress) { 388 if (entry->Get(syncable::IS_UNSYNCED) && !entry->Get(syncable::IS_DEL)) { 389 ServerDeletedPathChecker checker; 390 CrawlDeletedTreeMergingSets(trans, *entry, conflict_progress, checker); 391 } 392 if (entry->Get(syncable::IS_UNAPPLIED_UPDATE) && 393 !entry->Get(syncable::SERVER_IS_DEL)) { 394 syncable::Entry parent(trans, syncable::GET_BY_ID, 395 entry->Get(syncable::SERVER_PARENT_ID)); 396 syncable::Id parent_id = entry->Get(syncable::SERVER_PARENT_ID); 397 if (!parent.good()) 398 return; 399 LocallyDeletedPathChecker checker; 400 if (!checker.CausingConflict(parent, *entry)) 401 return; 402 conflict_progress->MergeSets(entry->Get(syncable::ID), 403 parent.Get(syncable::ID)); 404 CrawlDeletedTreeMergingSets(trans, parent, conflict_progress, checker); 405 } 406 } 407 408 } // namespace browser_sync 409