Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(22)

Side by Side Diff: chrome/browser/sync/engine/get_commit_ids_command.cc

Issue 8922015: [Sync] Don't commit items with predecessors/parents in conflict. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: More comments. Add racy test case Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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/get_commit_ids_command.h" 5 #include "chrome/browser/sync/engine/get_commit_ids_command.h"
6 6
7 #include <set> 7 #include <set>
8 #include <utility> 8 #include <utility>
9 #include <vector> 9 #include <vector>
10 10
11 #include "chrome/browser/sync/engine/nigori_util.h" 11 #include "chrome/browser/sync/engine/nigori_util.h"
12 #include "chrome/browser/sync/engine/syncer_util.h" 12 #include "chrome/browser/sync/engine/syncer_util.h"
13 #include "chrome/browser/sync/syncable/directory_manager.h" 13 #include "chrome/browser/sync/syncable/directory_manager.h"
14 #include "chrome/browser/sync/syncable/syncable.h" 14 #include "chrome/browser/sync/syncable/syncable.h"
15 #include "chrome/browser/sync/util/cryptographer.h" 15 #include "chrome/browser/sync/util/cryptographer.h"
16 16
17 using std::set; 17 using std::set;
18 using std::vector; 18 using std::vector;
19 19
20 namespace browser_sync { 20 namespace browser_sync {
21 21
22 using sessions::OrderedCommitSet; 22 using sessions::OrderedCommitSet;
23 using sessions::SyncSession; 23 using sessions::SyncSession;
24 using sessions::StatusController; 24 using sessions::StatusController;
25 25
26 GetCommitIdsCommand::GetCommitIdsCommand(int commit_batch_size) 26 GetCommitIdsCommand::GetCommitIdsCommand(int commit_batch_size)
27 : requested_commit_batch_size_(commit_batch_size), 27 : requested_commit_batch_size_(commit_batch_size) {}
28 passphrase_missing_(false) {}
29 28
30 GetCommitIdsCommand::~GetCommitIdsCommand() {} 29 GetCommitIdsCommand::~GetCommitIdsCommand() {}
31 30
32 SyncerError GetCommitIdsCommand::ExecuteImpl(SyncSession* session) { 31 SyncerError GetCommitIdsCommand::ExecuteImpl(SyncSession* session) {
33 // Gather the full set of unsynced items and store it in the session. They 32 // Gather the full set of unsynced items and store it in the session. They
34 // are not in the correct order for commit. 33 // are not in the correct order for commit.
34 std::set<int64> ready_unsynced_set;
35 syncable::Directory::UnsyncedMetaHandles all_unsynced_handles; 35 syncable::Directory::UnsyncedMetaHandles all_unsynced_handles;
36 SyncerUtil::GetUnsyncedEntries(session->write_transaction(), 36 SyncerUtil::GetUnsyncedEntries(session->write_transaction(),
37 &all_unsynced_handles); 37 &all_unsynced_handles);
38 38
39 syncable::ModelTypeSet encrypted_types;
40 bool passphrase_missing = false;
39 Cryptographer* cryptographer = 41 Cryptographer* cryptographer =
40 session->context()->directory_manager()->GetCryptographer( 42 session->context()->directory_manager()->GetCryptographer(
41 session->write_transaction()); 43 session->write_transaction());
42 if (cryptographer) { 44 if (cryptographer) {
43 encrypted_types_ = cryptographer->GetEncryptedTypes(); 45 encrypted_types = cryptographer->GetEncryptedTypes();
44 passphrase_missing_ = cryptographer->has_pending_keys(); 46 passphrase_missing = cryptographer->has_pending_keys();
45 }; 47 };
46 48
47 const syncable::ModelTypeSet throttled_types = 49 const syncable::ModelTypeSet throttled_types =
48 session->context()->GetThrottledTypes(); 50 session->context()->GetThrottledTypes();
49 // We filter out all unready entries from the set of unsynced handles to 51 // We filter out all unready entries from the set of unsynced handles to
50 // ensure we don't trigger useless sync cycles attempting to retry due to 52 // ensure we don't trigger useless sync cycles attempting to retry due to
51 // there being work to do. (see ScheduleNextSync in sync_scheduler) 53 // there being work to do (see ScheduleNextSync in sync_scheduler). Only items
54 // in |ready_unsynced_set| are candidates for committing.
52 FilterUnreadyEntries(session->write_transaction(), 55 FilterUnreadyEntries(session->write_transaction(),
53 throttled_types, 56 throttled_types,
54 &all_unsynced_handles); 57 encrypted_types,
58 passphrase_missing,
59 all_unsynced_handles,
60 &ready_unsynced_set);
61
62 BuildCommitIds(session->write_transaction(),
63 session->routing_info(),
64 ready_unsynced_set);
55 65
56 StatusController* status = session->mutable_status_controller(); 66 StatusController* status = session->mutable_status_controller();
57 status->set_unsynced_handles(all_unsynced_handles); 67 syncable::Directory::UnsyncedMetaHandles ready_unsynced_vector(
58 BuildCommitIds(status->unsynced_handles(), session->write_transaction(), 68 ready_unsynced_set.begin(), ready_unsynced_set.end());
59 session->routing_info(), throttled_types); 69 status->set_unsynced_handles(ready_unsynced_vector);
60
61 const vector<syncable::Id>& verified_commit_ids = 70 const vector<syncable::Id>& verified_commit_ids =
62 ordered_commit_set_->GetAllCommitIds(); 71 ordered_commit_set_->GetAllCommitIds();
63 72
64 for (size_t i = 0; i < verified_commit_ids.size(); i++) 73 for (size_t i = 0; i < verified_commit_ids.size(); i++)
65 DVLOG(1) << "Debug commit batch result:" << verified_commit_ids[i]; 74 DVLOG(1) << "Debug commit batch result:" << verified_commit_ids[i];
66 75
67 status->set_commit_set(*ordered_commit_set_.get()); 76 status->set_commit_set(*ordered_commit_set_.get());
68 return SYNCER_OK; 77 return SYNCER_OK;
69 } 78 }
70 79
71 namespace { 80 namespace {
72 81
73 // An entry ready for commit is defined as: 82 bool IsEntryInConflict(const syncable::Entry& entry) {
74 // 1. Not in conflict (SERVER_VERSION == BASE_VERSION || SERVER_VERSION == 0) 83 if (entry.Get(syncable::IS_UNSYNCED) &&
75 // and not requiring encryption (any entry containing an encrypted datatype 84 entry.Get(syncable::SERVER_VERSION) > 0 &&
76 // while the cryptographer requires a passphrase is not ready for commit.)
77 // 2. Its type is not currently throttled.
78 bool IsEntryReadyForCommit(syncable::ModelTypeSet encrypted_types,
79 bool passphrase_missing,
80 const syncable::Entry& entry,
81 syncable::ModelTypeSet throttled_types) {
82 if (!entry.Get(syncable::IS_UNSYNCED))
83 return false;
84
85 if (entry.Get(syncable::SERVER_VERSION) > 0 &&
86 (entry.Get(syncable::SERVER_VERSION) > 85 (entry.Get(syncable::SERVER_VERSION) >
87 entry.Get(syncable::BASE_VERSION))) { 86 entry.Get(syncable::BASE_VERSION))) {
88 // The local and server versions don't match. The item must be in 87 // The local and server versions don't match. The item must be in
89 // conflict, so there's no point in attempting to commit. 88 // conflict, so there's no point in attempting to commit.
90 DCHECK(entry.Get(syncable::IS_UNAPPLIED_UPDATE)); // In conflict. 89 DCHECK(entry.Get(syncable::IS_UNAPPLIED_UPDATE));
91 DVLOG(1) << "Excluding entry from commit due to version mismatch " 90 DVLOG(1) << "Excluding entry from commit due to version mismatch "
92 << entry; 91 << entry;
92 return true;
93 }
94 return false;
95 }
96
97 // An entry is not considered ready for commit if any are true:
98 // 1. It's in conflict.
99 // 2. It requires encryption (either the type is encrypted but a passphrase
100 // is missing from the cryptographer, or the entry itself wasn't properly
101 // encrypted).
102 // 3. It's type is currently throttled.
103 // 4. It's a delete but has not been committed.
104 bool IsEntryReadyForCommit(syncable::ModelTypeSet throttled_types,
105 syncable::ModelTypeSet encrypted_types,
106 bool passphrase_missing,
107 const syncable::Entry& entry) {
108 DCHECK(entry.Get(syncable::IS_UNSYNCED));
109 if (IsEntryInConflict(entry))
93 return false; 110 return false;
94 }
95 111
96 const syncable::ModelType type = entry.GetModelType(); 112 const syncable::ModelType type = entry.GetModelType();
97 // We special case the nigori node because even though it is considered an 113 // We special case the nigori node because even though it is considered an
98 // "encrypted type", not all nigori node changes require valid encryption 114 // "encrypted type", not all nigori node changes require valid encryption
99 // (ex: sync_tabs). 115 // (ex: sync_tabs).
100 if ((type != syncable::NIGORI) && 116 if ((type != syncable::NIGORI) &&
101 encrypted_types.Has(type) && 117 encrypted_types.Has(type) &&
102 (passphrase_missing || 118 (passphrase_missing ||
103 syncable::EntryNeedsEncryption(encrypted_types, entry))) { 119 syncable::EntryNeedsEncryption(encrypted_types, entry))) {
104 // This entry requires encryption but is not properly encrypted (possibly 120 // This entry requires encryption but is not properly encrypted (possibly
105 // due to the cryptographer not being initialized or the user hasn't 121 // due to the cryptographer not being initialized or the user hasn't
106 // provided the most recent passphrase). 122 // provided the most recent passphrase).
107 DVLOG(1) << "Excluding entry from commit due to lack of encryption " 123 DVLOG(1) << "Excluding entry from commit due to lack of encryption "
108 << entry; 124 << entry;
109 return false; 125 return false;
110 } 126 }
111 127
112 // Look at the throttled types. 128 // Look at the throttled types.
113 if (throttled_types.Has(type)) 129 if (throttled_types.Has(type))
114 return false; 130 return false;
115 131
132 // Drop deleted uncommitted entries.
133 if (entry.Get(syncable::IS_DEL) && !entry.Get(syncable::ID).ServerKnows()) {
rlarocque 2012/01/20 06:26:56 I worry about this. I think there's an unlikely r
134 // TODO(zea): These will remain unsynced indefinitely. This is harmless,
135 // but we should clean them up somewhere.
136 DVLOG(1) << "Ignoring deleted and uncommitted item." << entry;
137 return false;
138 }
139
140 // Extra validity checks.
rlarocque 2012/01/20 06:26:56 Why do these checks here? UPDATE: I see now that
141 syncable::Id id = entry.Get(syncable::ID);
142 if (id == entry.Get(syncable::PARENT_ID)) {
143 CHECK(id.IsRoot()) << "Non-root item is self parenting." << entry;
144 // If the root becomes unsynced it can cause us problems.
145 LOG(ERROR) << "Root item became unsynced " << entry;
146 return false;
147 }
148
149 if (entry.IsRoot()) {
150 LOG(ERROR) << "Permanent item became unsynced " << entry;
151 return false;
152 }
153
154 DVLOG(2) << "Entry is ready for commit: " << entry;
116 return true; 155 return true;
117 } 156 }
118 157
119 } // namespace 158 } // namespace
120 159
121 void GetCommitIdsCommand::FilterUnreadyEntries( 160 void GetCommitIdsCommand::FilterUnreadyEntries(
122 syncable::BaseTransaction* trans, 161 syncable::BaseTransaction* trans,
123 syncable::ModelTypeSet throttled_types, 162 syncable::ModelTypeSet throttled_types,
124 syncable::Directory::UnsyncedMetaHandles* unsynced_handles) { 163 syncable::ModelTypeSet encrypted_types,
125 syncable::Directory::UnsyncedMetaHandles::iterator iter; 164 bool passphrase_missing,
126 syncable::Directory::UnsyncedMetaHandles new_unsynced_handles; 165 const syncable::Directory::UnsyncedMetaHandles& unsynced_handles,
127 new_unsynced_handles.reserve(unsynced_handles->size()); 166 std::set<int64>* ready_unsynced_set) {
128 for (iter = unsynced_handles->begin(); 167 for (syncable::Directory::UnsyncedMetaHandles::const_iterator iter =
129 iter != unsynced_handles->end(); 168 unsynced_handles.begin(); iter != unsynced_handles.end(); ++iter) {
130 ++iter) {
131 syncable::Entry entry(trans, syncable::GET_BY_HANDLE, *iter); 169 syncable::Entry entry(trans, syncable::GET_BY_HANDLE, *iter);
132 if (IsEntryReadyForCommit(encrypted_types_, 170 if (entry.Get(syncable::IS_UNSYNCED) &&
rlarocque 2012/01/20 06:26:56 I prefer the DCHECK in IsEntryReadyForCommit() to
133 passphrase_missing_, 171 IsEntryReadyForCommit(throttled_types,
134 entry, 172 encrypted_types,
135 throttled_types)) 173 passphrase_missing,
136 new_unsynced_handles.push_back(*iter); 174 entry)) {
175 ready_unsynced_set->insert(*iter);
176 }
137 } 177 }
138 if (new_unsynced_handles.size() != unsynced_handles->size())
139 unsynced_handles->swap(new_unsynced_handles);
140 } 178 }
141 179
142 void GetCommitIdsCommand::AddUncommittedParentsAndTheirPredecessors( 180 bool GetCommitIdsCommand::AddUncommittedParentsAndTheirPredecessors(
143 syncable::BaseTransaction* trans, 181 syncable::BaseTransaction* trans,
144 syncable::Id parent_id,
145 const ModelSafeRoutingInfo& routes, 182 const ModelSafeRoutingInfo& routes,
146 syncable::ModelTypeSet throttled_types) { 183 const std::set<int64>& ready_unsynced_set,
184 const syncable::Entry& item,
185 sessions::OrderedCommitSet* result) const {
147 OrderedCommitSet item_dependencies(routes); 186 OrderedCommitSet item_dependencies(routes);
187 syncable::Id parent_id = item.Get(syncable::PARENT_ID);
148 188
149 // Climb the tree adding entries leaf -> root. 189 // Climb the tree adding entries leaf -> root.
150 while (!parent_id.ServerKnows()) { 190 while (!parent_id.ServerKnows()) {
151 syncable::Entry parent(trans, syncable::GET_BY_ID, parent_id); 191 syncable::Entry parent(trans, syncable::GET_BY_ID, parent_id);
152 CHECK(parent.good()) << "Bad user-only parent in item path."; 192 CHECK(parent.good()) << "Bad user-only parent in item path.";
153 int64 handle = parent.Get(syncable::META_HANDLE); 193 int64 handle = parent.Get(syncable::META_HANDLE);
154 if (ordered_commit_set_->HaveCommitItem(handle) || 194 if (ordered_commit_set_->HaveCommitItem(handle)) {
155 item_dependencies.HaveCommitItem(handle)) { 195 // We've already added this parent (and therefore all of its parents).
196 // We can return early.
156 break; 197 break;
157 } 198 }
158 if (!AddItemThenPredecessors(trans, throttled_types, &parent, 199 if (!AddItemThenPredecessors(trans, ready_unsynced_set, parent,
159 syncable::IS_UNSYNCED,
160 &item_dependencies)) { 200 &item_dependencies)) {
161 break; // Parent was already present in the set. 201 // There was a parent/predecessor in conflict. We return without adding
202 // anything to |ordered_commit_set_|.
203 DVLOG(1) << "Parent or parent's predecessor was in conflict, omitting "
204 << item;
205 return false;
162 } 206 }
163 parent_id = parent.Get(syncable::PARENT_ID); 207 parent_id = parent.Get(syncable::PARENT_ID);
164 } 208 }
165 209
210 // Ensure that the first committed parent is not itself in conflcit.
211 if (!parent_id.IsRoot()) {
212 syncable::Entry parent(trans, syncable::GET_BY_ID, parent_id);
213 CHECK(parent.good()) << "Bad committed parent in item path.";
214 if (IsEntryInConflict(parent))
215 return false;
216 }
217
166 // Reverse what we added to get the correct order. 218 // Reverse what we added to get the correct order.
167 ordered_commit_set_->AppendReverse(item_dependencies); 219 result->AppendReverse(item_dependencies);
220 return true;
168 } 221 }
169 222
170 bool GetCommitIdsCommand::AddItem(syncable::Entry* item, 223 bool GetCommitIdsCommand::AddItem(const std::set<int64>& ready_unsynced_set,
171 syncable::ModelTypeSet throttled_types, 224 const syncable::Entry& item,
172 OrderedCommitSet* result) { 225 OrderedCommitSet* result) const {
173 if (!IsEntryReadyForCommit(encrypted_types_, passphrase_missing_, *item, 226 DCHECK(item.Get(syncable::IS_UNSYNCED));
174 throttled_types)) 227 // An item in conflict means that dependent items (successors and children)
228 // cannot be added either.
229 if (IsEntryInConflict(item))
175 return false; 230 return false;
176 int64 item_handle = item->Get(syncable::META_HANDLE); 231 int64 item_handle = item.Get(syncable::META_HANDLE);
177 if (result->HaveCommitItem(item_handle) || 232 if (ready_unsynced_set.count(item_handle) == 0) {
178 ordered_commit_set_->HaveCommitItem(item_handle)) { 233 // It's not in conflict, but not ready for commit. Just return true without
179 return false; 234 // adding it to the commit set.
235 return true;
180 } 236 }
181 result->AddCommitItem(item_handle, item->Get(syncable::ID), 237 result->AddCommitItem(item_handle, item.Get(syncable::ID),
182 item->GetModelType()); 238 item.GetModelType());
183 return true; 239 return true;
184 } 240 }
185 241
186 bool GetCommitIdsCommand::AddItemThenPredecessors( 242 bool GetCommitIdsCommand::AddItemThenPredecessors(
187 syncable::BaseTransaction* trans, 243 syncable::BaseTransaction* trans,
188 syncable::ModelTypeSet throttled_types, 244 const std::set<int64>& ready_unsynced_set,
189 syncable::Entry* item, 245 const syncable::Entry& item,
190 syncable::IndexedBitField inclusion_filter, 246 OrderedCommitSet* result) const {
191 OrderedCommitSet* result) { 247 int64 item_handle = item.Get(syncable::META_HANDLE);
192 if (!AddItem(item, throttled_types, result)) 248 if (ordered_commit_set_->HaveCommitItem(item_handle)) {
193 return false; 249 // We've already added this item to the commit set, and so must have
194 if (item->Get(syncable::IS_DEL)) 250 // already added the predecessors as well.
251 return true;
252 }
253 if (!AddItem(ready_unsynced_set, item, result))
254 return false; // Item is in conflict.
255 if (item.Get(syncable::IS_DEL))
195 return true; // Deleted items have no predecessors. 256 return true; // Deleted items have no predecessors.
196 257
197 syncable::Id prev_id = item->Get(syncable::PREV_ID); 258 syncable::Id prev_id = item.Get(syncable::PREV_ID);
198 while (!prev_id.IsRoot()) { 259 while (!prev_id.IsRoot()) {
199 syncable::Entry prev(trans, syncable::GET_BY_ID, prev_id); 260 syncable::Entry prev(trans, syncable::GET_BY_ID, prev_id);
200 CHECK(prev.good()) << "Bad id when walking predecessors."; 261 CHECK(prev.good()) << "Bad id when walking predecessors.";
201 if (!prev.Get(inclusion_filter)) 262 if (!prev.Get(syncable::IS_UNSYNCED))
202 break; 263 break;
203 if (!AddItem(&prev, throttled_types, result)) 264 int64 handle = prev.Get(syncable::META_HANDLE);
204 break; 265 if (ordered_commit_set_->HaveCommitItem(handle)) {
266 // We've already added this item to the commit set, and so must have
267 // already added the predecessors as well.
268 return true;
269 }
270 if (!AddItem(ready_unsynced_set, prev, result))
271 return false; // Item is in conflict.
205 prev_id = prev.Get(syncable::PREV_ID); 272 prev_id = prev.Get(syncable::PREV_ID);
206 } 273 }
207 return true; 274 return true;
208 } 275 }
209 276
210 void GetCommitIdsCommand::AddPredecessorsThenItem( 277 bool GetCommitIdsCommand::AddPredecessorsThenItem(
211 syncable::BaseTransaction* trans, 278 syncable::BaseTransaction* trans,
212 syncable::ModelTypeSet throttled_types, 279 const ModelSafeRoutingInfo& routes,
213 syncable::Entry* item, 280 const std::set<int64>& ready_unsynced_set,
214 syncable::IndexedBitField inclusion_filter, 281 const syncable::Entry& item,
215 const ModelSafeRoutingInfo& routes) { 282 OrderedCommitSet* result) const {
216 OrderedCommitSet item_dependencies(routes); 283 OrderedCommitSet item_dependencies(routes);
217 AddItemThenPredecessors(trans, throttled_types, item, inclusion_filter, 284 if (!AddItemThenPredecessors(trans, ready_unsynced_set, item,
218 &item_dependencies); 285 &item_dependencies)) {
286 // Either the item or its predecessors are in conflict, so don't add any
287 // items to the commit set.
288 DVLOG(1) << "Predecessor was in conflict, omitting " << item;
289 return false;
290 }
219 291
220 // Reverse what we added to get the correct order. 292 // Reverse what we added to get the correct order.
221 ordered_commit_set_->AppendReverse(item_dependencies); 293 result->AppendReverse(item_dependencies);
294 return true;
222 } 295 }
223 296
224 bool GetCommitIdsCommand::IsCommitBatchFull() { 297 bool GetCommitIdsCommand::IsCommitBatchFull() const {
225 return ordered_commit_set_->Size() >= requested_commit_batch_size_; 298 return ordered_commit_set_->Size() >= requested_commit_batch_size_;
226 } 299 }
227 300
228 void GetCommitIdsCommand::AddCreatesAndMoves( 301 void GetCommitIdsCommand::AddCreatesAndMoves(
229 const vector<int64>& unsynced_handles,
230 syncable::WriteTransaction* write_transaction, 302 syncable::WriteTransaction* write_transaction,
231 const ModelSafeRoutingInfo& routes, 303 const ModelSafeRoutingInfo& routes,
232 syncable::ModelTypeSet throttled_types) { 304 const std::set<int64>& ready_unsynced_set) {
233 // Add moves and creates, and prepend their uncommitted parents. 305 // Add moves and creates, and prepend their uncommitted parents.
234 for (CommitMetahandleIterator iterator(unsynced_handles, write_transaction, 306 for (std::set<int64>::const_iterator iter = ready_unsynced_set.begin();
235 ordered_commit_set_.get()); 307 !IsCommitBatchFull() && iter != ready_unsynced_set.end(); ++iter) {
236 !IsCommitBatchFull() && iterator.Valid(); 308 int64 metahandle = *iter;
237 iterator.Increment()) { 309 if (ordered_commit_set_->HaveCommitItem(metahandle))
238 int64 metahandle = iterator.Current(); 310 continue;
239 311
240 syncable::Entry entry(write_transaction, 312 syncable::Entry entry(write_transaction,
241 syncable::GET_BY_HANDLE, 313 syncable::GET_BY_HANDLE,
242 metahandle); 314 metahandle);
243 if (!entry.Get(syncable::IS_DEL)) { 315 if (!entry.Get(syncable::IS_DEL)) {
244 AddUncommittedParentsAndTheirPredecessors(write_transaction, 316 // We only commit an item + its dependencies if it and all its
rlarocque 2012/01/20 06:26:56 This could interact badly with conflict set proces
245 entry.Get(syncable::PARENT_ID), routes, throttled_types); 317 // dependencies are not in conflict.
246 AddPredecessorsThenItem(write_transaction, throttled_types, &entry, 318 OrderedCommitSet item_dependencies(routes);
247 syncable::IS_UNSYNCED, routes); 319 if (AddUncommittedParentsAndTheirPredecessors(
320 write_transaction,
321 routes,
322 ready_unsynced_set,
323 entry,
324 &item_dependencies) &&
325 AddPredecessorsThenItem(write_transaction,
326 routes,
327 ready_unsynced_set,
328 entry,
329 &item_dependencies)) {
330 ordered_commit_set_->Append(item_dependencies);
331 }
248 } 332 }
249 } 333 }
250 334
251 // It's possible that we overcommitted while trying to expand dependent 335 // It's possible that we overcommitted while trying to expand dependent
252 // items. If so, truncate the set down to the allowed size. 336 // items. If so, truncate the set down to the allowed size.
253 ordered_commit_set_->Truncate(requested_commit_batch_size_); 337 ordered_commit_set_->Truncate(requested_commit_batch_size_);
254 } 338 }
255 339
256 void GetCommitIdsCommand::AddDeletes(const vector<int64>& unsynced_handles, 340 void GetCommitIdsCommand::AddDeletes(
257 syncable::WriteTransaction* write_transaction) { 341 syncable::WriteTransaction* write_transaction,
342 const std::set<int64>& ready_unsynced_set) {
258 set<syncable::Id> legal_delete_parents; 343 set<syncable::Id> legal_delete_parents;
259 344
260 for (CommitMetahandleIterator iterator(unsynced_handles, write_transaction, 345 for (std::set<int64>::const_iterator iter = ready_unsynced_set.begin();
261 ordered_commit_set_.get()); 346 !IsCommitBatchFull() && iter != ready_unsynced_set.end(); ++iter) {
262 !IsCommitBatchFull() && iterator.Valid(); 347 int64 metahandle = *iter;
263 iterator.Increment()) { 348 if (ordered_commit_set_->HaveCommitItem(metahandle))
264 int64 metahandle = iterator.Current(); 349 continue;
265 350
266 syncable::Entry entry(write_transaction, syncable::GET_BY_HANDLE, 351 syncable::Entry entry(write_transaction, syncable::GET_BY_HANDLE,
267 metahandle); 352 metahandle);
268 353
269 if (entry.Get(syncable::IS_DEL)) { 354 if (entry.Get(syncable::IS_DEL)) {
270 syncable::Entry parent(write_transaction, syncable::GET_BY_ID, 355 syncable::Entry parent(write_transaction, syncable::GET_BY_ID,
271 entry.Get(syncable::PARENT_ID)); 356 entry.Get(syncable::PARENT_ID));
357 // We do not commit entries that have a parent in conflict. By not
358 // adding any of its parents to legal_delete_parents, we ensure none
359 // be committed.
360 if (parent.good() && IsEntryInConflict(parent))
361 continue;
362
272 // If the parent is deleted and unsynced, then any children of that 363 // If the parent is deleted and unsynced, then any children of that
273 // parent don't need to be added to the delete queue. 364 // parent don't need to be added to the delete queue.
274 // 365 //
275 // Note: the parent could be synced if there was an update deleting a 366 // Note: the parent could be synced if there was an update deleting a
276 // folder when we had a deleted all items in it. 367 // folder when we had a deleted all items in it.
277 // We may get more updates, or we may want to delete the entry. 368 // We may get more updates, or we may want to delete the entry.
278 if (parent.good() && 369 if (parent.good() &&
279 parent.Get(syncable::IS_DEL) && 370 parent.Get(syncable::IS_DEL) &&
280 parent.Get(syncable::IS_UNSYNCED)) { 371 parent.Get(syncable::IS_UNSYNCED)) {
281 // However, if an entry is moved, these rules can apply differently. 372 // However, if an entry is moved, these rules can apply differently.
(...skipping 30 matching lines...) Expand all
312 // examined entries. 403 // examined entries.
313 // 404 //
314 // Scan through the UnsyncedMetaHandles again. If we have a deleted 405 // Scan through the UnsyncedMetaHandles again. If we have a deleted
315 // entry, then check if the parent is in legal_delete_parents. 406 // entry, then check if the parent is in legal_delete_parents.
316 // 407 //
317 // Parent being in legal_delete_parents means for the child: 408 // Parent being in legal_delete_parents means for the child:
318 // a recursive delete is not currently happening (no recent deletes in same 409 // a recursive delete is not currently happening (no recent deletes in same
319 // folder) 410 // folder)
320 // parent did expect at least one old deleted child 411 // parent did expect at least one old deleted child
321 // parent was not deleted 412 // parent was not deleted
322 413 for (std::set<int64>::const_iterator iter = ready_unsynced_set.begin();
323 for (CommitMetahandleIterator iterator(unsynced_handles, write_transaction, 414 !IsCommitBatchFull() && iter != ready_unsynced_set.end(); ++iter) {
324 ordered_commit_set_.get()); 415 int64 metahandle = *iter;
325 !IsCommitBatchFull() && iterator.Valid(); 416 if (ordered_commit_set_->HaveCommitItem(metahandle))
326 iterator.Increment()) { 417 continue;
327 int64 metahandle = iterator.Current();
328 syncable::MutableEntry entry(write_transaction, syncable::GET_BY_HANDLE, 418 syncable::MutableEntry entry(write_transaction, syncable::GET_BY_HANDLE,
329 metahandle); 419 metahandle);
330 if (entry.Get(syncable::IS_DEL)) { 420 if (entry.Get(syncable::IS_DEL)) {
331 syncable::Id parent_id = entry.Get(syncable::PARENT_ID); 421 syncable::Id parent_id = entry.Get(syncable::PARENT_ID);
332 if (legal_delete_parents.count(parent_id)) { 422 if (legal_delete_parents.count(parent_id)) {
333 ordered_commit_set_->AddCommitItem(metahandle, entry.Get(syncable::ID), 423 ordered_commit_set_->AddCommitItem(metahandle, entry.Get(syncable::ID),
334 entry.GetModelType()); 424 entry.GetModelType());
335 } 425 }
336 } 426 }
337 } 427 }
338 } 428 }
339 429
340 void GetCommitIdsCommand::BuildCommitIds(const vector<int64>& unsynced_handles, 430 void GetCommitIdsCommand::BuildCommitIds(
341 syncable::WriteTransaction* write_transaction, 431 syncable::WriteTransaction* write_transaction,
342 const ModelSafeRoutingInfo& routes, 432 const ModelSafeRoutingInfo& routes,
343 syncable::ModelTypeSet throttled_types) { 433 const std::set<int64>& ready_unsynced_set) {
344 ordered_commit_set_.reset(new OrderedCommitSet(routes)); 434 ordered_commit_set_.reset(new OrderedCommitSet(routes));
345 // Commits follow these rules: 435 // Commits follow these rules:
346 // 1. Moves or creates are preceded by needed folder creates, from 436 // 1. Moves or creates are preceded by needed folder creates, from
347 // root to leaf. For folders whose contents are ordered, moves 437 // root to leaf. For folders whose contents are ordered, moves
348 // and creates appear in order. 438 // and creates appear in order.
349 // 2. Moves/Creates before deletes. 439 // 2. Moves/Creates before deletes.
350 // 3. Deletes, collapsed. 440 // 3. Deletes, collapsed.
351 // We commit deleted moves under deleted items as moves when collapsing 441 // We commit deleted moves under deleted items as moves when collapsing
352 // delete trees. 442 // delete trees.
353 443
354 // Add moves and creates, and prepend their uncommitted parents. 444 // Add moves and creates, and prepend their uncommitted parents.
355 AddCreatesAndMoves(unsynced_handles, write_transaction, routes, 445 AddCreatesAndMoves(write_transaction, routes, ready_unsynced_set);
356 throttled_types);
357 446
358 // Add all deletes. 447 // Add all deletes.
359 AddDeletes(unsynced_handles, write_transaction); 448 AddDeletes(write_transaction, ready_unsynced_set);
360 } 449 }
361 450
362 } // namespace browser_sync 451 } // namespace browser_sync
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698