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 |