| 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 "chrome/browser/sync/engine/build_and_process_conflict_sets_command.h" | 5 #include "chrome/browser/sync/engine/build_and_process_conflict_sets_command.h" |
| 6 | 6 |
| 7 #include <set> | 7 #include <set> |
| 8 #include <string> | 8 #include <string> |
| 9 #include <sstream> | 9 #include <sstream> |
| 10 #include <vector> | 10 #include <vector> |
| (...skipping 19 matching lines...) Expand all Loading... |
| 30 BuildAndProcessConflictSetsCommand::BuildAndProcessConflictSetsCommand() {} | 30 BuildAndProcessConflictSetsCommand::BuildAndProcessConflictSetsCommand() {} |
| 31 BuildAndProcessConflictSetsCommand::~BuildAndProcessConflictSetsCommand() {} | 31 BuildAndProcessConflictSetsCommand::~BuildAndProcessConflictSetsCommand() {} |
| 32 | 32 |
| 33 std::set<ModelSafeGroup> BuildAndProcessConflictSetsCommand::GetGroupsToChange( | 33 std::set<ModelSafeGroup> BuildAndProcessConflictSetsCommand::GetGroupsToChange( |
| 34 const sessions::SyncSession& session) const { | 34 const sessions::SyncSession& session) const { |
| 35 return session.GetEnabledGroupsWithConflicts(); | 35 return session.GetEnabledGroupsWithConflicts(); |
| 36 } | 36 } |
| 37 | 37 |
| 38 SyncerError BuildAndProcessConflictSetsCommand::ModelChangingExecuteImpl( | 38 SyncerError BuildAndProcessConflictSetsCommand::ModelChangingExecuteImpl( |
| 39 SyncSession* session) { | 39 SyncSession* session) { |
| 40 session->mutable_status_controller()->update_conflict_sets_built( | |
| 41 BuildAndProcessConflictSets(session)); | |
| 42 return SYNCER_OK; | |
| 43 } | |
| 44 | |
| 45 bool BuildAndProcessConflictSetsCommand::BuildAndProcessConflictSets( | |
| 46 SyncSession* session) { | |
| 47 syncable::ScopedDirLookup dir(session->context()->directory_manager(), | 40 syncable::ScopedDirLookup dir(session->context()->directory_manager(), |
| 48 session->context()->account_name()); | 41 session->context()->account_name()); |
| 49 if (!dir.good()) | 42 if (!dir.good()) |
| 50 return false; | 43 return DIRECTORY_LOOKUP_FAILED; |
| 51 bool had_single_direction_sets = false; | |
| 52 { // Scope for transaction. | |
| 53 syncable::WriteTransaction trans(FROM_HERE, syncable::SYNCER, dir); | |
| 54 BuildConflictSets(&trans, | |
| 55 session->mutable_status_controller()->mutable_conflict_progress()); | |
| 56 had_single_direction_sets = ProcessSingleDirectionConflictSets(&trans, | |
| 57 session->context()->resolver(), | |
| 58 session->context()->directory_manager()->GetCryptographer(&trans), | |
| 59 session->mutable_status_controller(), session->routing_info()); | |
| 60 // We applied some updates transactionally, lets try syncing again. | |
| 61 if (had_single_direction_sets) | |
| 62 return true; | |
| 63 } | |
| 64 return false; | |
| 65 } | |
| 66 | 44 |
| 67 bool BuildAndProcessConflictSetsCommand::ProcessSingleDirectionConflictSets( | 45 syncable::WriteTransaction trans(FROM_HERE, syncable::SYNCER, dir); |
| 68 syncable::WriteTransaction* trans, ConflictResolver* resolver, | 46 BuildConflictSets(&trans, |
| 69 Cryptographer* cryptographer, StatusController* status, | 47 session->mutable_status_controller()->mutable_conflict_progress()); |
| 70 const ModelSafeRoutingInfo& routes) { | |
| 71 if (!status->conflict_progress()) | |
| 72 return false; | |
| 73 bool rv = false; | |
| 74 set<ConflictSet*>::const_iterator all_sets_iterator; | |
| 75 for (all_sets_iterator = status->conflict_progress()->ConflictSetsBegin(); | |
| 76 all_sets_iterator != status->conflict_progress()->ConflictSetsEnd();) { | |
| 77 const ConflictSet* conflict_set = *all_sets_iterator; | |
| 78 CHECK_GE(conflict_set->size(), 2U); | |
| 79 // We scan the set to see if it consists of changes of only one type. | |
| 80 ConflictSet::const_iterator i; | |
| 81 size_t unsynced_count = 0, unapplied_count = 0; | |
| 82 for (i = conflict_set->begin(); i != conflict_set->end(); ++i) { | |
| 83 syncable::Entry entry(trans, syncable::GET_BY_ID, *i); | |
| 84 CHECK(entry.good()); | |
| 85 if (entry.Get(syncable::IS_UNSYNCED)) | |
| 86 unsynced_count++; | |
| 87 if (entry.Get(syncable::IS_UNAPPLIED_UPDATE)) | |
| 88 unapplied_count++; | |
| 89 } | |
| 90 if (conflict_set->size() == unsynced_count && 0 == unapplied_count) { | |
| 91 DVLOG(1) << "Skipped transactional commit attempt."; | |
| 92 } else if (conflict_set->size() == unapplied_count && 0 == unsynced_count && | |
| 93 ApplyUpdatesTransactionally(trans, conflict_set, resolver, | |
| 94 cryptographer, routes, status)) { | |
| 95 rv = true; | |
| 96 } | |
| 97 ++all_sets_iterator; | |
| 98 } | |
| 99 return rv; | |
| 100 } | |
| 101 | 48 |
| 102 namespace { | 49 return SYNCER_OK; |
| 103 | |
| 104 void StoreLocalDataForUpdateRollback(syncable::Entry* entry, | |
| 105 syncable::EntryKernel* backup) { | |
| 106 CHECK(!entry->Get(syncable::IS_UNSYNCED)) << " Storing Rollback data for " | |
| 107 "entry that's unsynced." << *entry; | |
| 108 CHECK(entry->Get(syncable::IS_UNAPPLIED_UPDATE)) << " Storing Rollback data " | |
| 109 "for entry that's not an unapplied update." << *entry; | |
| 110 *backup = entry->GetKernelCopy(); | |
| 111 } | |
| 112 | |
| 113 | |
| 114 bool RollbackEntry(syncable::WriteTransaction* trans, | |
| 115 syncable::EntryKernel* backup) { | |
| 116 syncable::MutableEntry entry(trans, syncable::GET_BY_HANDLE, | |
| 117 backup->ref(syncable::META_HANDLE)); | |
| 118 CHECK(entry.good()); | |
| 119 | |
| 120 if (!entry.Put(syncable::IS_DEL, backup->ref(syncable::IS_DEL))) | |
| 121 return false; | |
| 122 | |
| 123 entry.Put(syncable::NON_UNIQUE_NAME, backup->ref(syncable::NON_UNIQUE_NAME)); | |
| 124 entry.Put(syncable::PARENT_ID, backup->ref(syncable::PARENT_ID)); | |
| 125 | |
| 126 if (!backup->ref(syncable::IS_DEL)) { | |
| 127 if (!entry.PutPredecessor(backup->ref(syncable::PREV_ID))) { | |
| 128 // TODO(lipalani) : Propagate the error to caller. crbug.com/100444. | |
| 129 NOTREACHED(); | |
| 130 } | |
| 131 } | |
| 132 | |
| 133 if (backup->ref(syncable::PREV_ID) != entry.Get(syncable::PREV_ID)) | |
| 134 return false; | |
| 135 | |
| 136 entry.Put(syncable::CTIME, backup->ref(syncable::CTIME)); | |
| 137 entry.Put(syncable::MTIME, backup->ref(syncable::MTIME)); | |
| 138 entry.Put(syncable::BASE_VERSION, backup->ref(syncable::BASE_VERSION)); | |
| 139 entry.Put(syncable::IS_DIR, backup->ref(syncable::IS_DIR)); | |
| 140 entry.Put(syncable::IS_DEL, backup->ref(syncable::IS_DEL)); | |
| 141 entry.Put(syncable::ID, backup->ref(syncable::ID)); | |
| 142 entry.Put(syncable::IS_UNAPPLIED_UPDATE, | |
| 143 backup->ref(syncable::IS_UNAPPLIED_UPDATE)); | |
| 144 return true; | |
| 145 } | |
| 146 | |
| 147 void PlaceEntriesAtRoot(syncable::WriteTransaction* trans, | |
| 148 const vector<syncable::Id>* ids) { | |
| 149 vector<syncable::Id>::const_iterator it; | |
| 150 for (it = ids->begin(); it != ids->end(); ++it) { | |
| 151 syncable::MutableEntry entry(trans, syncable::GET_BY_ID, *it); | |
| 152 entry.Put(syncable::PARENT_ID, trans->root_id()); | |
| 153 } | |
| 154 } | |
| 155 | |
| 156 } // namespace | |
| 157 | |
| 158 bool BuildAndProcessConflictSetsCommand::ApplyUpdatesTransactionally( | |
| 159 syncable::WriteTransaction* trans, | |
| 160 const vector<syncable::Id>* const update_set, | |
| 161 ConflictResolver* resolver, | |
| 162 Cryptographer* cryptographer, | |
| 163 const ModelSafeRoutingInfo& routes, | |
| 164 StatusController* status) { | |
| 165 // The handles in the |update_set| order. | |
| 166 vector<int64> handles; | |
| 167 | |
| 168 // Holds the same Ids as update_set, but sorted so that runs of adjacent | |
| 169 // nodes appear in order. | |
| 170 vector<syncable::Id> rollback_ids; | |
| 171 rollback_ids.reserve(update_set->size()); | |
| 172 | |
| 173 // Tracks what's added to |rollback_ids|. | |
| 174 syncable::MetahandleSet rollback_ids_inserted_items; | |
| 175 vector<syncable::Id>::const_iterator it; | |
| 176 | |
| 177 // 1. Build |rollback_ids| in the order required for successful rollback. | |
| 178 // Specifically, for positions to come out right, restoring an item | |
| 179 // requires that its predecessor in the sibling order is properly | |
| 180 // restored first. | |
| 181 // 2. Build |handles|, the list of handles for ApplyUpdates. | |
| 182 for (it = update_set->begin(); it != update_set->end(); ++it) { | |
| 183 syncable::Entry entry(trans, syncable::GET_BY_ID, *it); | |
| 184 SyncerUtil::AddPredecessorsThenItem(trans, &entry, | |
| 185 syncable::IS_UNAPPLIED_UPDATE, &rollback_ids_inserted_items, | |
| 186 &rollback_ids); | |
| 187 handles.push_back(entry.Get(syncable::META_HANDLE)); | |
| 188 } | |
| 189 DCHECK_EQ(rollback_ids.size(), update_set->size()); | |
| 190 DCHECK_EQ(rollback_ids_inserted_items.size(), update_set->size()); | |
| 191 | |
| 192 // 3. Store the information needed to rollback if the transaction fails. | |
| 193 // Do this before modifying anything to keep the next/prev values intact. | |
| 194 vector<syncable::EntryKernel> rollback_data(rollback_ids.size()); | |
| 195 for (size_t i = 0; i < rollback_ids.size(); ++i) { | |
| 196 syncable::Entry entry(trans, syncable::GET_BY_ID, rollback_ids[i]); | |
| 197 StoreLocalDataForUpdateRollback(&entry, &rollback_data[i]); | |
| 198 } | |
| 199 | |
| 200 // 4. Use the preparer to move things to an initial starting state where | |
| 201 // nothing in the set is a child of anything else. If | |
| 202 // we've correctly calculated the set, the server tree is valid and no | |
| 203 // changes have occurred locally we should be able to apply updates from this | |
| 204 // state. | |
| 205 PlaceEntriesAtRoot(trans, update_set); | |
| 206 | |
| 207 // 5. Use the usual apply updates from the special start state we've just | |
| 208 // prepared. | |
| 209 UpdateApplicator applicator(resolver, cryptographer, | |
| 210 handles.begin(), handles.end(), | |
| 211 routes, status->group_restriction()); | |
| 212 while (applicator.AttemptOneApplication(trans)) { | |
| 213 // Keep going till all updates are applied. | |
| 214 } | |
| 215 if (!applicator.AllUpdatesApplied()) { | |
| 216 LOG(ERROR) << "Transactional Apply Failed, Rolling back."; | |
| 217 // We have to move entries into the temp dir again. e.g. if a swap was in a | |
| 218 // set with other failing updates, the swap may have gone through, meaning | |
| 219 // the roll back needs to be transactional. But as we're going to a known | |
| 220 // good state we should always succeed. | |
| 221 PlaceEntriesAtRoot(trans, update_set); | |
| 222 | |
| 223 // Rollback all entries. | |
| 224 for (size_t i = 0; i < rollback_data.size(); ++i) { | |
| 225 CHECK(RollbackEntry(trans, &rollback_data[i])); | |
| 226 } | |
| 227 return false; // Don't save progress -- we just undid it. | |
| 228 } | |
| 229 applicator.SaveProgressIntoSessionState(status->mutable_conflict_progress(), | |
| 230 status->mutable_update_progress()); | |
| 231 return true; | |
| 232 } | 50 } |
| 233 | 51 |
| 234 void BuildAndProcessConflictSetsCommand::BuildConflictSets( | 52 void BuildAndProcessConflictSetsCommand::BuildConflictSets( |
| 235 syncable::BaseTransaction* trans, | 53 syncable::BaseTransaction* trans, |
| 236 ConflictProgress* conflict_progress) { | 54 ConflictProgress* conflict_progress) { |
| 237 conflict_progress->CleanupSets(); | 55 conflict_progress->CleanupSets(); |
| 238 set<syncable::Id>::const_iterator i = | 56 set<syncable::Id>::const_iterator i = |
| 239 conflict_progress->ConflictingItemsBegin(); | 57 conflict_progress->ConflictingItemsBegin(); |
| 240 while (i != conflict_progress->ConflictingItemsEnd()) { | 58 while (i != conflict_progress->ConflictingItemsEnd()) { |
| 241 syncable::Entry entry(trans, syncable::GET_BY_ID, *i); | 59 syncable::Entry entry(trans, syncable::GET_BY_ID, *i); |
| (...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 411 LocallyDeletedPathChecker checker; | 229 LocallyDeletedPathChecker checker; |
| 412 if (!checker.CausingConflict(parent, *entry)) | 230 if (!checker.CausingConflict(parent, *entry)) |
| 413 return; | 231 return; |
| 414 conflict_progress->MergeSets(entry->Get(syncable::ID), | 232 conflict_progress->MergeSets(entry->Get(syncable::ID), |
| 415 parent.Get(syncable::ID)); | 233 parent.Get(syncable::ID)); |
| 416 CrawlDeletedTreeMergingSets(trans, parent, conflict_progress, checker); | 234 CrawlDeletedTreeMergingSets(trans, parent, conflict_progress, checker); |
| 417 } | 235 } |
| 418 } | 236 } |
| 419 | 237 |
| 420 } // namespace browser_sync | 238 } // namespace browser_sync |
| OLD | NEW |