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 |