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

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: Ensure we clear BASE_SERVER_SPECIFICS 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) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 void GetCommitIdsCommand::ExecuteImpl(SyncSession* session) { 31 void 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 } 77 }
69 78
70 namespace { 79 namespace {
71 80
72 // An entry ready for commit is defined as: 81 bool IsEntryInConflict(const syncable::Entry& entry) {
73 // 1. Not in conflict (SERVER_VERSION == BASE_VERSION || SERVER_VERSION == 0) 82 if (entry.Get(syncable::IS_UNSYNCED) &&
74 // and not requiring encryption (any entry containing an encrypted datatype 83 entry.Get(syncable::SERVER_VERSION) > 0 &&
75 // while the cryptographer requires a passphrase is not ready for commit.)
76 // 2. Its type is not currently throttled.
77 bool IsEntryReadyForCommit(syncable::ModelTypeSet encrypted_types,
78 bool passphrase_missing,
79 const syncable::Entry& entry,
80 syncable::ModelTypeSet throttled_types) {
81 if (!entry.Get(syncable::IS_UNSYNCED))
82 return false;
83
84 if (entry.Get(syncable::SERVER_VERSION) > 0 &&
85 (entry.Get(syncable::SERVER_VERSION) > 84 (entry.Get(syncable::SERVER_VERSION) >
86 entry.Get(syncable::BASE_VERSION))) { 85 entry.Get(syncable::BASE_VERSION))) {
87 // The local and server versions don't match. The item must be in 86 // The local and server versions don't match. The item must be in
88 // conflict, so there's no point in attempting to commit. 87 // conflict, so there's no point in attempting to commit.
89 DCHECK(entry.Get(syncable::IS_UNAPPLIED_UPDATE)); // In conflict. 88 DCHECK(entry.Get(syncable::IS_UNAPPLIED_UPDATE));
90 DVLOG(1) << "Excluding entry from commit due to version mismatch " 89 DVLOG(1) << "Excluding entry from commit due to version mismatch "
91 << entry; 90 << entry;
91 return true;
92 }
93 return false;
94 }
95
96 // An entry is not considered ready for commit if any are true:
97 // 1. It's in conflict.
98 // 2. It requires encryption (either the type is encrypted but a passphrase
99 // is missing from the cryptographer, or the entry itself wasn't properly
100 // encrypted).
101 // 3. It's type is currently throttled.
102 // 4. It's a delete but has not been committed.
103 bool IsEntryReadyForCommit(syncable::ModelTypeSet throttled_types,
104 syncable::ModelTypeSet encrypted_types,
105 bool passphrase_missing,
106 const syncable::Entry& entry) {
107 DCHECK(entry.Get(syncable::IS_UNSYNCED));
108 if (IsEntryInConflict(entry))
92 return false; 109 return false;
93 }
94 110
95 const syncable::ModelType type = entry.GetModelType(); 111 const syncable::ModelType type = entry.GetModelType();
96 // We special case the nigori node because even though it is considered an 112 // We special case the nigori node because even though it is considered an
97 // "encrypted type", not all nigori node changes require valid encryption 113 // "encrypted type", not all nigori node changes require valid encryption
98 // (ex: sync_tabs). 114 // (ex: sync_tabs).
99 if ((type != syncable::NIGORI) && 115 if ((type != syncable::NIGORI) &&
100 encrypted_types.Has(type) && 116 encrypted_types.Has(type) &&
101 (passphrase_missing || 117 (passphrase_missing ||
102 syncable::EntryNeedsEncryption(encrypted_types, entry))) { 118 syncable::EntryNeedsEncryption(encrypted_types, entry))) {
103 // This entry requires encryption but is not properly encrypted (possibly 119 // This entry requires encryption but is not properly encrypted (possibly
104 // due to the cryptographer not being initialized or the user hasn't 120 // due to the cryptographer not being initialized or the user hasn't
105 // provided the most recent passphrase). 121 // provided the most recent passphrase).
106 DVLOG(1) << "Excluding entry from commit due to lack of encryption " 122 DVLOG(1) << "Excluding entry from commit due to lack of encryption "
107 << entry; 123 << entry;
108 return false; 124 return false;
109 } 125 }
110 126
111 // Look at the throttled types. 127 // Look at the throttled types.
112 if (throttled_types.Has(type)) 128 if (throttled_types.Has(type))
113 return false; 129 return false;
114 130
131 // Drop deleted uncommitted entries.
132 if (entry.Get(syncable::IS_DEL) && !entry.Get(syncable::ID).ServerKnows()) {
133 // TODO(zea): These will remain unsynced indefinitely. This is harmless,
134 // but we should clean them up somewhere.
135 DVLOG(1) << "Ignoring deleted and uncommitted item." << entry;
136 return false;
137 }
138
139 // Extra validity checks.
140 syncable::Id id = entry.Get(syncable::ID);
141 if (id == entry.Get(syncable::PARENT_ID)) {
142 CHECK(id.IsRoot()) << "Non-root item is self parenting." << entry;
143 // If the root becomes unsynced it can cause us problems.
144 LOG(ERROR) << "Root item became unsynced " << entry;
145 return false;
146 }
147
148 if (entry.IsRoot()) {
149 LOG(ERROR) << "Permanent item became unsynced " << entry;
150 return false;
151 }
152
153 DVLOG(2) << "Entry is ready for commit: " << entry;
115 return true; 154 return true;
116 } 155 }
117 156
118 } // namespace 157 } // namespace
119 158
120 void GetCommitIdsCommand::FilterUnreadyEntries( 159 void GetCommitIdsCommand::FilterUnreadyEntries(
121 syncable::BaseTransaction* trans, 160 syncable::BaseTransaction* trans,
122 syncable::ModelTypeSet throttled_types, 161 syncable::ModelTypeSet throttled_types,
123 syncable::Directory::UnsyncedMetaHandles* unsynced_handles) { 162 syncable::ModelTypeSet encrypted_types,
124 syncable::Directory::UnsyncedMetaHandles::iterator iter; 163 bool passphrase_missing,
125 syncable::Directory::UnsyncedMetaHandles new_unsynced_handles; 164 const syncable::Directory::UnsyncedMetaHandles& unsynced_handles,
126 new_unsynced_handles.reserve(unsynced_handles->size()); 165 std::set<int64>* ready_unsynced_set) {
127 for (iter = unsynced_handles->begin(); 166 for (syncable::Directory::UnsyncedMetaHandles::const_iterator iter =
128 iter != unsynced_handles->end(); 167 unsynced_handles.begin(); iter != unsynced_handles.end(); ++iter) {
129 ++iter) {
130 syncable::Entry entry(trans, syncable::GET_BY_HANDLE, *iter); 168 syncable::Entry entry(trans, syncable::GET_BY_HANDLE, *iter);
131 if (IsEntryReadyForCommit(encrypted_types_, 169 if (entry.Get(syncable::IS_UNSYNCED) &&
132 passphrase_missing_, 170 IsEntryReadyForCommit(throttled_types,
133 entry, 171 encrypted_types,
134 throttled_types)) 172 passphrase_missing,
135 new_unsynced_handles.push_back(*iter); 173 entry)) {
174 ready_unsynced_set->insert(*iter);
175 }
136 } 176 }
137 if (new_unsynced_handles.size() != unsynced_handles->size())
138 unsynced_handles->swap(new_unsynced_handles);
139 } 177 }
140 178
141 void GetCommitIdsCommand::AddUncommittedParentsAndTheirPredecessors( 179 bool GetCommitIdsCommand::AddUncommittedParentsAndTheirPredecessors(
142 syncable::BaseTransaction* trans, 180 syncable::BaseTransaction* trans,
143 syncable::Id parent_id,
144 const ModelSafeRoutingInfo& routes, 181 const ModelSafeRoutingInfo& routes,
145 syncable::ModelTypeSet throttled_types) { 182 const std::set<int64>& ready_unsynced_set,
183 const syncable::Entry& item,
184 sessions::OrderedCommitSet* result) const {
146 OrderedCommitSet item_dependencies(routes); 185 OrderedCommitSet item_dependencies(routes);
186 syncable::Id parent_id = item.Get(syncable::PARENT_ID);
147 187
148 // Climb the tree adding entries leaf -> root. 188 // Climb the tree adding entries leaf -> root.
149 while (!parent_id.ServerKnows()) { 189 while (!parent_id.ServerKnows()) {
150 syncable::Entry parent(trans, syncable::GET_BY_ID, parent_id); 190 syncable::Entry parent(trans, syncable::GET_BY_ID, parent_id);
151 CHECK(parent.good()) << "Bad user-only parent in item path."; 191 CHECK(parent.good()) << "Bad user-only parent in item path.";
152 int64 handle = parent.Get(syncable::META_HANDLE); 192 int64 handle = parent.Get(syncable::META_HANDLE);
153 if (ordered_commit_set_->HaveCommitItem(handle) || 193 if (ordered_commit_set_->HaveCommitItem(handle)) {
154 item_dependencies.HaveCommitItem(handle)) { 194 // We've already added this parent (and therefore all of its parents).
195 // We can return early.
155 break; 196 break;
156 } 197 }
157 if (!AddItemThenPredecessors(trans, throttled_types, &parent, 198 if (!AddItemThenPredecessors(trans, ready_unsynced_set, parent,
158 syncable::IS_UNSYNCED,
159 &item_dependencies)) { 199 &item_dependencies)) {
160 break; // Parent was already present in the set. 200 // There was a parent/predecessor in conflict. We return without adding
201 // anything to |ordered_commit_set_|.
202 DVLOG(1) << "Parent or parent's predecessor was in conflict, omitting "
203 << item;
204 return false;
161 } 205 }
162 parent_id = parent.Get(syncable::PARENT_ID); 206 parent_id = parent.Get(syncable::PARENT_ID);
163 } 207 }
164 208
209 // Ensure that the first committed parent is not itself in conflcit.
210 if (!parent_id.IsRoot()) {
211 syncable::Entry parent(trans, syncable::GET_BY_ID, parent_id);
212 CHECK(parent.good()) << "Bad committed parent in item path.";
213 if (IsEntryInConflict(parent))
214 return false;
215 }
216
165 // Reverse what we added to get the correct order. 217 // Reverse what we added to get the correct order.
166 ordered_commit_set_->AppendReverse(item_dependencies); 218 result->AppendReverse(item_dependencies);
219 return true;
167 } 220 }
168 221
169 bool GetCommitIdsCommand::AddItem(syncable::Entry* item, 222 bool GetCommitIdsCommand::AddItem(const std::set<int64>& ready_unsynced_set,
170 syncable::ModelTypeSet throttled_types, 223 const syncable::Entry& item,
171 OrderedCommitSet* result) { 224 OrderedCommitSet* result) const {
172 if (!IsEntryReadyForCommit(encrypted_types_, passphrase_missing_, *item, 225 DCHECK(item.Get(syncable::IS_UNSYNCED));
173 throttled_types)) 226 // An item in conflict means that dependent items (successors and children)
227 // cannot be added either.
228 if (IsEntryInConflict(item))
174 return false; 229 return false;
175 int64 item_handle = item->Get(syncable::META_HANDLE); 230 int64 item_handle = item.Get(syncable::META_HANDLE);
176 if (result->HaveCommitItem(item_handle) || 231 if (ready_unsynced_set.count(item_handle) == 0) {
177 ordered_commit_set_->HaveCommitItem(item_handle)) { 232 // It's not in conflict, but not ready for commit. Just return true without
178 return false; 233 // adding it to the commit set.
234 return true;
179 } 235 }
180 result->AddCommitItem(item_handle, item->Get(syncable::ID), 236 result->AddCommitItem(item_handle, item.Get(syncable::ID),
181 item->GetModelType()); 237 item.GetModelType());
182 return true; 238 return true;
183 } 239 }
184 240
185 bool GetCommitIdsCommand::AddItemThenPredecessors( 241 bool GetCommitIdsCommand::AddItemThenPredecessors(
186 syncable::BaseTransaction* trans, 242 syncable::BaseTransaction* trans,
187 syncable::ModelTypeSet throttled_types, 243 const std::set<int64>& ready_unsynced_set,
188 syncable::Entry* item, 244 const syncable::Entry& item,
189 syncable::IndexedBitField inclusion_filter, 245 OrderedCommitSet* result) const {
190 OrderedCommitSet* result) { 246 int64 item_handle = item.Get(syncable::META_HANDLE);
191 if (!AddItem(item, throttled_types, result)) 247 if (ordered_commit_set_->HaveCommitItem(item_handle)) {
192 return false; 248 // We've already added this item to the commit set, and so must have
193 if (item->Get(syncable::IS_DEL)) 249 // already added the predecessors as well.
250 return true;
251 }
252 if (!AddItem(ready_unsynced_set, item, result))
253 return false; // Item is in conflict.
254 if (item.Get(syncable::IS_DEL))
194 return true; // Deleted items have no predecessors. 255 return true; // Deleted items have no predecessors.
195 256
196 syncable::Id prev_id = item->Get(syncable::PREV_ID); 257 syncable::Id prev_id = item.Get(syncable::PREV_ID);
197 while (!prev_id.IsRoot()) { 258 while (!prev_id.IsRoot()) {
198 syncable::Entry prev(trans, syncable::GET_BY_ID, prev_id); 259 syncable::Entry prev(trans, syncable::GET_BY_ID, prev_id);
199 CHECK(prev.good()) << "Bad id when walking predecessors."; 260 CHECK(prev.good()) << "Bad id when walking predecessors.";
200 if (!prev.Get(inclusion_filter)) 261 if (!prev.Get(syncable::IS_UNSYNCED))
201 break; 262 break;
202 if (!AddItem(&prev, throttled_types, result)) 263 int64 handle = prev.Get(syncable::META_HANDLE);
203 break; 264 if (ordered_commit_set_->HaveCommitItem(handle)) {
265 // We've already added this item to the commit set, and so must have
266 // already added the predecessors as well.
267 return true;
268 }
269 if (!AddItem(ready_unsynced_set, prev, result))
270 return false; // Item is in conflict.
204 prev_id = prev.Get(syncable::PREV_ID); 271 prev_id = prev.Get(syncable::PREV_ID);
205 } 272 }
206 return true; 273 return true;
207 } 274 }
208 275
209 void GetCommitIdsCommand::AddPredecessorsThenItem( 276 bool GetCommitIdsCommand::AddPredecessorsThenItem(
210 syncable::BaseTransaction* trans, 277 syncable::BaseTransaction* trans,
211 syncable::ModelTypeSet throttled_types, 278 const ModelSafeRoutingInfo& routes,
212 syncable::Entry* item, 279 const std::set<int64>& ready_unsynced_set,
213 syncable::IndexedBitField inclusion_filter, 280 const syncable::Entry& item,
214 const ModelSafeRoutingInfo& routes) { 281 OrderedCommitSet* result) const {
215 OrderedCommitSet item_dependencies(routes); 282 OrderedCommitSet item_dependencies(routes);
216 AddItemThenPredecessors(trans, throttled_types, item, inclusion_filter, 283 if (!AddItemThenPredecessors(trans, ready_unsynced_set, item,
217 &item_dependencies); 284 &item_dependencies)) {
285 // Either the item or its predecessors are in conflict, so don't add any
286 // items to the commit set.
287 DVLOG(1) << "Predecessor was in conflict, omitting " << item;
288 return false;
289 }
218 290
219 // Reverse what we added to get the correct order. 291 // Reverse what we added to get the correct order.
220 ordered_commit_set_->AppendReverse(item_dependencies); 292 result->AppendReverse(item_dependencies);
293 return true;
221 } 294 }
222 295
223 bool GetCommitIdsCommand::IsCommitBatchFull() { 296 bool GetCommitIdsCommand::IsCommitBatchFull() const {
224 return ordered_commit_set_->Size() >= requested_commit_batch_size_; 297 return ordered_commit_set_->Size() >= requested_commit_batch_size_;
225 } 298 }
226 299
227 void GetCommitIdsCommand::AddCreatesAndMoves( 300 void GetCommitIdsCommand::AddCreatesAndMoves(
228 const vector<int64>& unsynced_handles,
229 syncable::WriteTransaction* write_transaction, 301 syncable::WriteTransaction* write_transaction,
230 const ModelSafeRoutingInfo& routes, 302 const ModelSafeRoutingInfo& routes,
231 syncable::ModelTypeSet throttled_types) { 303 const std::set<int64>& ready_unsynced_set) {
232 // Add moves and creates, and prepend their uncommitted parents. 304 // Add moves and creates, and prepend their uncommitted parents.
233 for (CommitMetahandleIterator iterator(unsynced_handles, write_transaction, 305 for (std::set<int64>::const_iterator iter = ready_unsynced_set.begin();
234 ordered_commit_set_.get()); 306 !IsCommitBatchFull() && iter != ready_unsynced_set.end(); ++iter) {
235 !IsCommitBatchFull() && iterator.Valid(); 307 int64 metahandle = *iter;
236 iterator.Increment()) { 308 if (ordered_commit_set_->HaveCommitItem(metahandle))
237 int64 metahandle = iterator.Current(); 309 continue;
238 310
239 syncable::Entry entry(write_transaction, 311 syncable::Entry entry(write_transaction,
240 syncable::GET_BY_HANDLE, 312 syncable::GET_BY_HANDLE,
241 metahandle); 313 metahandle);
242 if (!entry.Get(syncable::IS_DEL)) { 314 if (!entry.Get(syncable::IS_DEL)) {
243 AddUncommittedParentsAndTheirPredecessors(write_transaction, 315 // We only commit an item + its dependencies if it and all its
244 entry.Get(syncable::PARENT_ID), routes, throttled_types); 316 // dependencies are not in conflict.
245 AddPredecessorsThenItem(write_transaction, throttled_types, &entry, 317 OrderedCommitSet item_dependencies(routes);
246 syncable::IS_UNSYNCED, routes); 318 if (AddUncommittedParentsAndTheirPredecessors(
319 write_transaction,
320 routes,
321 ready_unsynced_set,
322 entry,
323 &item_dependencies) &&
324 AddPredecessorsThenItem(write_transaction,
325 routes,
326 ready_unsynced_set,
327 entry,
328 &item_dependencies)) {
329 ordered_commit_set_->Append(item_dependencies);
330 }
247 } 331 }
248 } 332 }
249 333
250 // It's possible that we overcommitted while trying to expand dependent 334 // It's possible that we overcommitted while trying to expand dependent
251 // items. If so, truncate the set down to the allowed size. 335 // items. If so, truncate the set down to the allowed size.
252 ordered_commit_set_->Truncate(requested_commit_batch_size_); 336 ordered_commit_set_->Truncate(requested_commit_batch_size_);
253 } 337 }
254 338
255 void GetCommitIdsCommand::AddDeletes(const vector<int64>& unsynced_handles, 339 void GetCommitIdsCommand::AddDeletes(
256 syncable::WriteTransaction* write_transaction) { 340 syncable::WriteTransaction* write_transaction,
341 const std::set<int64>& ready_unsynced_set) {
257 set<syncable::Id> legal_delete_parents; 342 set<syncable::Id> legal_delete_parents;
258 343
259 for (CommitMetahandleIterator iterator(unsynced_handles, write_transaction, 344 for (std::set<int64>::const_iterator iter = ready_unsynced_set.begin();
260 ordered_commit_set_.get()); 345 !IsCommitBatchFull() && iter != ready_unsynced_set.end(); ++iter) {
261 !IsCommitBatchFull() && iterator.Valid(); 346 int64 metahandle = *iter;
262 iterator.Increment()) { 347 if (ordered_commit_set_->HaveCommitItem(metahandle))
263 int64 metahandle = iterator.Current(); 348 continue;
264 349
265 syncable::Entry entry(write_transaction, syncable::GET_BY_HANDLE, 350 syncable::Entry entry(write_transaction, syncable::GET_BY_HANDLE,
266 metahandle); 351 metahandle);
267 352
268 if (entry.Get(syncable::IS_DEL)) { 353 if (entry.Get(syncable::IS_DEL)) {
269 syncable::Entry parent(write_transaction, syncable::GET_BY_ID, 354 syncable::Entry parent(write_transaction, syncable::GET_BY_ID,
270 entry.Get(syncable::PARENT_ID)); 355 entry.Get(syncable::PARENT_ID));
356 // We do not commit entries that have a parent in conflict. By not
357 // adding any of its parents to legal_delete_parents, we ensure none
358 // be committed.
359 if (parent.good() && IsEntryInConflict(parent))
360 continue;
361
271 // If the parent is deleted and unsynced, then any children of that 362 // If the parent is deleted and unsynced, then any children of that
272 // parent don't need to be added to the delete queue. 363 // parent don't need to be added to the delete queue.
273 // 364 //
274 // Note: the parent could be synced if there was an update deleting a 365 // Note: the parent could be synced if there was an update deleting a
275 // folder when we had a deleted all items in it. 366 // folder when we had a deleted all items in it.
276 // We may get more updates, or we may want to delete the entry. 367 // We may get more updates, or we may want to delete the entry.
277 if (parent.good() && 368 if (parent.good() &&
278 parent.Get(syncable::IS_DEL) && 369 parent.Get(syncable::IS_DEL) &&
279 parent.Get(syncable::IS_UNSYNCED)) { 370 parent.Get(syncable::IS_UNSYNCED)) {
280 // However, if an entry is moved, these rules can apply differently. 371 // However, if an entry is moved, these rules can apply differently.
(...skipping 30 matching lines...) Expand all
311 // examined entries. 402 // examined entries.
312 // 403 //
313 // Scan through the UnsyncedMetaHandles again. If we have a deleted 404 // Scan through the UnsyncedMetaHandles again. If we have a deleted
314 // entry, then check if the parent is in legal_delete_parents. 405 // entry, then check if the parent is in legal_delete_parents.
315 // 406 //
316 // Parent being in legal_delete_parents means for the child: 407 // Parent being in legal_delete_parents means for the child:
317 // a recursive delete is not currently happening (no recent deletes in same 408 // a recursive delete is not currently happening (no recent deletes in same
318 // folder) 409 // folder)
319 // parent did expect at least one old deleted child 410 // parent did expect at least one old deleted child
320 // parent was not deleted 411 // parent was not deleted
321 412 for (std::set<int64>::const_iterator iter = ready_unsynced_set.begin();
322 for (CommitMetahandleIterator iterator(unsynced_handles, write_transaction, 413 !IsCommitBatchFull() && iter != ready_unsynced_set.end(); ++iter) {
323 ordered_commit_set_.get()); 414 int64 metahandle = *iter;
324 !IsCommitBatchFull() && iterator.Valid(); 415 if (ordered_commit_set_->HaveCommitItem(metahandle))
325 iterator.Increment()) { 416 continue;
326 int64 metahandle = iterator.Current();
327 syncable::MutableEntry entry(write_transaction, syncable::GET_BY_HANDLE, 417 syncable::MutableEntry entry(write_transaction, syncable::GET_BY_HANDLE,
328 metahandle); 418 metahandle);
329 if (entry.Get(syncable::IS_DEL)) { 419 if (entry.Get(syncable::IS_DEL)) {
330 syncable::Id parent_id = entry.Get(syncable::PARENT_ID); 420 syncable::Id parent_id = entry.Get(syncable::PARENT_ID);
331 if (legal_delete_parents.count(parent_id)) { 421 if (legal_delete_parents.count(parent_id)) {
332 ordered_commit_set_->AddCommitItem(metahandle, entry.Get(syncable::ID), 422 ordered_commit_set_->AddCommitItem(metahandle, entry.Get(syncable::ID),
333 entry.GetModelType()); 423 entry.GetModelType());
334 } 424 }
335 } 425 }
336 } 426 }
337 } 427 }
338 428
339 void GetCommitIdsCommand::BuildCommitIds(const vector<int64>& unsynced_handles, 429 void GetCommitIdsCommand::BuildCommitIds(
340 syncable::WriteTransaction* write_transaction, 430 syncable::WriteTransaction* write_transaction,
341 const ModelSafeRoutingInfo& routes, 431 const ModelSafeRoutingInfo& routes,
342 syncable::ModelTypeSet throttled_types) { 432 const std::set<int64>& ready_unsynced_set) {
343 ordered_commit_set_.reset(new OrderedCommitSet(routes)); 433 ordered_commit_set_.reset(new OrderedCommitSet(routes));
344 // Commits follow these rules: 434 // Commits follow these rules:
345 // 1. Moves or creates are preceded by needed folder creates, from 435 // 1. Moves or creates are preceded by needed folder creates, from
346 // root to leaf. For folders whose contents are ordered, moves 436 // root to leaf. For folders whose contents are ordered, moves
347 // and creates appear in order. 437 // and creates appear in order.
348 // 2. Moves/Creates before deletes. 438 // 2. Moves/Creates before deletes.
349 // 3. Deletes, collapsed. 439 // 3. Deletes, collapsed.
350 // We commit deleted moves under deleted items as moves when collapsing 440 // We commit deleted moves under deleted items as moves when collapsing
351 // delete trees. 441 // delete trees.
352 442
353 // Add moves and creates, and prepend their uncommitted parents. 443 // Add moves and creates, and prepend their uncommitted parents.
354 AddCreatesAndMoves(unsynced_handles, write_transaction, routes, 444 AddCreatesAndMoves(write_transaction, routes, ready_unsynced_set);
355 throttled_types);
356 445
357 // Add all deletes. 446 // Add all deletes.
358 AddDeletes(unsynced_handles, write_transaction); 447 AddDeletes(write_transaction, ready_unsynced_set);
359 } 448 }
360 449
361 } // namespace browser_sync 450 } // namespace browser_sync
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698