| OLD | NEW |
| 1 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-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 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 <string> | 7 #include <string> |
| 8 #include <sstream> | 8 #include <sstream> |
| 9 #include <vector> | 9 #include <vector> |
| 10 | 10 |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 85 | 85 |
| 86 void StoreLocalDataForUpdateRollback(syncable::Entry* entry, | 86 void StoreLocalDataForUpdateRollback(syncable::Entry* entry, |
| 87 syncable::EntryKernel* backup) { | 87 syncable::EntryKernel* backup) { |
| 88 CHECK(!entry->Get(syncable::IS_UNSYNCED)) << " Storing Rollback data for " | 88 CHECK(!entry->Get(syncable::IS_UNSYNCED)) << " Storing Rollback data for " |
| 89 "entry that's unsynced." << *entry; | 89 "entry that's unsynced." << *entry; |
| 90 CHECK(entry->Get(syncable::IS_UNAPPLIED_UPDATE)) << " Storing Rollback data " | 90 CHECK(entry->Get(syncable::IS_UNAPPLIED_UPDATE)) << " Storing Rollback data " |
| 91 "for entry that's not an unapplied update." << *entry; | 91 "for entry that's not an unapplied update." << *entry; |
| 92 *backup = entry->GetKernelCopy(); | 92 *backup = entry->GetKernelCopy(); |
| 93 } | 93 } |
| 94 | 94 |
| 95 class UniqueNameGenerator { | |
| 96 public: | |
| 97 void Initialize() { | |
| 98 // To avoid name collisions we prefix the names with hex data derived from | |
| 99 // 64 bits of randomness. | |
| 100 int64 name_prefix = static_cast<int64>(base::RandUint64()); | |
| 101 name_stem_ = StringPrintf("%0" PRId64 "x.", name_prefix); | |
| 102 } | |
| 103 string StringNameForEntry(const syncable::Entry& entry) { | |
| 104 CHECK(!name_stem_.empty()); | |
| 105 std::stringstream rv; | |
| 106 rv << name_stem_ << entry.Get(syncable::ID); | |
| 107 return rv.str(); | |
| 108 } | |
| 109 PathString PathStringNameForEntry(const syncable::Entry& entry) { | |
| 110 string name = StringNameForEntry(entry); | |
| 111 return PathString(name.begin(), name.end()); | |
| 112 } | |
| 113 | |
| 114 private: | |
| 115 string name_stem_; | |
| 116 }; | |
| 117 | 95 |
| 118 bool RollbackEntry(syncable::WriteTransaction* trans, | 96 bool RollbackEntry(syncable::WriteTransaction* trans, |
| 119 syncable::EntryKernel* backup) { | 97 syncable::EntryKernel* backup) { |
| 120 syncable::MutableEntry entry(trans, syncable::GET_BY_HANDLE, | 98 syncable::MutableEntry entry(trans, syncable::GET_BY_HANDLE, |
| 121 backup->ref(syncable::META_HANDLE)); | 99 backup->ref(syncable::META_HANDLE)); |
| 122 CHECK(entry.good()); | 100 CHECK(entry.good()); |
| 123 | 101 |
| 124 if (!entry.Put(syncable::IS_DEL, backup->ref(syncable::IS_DEL))) | 102 if (!entry.Put(syncable::IS_DEL, backup->ref(syncable::IS_DEL))) |
| 125 return false; | 103 return false; |
| 126 syncable::Name name = syncable::Name::FromEntryKernel(backup); | 104 |
| 127 if (!entry.PutParentIdAndName(backup->ref(syncable::PARENT_ID), name)) | 105 entry.Put(syncable::NON_UNIQUE_NAME, backup->ref(syncable::NON_UNIQUE_NAME)); |
| 128 return false; | 106 entry.Put(syncable::PARENT_ID, backup->ref(syncable::PARENT_ID)); |
| 129 | 107 |
| 130 if (!backup->ref(syncable::IS_DEL)) { | 108 if (!backup->ref(syncable::IS_DEL)) { |
| 131 if (!entry.PutPredecessor(backup->ref(syncable::PREV_ID))) | 109 if (!entry.PutPredecessor(backup->ref(syncable::PREV_ID))) |
| 132 return false; | 110 return false; |
| 133 } | 111 } |
| 134 | 112 |
| 135 if (backup->ref(syncable::PREV_ID) != entry.Get(syncable::PREV_ID)) | 113 if (backup->ref(syncable::PREV_ID) != entry.Get(syncable::PREV_ID)) |
| 136 return false; | 114 return false; |
| 137 | 115 |
| 138 entry.Put(syncable::CTIME, backup->ref(syncable::CTIME)); | 116 entry.Put(syncable::CTIME, backup->ref(syncable::CTIME)); |
| 139 entry.Put(syncable::MTIME, backup->ref(syncable::MTIME)); | 117 entry.Put(syncable::MTIME, backup->ref(syncable::MTIME)); |
| 140 entry.Put(syncable::BASE_VERSION, backup->ref(syncable::BASE_VERSION)); | 118 entry.Put(syncable::BASE_VERSION, backup->ref(syncable::BASE_VERSION)); |
| 141 entry.Put(syncable::IS_DIR, backup->ref(syncable::IS_DIR)); | 119 entry.Put(syncable::IS_DIR, backup->ref(syncable::IS_DIR)); |
| 142 entry.Put(syncable::IS_DEL, backup->ref(syncable::IS_DEL)); | 120 entry.Put(syncable::IS_DEL, backup->ref(syncable::IS_DEL)); |
| 143 entry.Put(syncable::ID, backup->ref(syncable::ID)); | 121 entry.Put(syncable::ID, backup->ref(syncable::ID)); |
| 144 entry.Put(syncable::IS_UNAPPLIED_UPDATE, | 122 entry.Put(syncable::IS_UNAPPLIED_UPDATE, |
| 145 backup->ref(syncable::IS_UNAPPLIED_UPDATE)); | 123 backup->ref(syncable::IS_UNAPPLIED_UPDATE)); |
| 146 return true; | 124 return true; |
| 147 } | 125 } |
| 148 | 126 |
| 149 class TransactionalUpdateEntryPreparer { | 127 void PlaceEntriesAtRoot(syncable::WriteTransaction* trans, |
| 150 public: | 128 const vector<syncable::Id>* ids) { |
| 151 TransactionalUpdateEntryPreparer() { | 129 vector<syncable::Id>::const_iterator it; |
| 152 namegen_.Initialize(); | 130 for (it = ids->begin(); it != ids->end(); ++it) { |
| 131 syncable::MutableEntry entry(trans, syncable::GET_BY_ID, *it); |
| 132 entry.Put(syncable::PARENT_ID, trans->root_id()); |
| 153 } | 133 } |
| 154 | 134 } |
| 155 void PrepareEntries(syncable::WriteTransaction* trans, | |
| 156 const vector<syncable::Id>* ids) { | |
| 157 vector<syncable::Id>::const_iterator it; | |
| 158 for (it = ids->begin(); it != ids->end(); ++it) { | |
| 159 syncable::MutableEntry entry(trans, syncable::GET_BY_ID, *it); | |
| 160 syncable::Name random_name(namegen_.PathStringNameForEntry(entry)); | |
| 161 CHECK(entry.PutParentIdAndName(trans->root_id(), random_name)); | |
| 162 } | |
| 163 } | |
| 164 | |
| 165 private: | |
| 166 UniqueNameGenerator namegen_; | |
| 167 DISALLOW_COPY_AND_ASSIGN(TransactionalUpdateEntryPreparer); | |
| 168 }; | |
| 169 | 135 |
| 170 } // namespace | 136 } // namespace |
| 171 | 137 |
| 172 bool BuildAndProcessConflictSetsCommand::ApplyUpdatesTransactionally( | 138 bool BuildAndProcessConflictSetsCommand::ApplyUpdatesTransactionally( |
| 173 syncable::WriteTransaction* trans, | 139 syncable::WriteTransaction* trans, |
| 174 const vector<syncable::Id>* const update_set, | 140 const vector<syncable::Id>* const update_set, |
| 175 SyncerSession* const session) { | 141 SyncerSession* const session) { |
| 176 // The handles in the |update_set| order. | 142 // The handles in the |update_set| order. |
| 177 vector<int64> handles; | 143 vector<int64> handles; |
| 178 | 144 |
| (...skipping 22 matching lines...) Expand all Loading... |
| 201 DCHECK_EQ(rollback_ids_inserted_items.size(), update_set->size()); | 167 DCHECK_EQ(rollback_ids_inserted_items.size(), update_set->size()); |
| 202 | 168 |
| 203 // 3. Store the information needed to rollback if the transaction fails. | 169 // 3. Store the information needed to rollback if the transaction fails. |
| 204 // Do this before modifying anything to keep the next/prev values intact. | 170 // Do this before modifying anything to keep the next/prev values intact. |
| 205 vector<syncable::EntryKernel> rollback_data(rollback_ids.size()); | 171 vector<syncable::EntryKernel> rollback_data(rollback_ids.size()); |
| 206 for (size_t i = 0; i < rollback_ids.size(); ++i) { | 172 for (size_t i = 0; i < rollback_ids.size(); ++i) { |
| 207 syncable::Entry entry(trans, syncable::GET_BY_ID, rollback_ids[i]); | 173 syncable::Entry entry(trans, syncable::GET_BY_ID, rollback_ids[i]); |
| 208 StoreLocalDataForUpdateRollback(&entry, &rollback_data[i]); | 174 StoreLocalDataForUpdateRollback(&entry, &rollback_data[i]); |
| 209 } | 175 } |
| 210 | 176 |
| 211 // 4. Use the preparer to move things to an initial starting state where no | 177 // 4. Use the preparer to move things to an initial starting state where |
| 212 // names collide, and nothing in the set is a child of anything else. If | 178 // nothing in the set is a child of anything else. If |
| 213 // we've correctly calculated the set, the server tree is valid and no | 179 // we've correctly calculated the set, the server tree is valid and no |
| 214 // changes have occurred locally we should be able to apply updates from this | 180 // changes have occurred locally we should be able to apply updates from this |
| 215 // state. | 181 // state. |
| 216 TransactionalUpdateEntryPreparer preparer; | 182 PlaceEntriesAtRoot(trans, update_set); |
| 217 preparer.PrepareEntries(trans, update_set); | |
| 218 | 183 |
| 219 // 5. Use the usual apply updates from the special start state we've just | 184 // 5. Use the usual apply updates from the special start state we've just |
| 220 // prepared. | 185 // prepared. |
| 221 UpdateApplicator applicator(session->resolver(), handles.begin(), | 186 UpdateApplicator applicator(session->resolver(), handles.begin(), |
| 222 handles.end()); | 187 handles.end()); |
| 223 while (applicator.AttemptOneApplication(trans)) { | 188 while (applicator.AttemptOneApplication(trans)) { |
| 224 // Keep going till all updates are applied. | 189 // Keep going till all updates are applied. |
| 225 } | 190 } |
| 226 if (!applicator.AllUpdatesApplied()) { | 191 if (!applicator.AllUpdatesApplied()) { |
| 227 LOG(ERROR) << "Transactional Apply Failed, Rolling back."; | 192 LOG(ERROR) << "Transactional Apply Failed, Rolling back."; |
| 228 // We have to move entries into the temp dir again. e.g. if a swap was in a | 193 // We have to move entries into the temp dir again. e.g. if a swap was in a |
| 229 // set with other failing updates, the swap may have gone through, meaning | 194 // set with other failing updates, the swap may have gone through, meaning |
| 230 // the roll back needs to be transactional. But as we're going to a known | 195 // the roll back needs to be transactional. But as we're going to a known |
| 231 // good state we should always succeed. | 196 // good state we should always succeed. |
| 232 preparer.PrepareEntries(trans, update_set); | 197 PlaceEntriesAtRoot(trans, update_set); |
| 233 | 198 |
| 234 // Rollback all entries. | 199 // Rollback all entries. |
| 235 for (size_t i = 0; i < rollback_data.size(); ++i) { | 200 for (size_t i = 0; i < rollback_data.size(); ++i) { |
| 236 CHECK(RollbackEntry(trans, &rollback_data[i])); | 201 CHECK(RollbackEntry(trans, &rollback_data[i])); |
| 237 } | 202 } |
| 238 return false; // Don't save progress -- we just undid it. | 203 return false; // Don't save progress -- we just undid it. |
| 239 } | 204 } |
| 240 applicator.SaveProgressIntoSessionState(session); | 205 applicator.SaveProgressIntoSessionState(session); |
| 241 return true; | 206 return true; |
| 242 } | 207 } |
| 243 | 208 |
| 244 void BuildAndProcessConflictSetsCommand::BuildConflictSets( | 209 void BuildAndProcessConflictSetsCommand::BuildConflictSets( |
| 245 syncable::BaseTransaction* trans, | 210 syncable::BaseTransaction* trans, |
| 246 ConflictResolutionView* view) { | 211 ConflictResolutionView* view) { |
| 247 view->CleanupSets(); | 212 view->CleanupSets(); |
| 248 set<syncable::Id>::iterator i = view->CommitConflictsBegin(); | 213 set<syncable::Id>::iterator i = view->CommitConflictsBegin(); |
| 249 while (i != view->CommitConflictsEnd()) { | 214 while (i != view->CommitConflictsEnd()) { |
| 250 syncable::Entry entry(trans, syncable::GET_BY_ID, *i); | 215 syncable::Entry entry(trans, syncable::GET_BY_ID, *i); |
| 251 CHECK(entry.good()); | 216 CHECK(entry.good()); |
| 252 if (!entry.Get(syncable::IS_UNSYNCED) && | 217 if (!entry.Get(syncable::IS_UNSYNCED) && |
| 253 !entry.Get(syncable::IS_UNAPPLIED_UPDATE)) { | 218 !entry.Get(syncable::IS_UNAPPLIED_UPDATE)) { |
| 254 // This can happen very rarely. It means we had a simply conflicting item | 219 // This can happen very rarely. It means we had a simply conflicting item |
| 255 // that randomly committed. We drop the entry as it's no longer | 220 // that randomly committed. We drop the entry as it's no longer |
| 256 // conflicting. | 221 // conflicting. |
| 257 view->EraseCommitConflict(i++); | 222 view->EraseCommitConflict(i++); |
| 258 continue; | 223 continue; |
| 259 } | 224 } |
| 260 if (entry.ExistsOnClientBecauseDatabaseNameIsNonEmpty() && | 225 if (entry.ExistsOnClientBecauseNameIsNonEmpty() && |
| 261 (entry.Get(syncable::IS_DEL) || entry.Get(syncable::SERVER_IS_DEL))) { | 226 (entry.Get(syncable::IS_DEL) || entry.Get(syncable::SERVER_IS_DEL))) { |
| 262 // If we're deleted on client or server we can't be in a complex set. | 227 // If we're deleted on client or server we can't be in a complex set. |
| 263 ++i; | 228 ++i; |
| 264 continue; | 229 continue; |
| 265 } | 230 } |
| 266 bool new_parent = | 231 bool new_parent = |
| 267 entry.Get(syncable::PARENT_ID) != entry.Get(syncable::SERVER_PARENT_ID); | 232 entry.Get(syncable::PARENT_ID) != entry.Get(syncable::SERVER_PARENT_ID); |
| 268 bool new_name = 0 != syncable::ComparePathNames(entry.GetSyncNameValue(), | |
| 269 entry.Get(syncable::SERVER_NAME)); | |
| 270 if (new_parent || new_name) | |
| 271 MergeSetsForNameClash(trans, &entry, view); | |
| 272 if (new_parent) | 233 if (new_parent) |
| 273 MergeSetsForIntroducedLoops(trans, &entry, view); | 234 MergeSetsForIntroducedLoops(trans, &entry, view); |
| 274 MergeSetsForNonEmptyDirectories(trans, &entry, view); | 235 MergeSetsForNonEmptyDirectories(trans, &entry, view); |
| 275 ++i; | 236 ++i; |
| 276 } | 237 } |
| 277 } | 238 } |
| 278 | 239 |
| 279 void BuildAndProcessConflictSetsCommand::MergeSetsForNameClash( | |
| 280 syncable::BaseTransaction* trans, syncable::Entry* entry, | |
| 281 ConflictResolutionView* view) { | |
| 282 PathString server_name = entry->Get(syncable::SERVER_NAME); | |
| 283 // Uncommitted entries have no server name. We trap this because the root | |
| 284 // item has a null name and 0 parentid. | |
| 285 if (server_name.empty()) | |
| 286 return; | |
| 287 syncable::Id conflicting_id = | |
| 288 SyncerUtil::GetNameConflictingItemId( | |
| 289 trans, entry->Get(syncable::SERVER_PARENT_ID), server_name); | |
| 290 if (syncable::kNullId != conflicting_id) | |
| 291 view->MergeSets(entry->Get(syncable::ID), conflicting_id); | |
| 292 } | |
| 293 | |
| 294 void BuildAndProcessConflictSetsCommand::MergeSetsForIntroducedLoops( | 240 void BuildAndProcessConflictSetsCommand::MergeSetsForIntroducedLoops( |
| 295 syncable::BaseTransaction* trans, syncable::Entry* entry, | 241 syncable::BaseTransaction* trans, syncable::Entry* entry, |
| 296 ConflictResolutionView* view) { | 242 ConflictResolutionView* view) { |
| 297 // This code crawls up from the item in question until it gets to the root | 243 // This code crawls up from the item in question until it gets to the root |
| 298 // or itself. If it gets to the root it does nothing. If it finds a loop all | 244 // or itself. If it gets to the root it does nothing. If it finds a loop all |
| 299 // moved unsynced entries in the list of crawled entries have their sets | 245 // moved unsynced entries in the list of crawled entries have their sets |
| 300 // merged with the entry. | 246 // merged with the entry. |
| 301 // TODO(sync): Build test cases to cover this function when the argument list | 247 // TODO(sync): Build test cases to cover this function when the argument list |
| 302 // has settled. | 248 // has settled. |
| 303 syncable::Id parent_id = entry->Get(syncable::SERVER_PARENT_ID); | 249 syncable::Id parent_id = entry->Get(syncable::SERVER_PARENT_ID); |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 435 return; | 381 return; |
| 436 LocallyDeletedPathChecker checker; | 382 LocallyDeletedPathChecker checker; |
| 437 if (!checker.CausingConflict(parent, *entry)) | 383 if (!checker.CausingConflict(parent, *entry)) |
| 438 return; | 384 return; |
| 439 view->MergeSets(entry->Get(syncable::ID), parent.Get(syncable::ID)); | 385 view->MergeSets(entry->Get(syncable::ID), parent.Get(syncable::ID)); |
| 440 CrawlDeletedTreeMergingSets(trans, parent, view, checker); | 386 CrawlDeletedTreeMergingSets(trans, parent, view, checker); |
| 441 } | 387 } |
| 442 } | 388 } |
| 443 | 389 |
| 444 } // namespace browser_sync | 390 } // namespace browser_sync |
| OLD | NEW |