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

Side by Side Diff: components/sync/engine_impl/get_commit_ids.cc

Issue 2826473002: [Sync] Refactor get_commit_ids.cc without any functional changes. (Closed)
Patch Set: Adding back namespace to static function. Created 3 years, 8 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 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 "components/sync/engine_impl/get_commit_ids.h" 5 #include "components/sync/engine_impl/get_commit_ids.h"
6 6
7 #include <set> 7 #include <set>
8 8
9 #include "base/macros.h" 9 #include "base/macros.h"
10 #include "components/sync/base/cryptographer.h" 10 #include "components/sync/base/cryptographer.h"
11 #include "components/sync/engine_impl/syncer_util.h" 11 #include "components/sync/engine_impl/syncer_util.h"
12 #include "components/sync/syncable/entry.h" 12 #include "components/sync/syncable/entry.h"
13 #include "components/sync/syncable/nigori_handler.h" 13 #include "components/sync/syncable/nigori_handler.h"
14 #include "components/sync/syncable/nigori_util.h" 14 #include "components/sync/syncable/nigori_util.h"
15 #include "components/sync/syncable/syncable_base_transaction.h" 15 #include "components/sync/syncable/syncable_base_transaction.h"
16 #include "components/sync/syncable/syncable_util.h" 16 #include "components/sync/syncable/syncable_util.h"
17 17
18 using std::set; 18 namespace syncer {
19 using std::vector;
20 19
21 namespace syncer { 20 using syncable::Directory;
21 using syncable::Entry;
22 22
23 namespace { 23 namespace {
24 24
25 // Forward-declare some helper functions. This gives us more options for 25 bool IsEntryInConflict(const Entry& entry) {
26 // ordering the function defintions within this file.
27
28 // Filters |unsynced_handles| to remove all entries that do not belong to the
29 // specified |requested_types|, or are not eligible for a commit at this time.
30 void FilterUnreadyEntries(
31 syncable::BaseTransaction* trans,
32 ModelTypeSet requested_types,
33 ModelTypeSet encrypted_types,
34 bool passphrase_missing,
35 const syncable::Directory::Metahandles& unsynced_handles,
36 std::set<int64_t>* ready_unsynced_set);
37
38 // Given a set of commit metahandles that are ready for commit
39 // (|ready_unsynced_set|), sorts these into commit order and places up to
40 // |max_entries| of them in the output parameter |out|.
41 //
42 // See the header file for an explanation of commit ordering.
43 void OrderCommitIds(syncable::BaseTransaction* trans,
44 size_t max_entries,
45 const std::set<int64_t>& ready_unsynced_set,
46 std::vector<int64_t>* out);
47
48 } // namespace
49
50 void GetCommitIdsForType(syncable::BaseTransaction* trans,
51 ModelType type,
52 size_t max_entries,
53 syncable::Directory::Metahandles* out) {
54 syncable::Directory* dir = trans->directory();
55
56 // Gather the full set of unsynced items and store it in the cycle. They
57 // are not in the correct order for commit.
58 std::set<int64_t> ready_unsynced_set;
59 syncable::Directory::Metahandles all_unsynced_handles;
60 GetUnsyncedEntries(trans, &all_unsynced_handles);
61
62 ModelTypeSet encrypted_types;
63 bool passphrase_missing = false;
64 Cryptographer* cryptographer = dir->GetCryptographer(trans);
65 if (cryptographer) {
66 encrypted_types = dir->GetNigoriHandler()->GetEncryptedTypes(trans);
67 passphrase_missing = cryptographer->has_pending_keys();
68 }
69
70 // We filter out all unready entries from the set of unsynced handles. This
71 // new set of ready and unsynced items is then what we use to determine what
72 // is a candidate for commit. The caller is responsible for ensuring that no
73 // throttled types are included among the requested_types.
74 FilterUnreadyEntries(trans, ModelTypeSet(type), encrypted_types,
75 passphrase_missing, all_unsynced_handles,
76 &ready_unsynced_set);
77
78 OrderCommitIds(trans, max_entries, ready_unsynced_set, out);
79
80 for (size_t i = 0; i < out->size(); i++) {
81 DVLOG(1) << "Debug commit batch result:" << (*out)[i];
82 }
83 }
84
85 namespace {
86
87 bool IsEntryInConflict(const syncable::Entry& entry) {
88 if (entry.GetIsUnsynced() && entry.GetServerVersion() > 0 && 26 if (entry.GetIsUnsynced() && entry.GetServerVersion() > 0 &&
89 (entry.GetServerVersion() > entry.GetBaseVersion())) { 27 (entry.GetServerVersion() > entry.GetBaseVersion())) {
90 // The local and server versions don't match. The item must be in 28 // The local and server versions don't match. The item must be in
91 // conflict, so there's no point in attempting to commit. 29 // conflict, so there's no point in attempting to commit.
92 DCHECK(entry.GetIsUnappliedUpdate()); 30 DCHECK(entry.GetIsUnappliedUpdate());
93 DVLOG(1) << "Excluding entry from commit due to version mismatch " << entry; 31 DVLOG(1) << "Excluding entry from commit due to version mismatch " << entry;
94 return true; 32 return true;
95 } 33 }
96 return false; 34 return false;
97 } 35 }
98 36
99 // Return true if this entry has any attachments that haven't yet been uploaded 37 // Return true if this entry has any attachments that haven't yet been uploaded
100 // to the server. 38 // to the server.
101 bool HasAttachmentNotOnServer(const syncable::Entry& entry) { 39 bool HasAttachmentNotOnServer(const Entry& entry) {
102 const sync_pb::AttachmentMetadata& metadata = entry.GetAttachmentMetadata(); 40 const sync_pb::AttachmentMetadata& metadata = entry.GetAttachmentMetadata();
103 for (int i = 0; i < metadata.record_size(); ++i) { 41 for (int i = 0; i < metadata.record_size(); ++i) {
104 if (!metadata.record(i).is_on_server()) { 42 if (!metadata.record(i).is_on_server()) {
105 return true; 43 return true;
106 } 44 }
107 } 45 }
108 return false; 46 return false;
109 } 47 }
110 48
111 // An entry may not commit if any are true: 49 // An entry may not commit if any are true:
112 // 1. It requires encryption (either the type is encrypted but a passphrase 50 // 1. It requires encryption (either the type is encrypted but a passphrase
113 // is missing from the cryptographer, or the entry itself wasn't properly 51 // is missing from the cryptographer, or the entry itself wasn't properly
114 // encrypted). 52 // encrypted).
115 // 2. It's type is currently throttled. 53 // 2. It's type is currently throttled.
116 // 3. It's a delete but has not been committed. 54 // 3. It's a delete but has not been committed.
117 bool MayEntryCommit(ModelTypeSet requested_types, 55 bool MayEntryCommit(ModelTypeSet requested_types,
118 ModelTypeSet encrypted_types, 56 ModelTypeSet encrypted_types,
119 bool passphrase_missing, 57 bool passphrase_missing,
120 const syncable::Entry& entry) { 58 const Entry& entry) {
121 DCHECK(entry.GetIsUnsynced()); 59 DCHECK(entry.GetIsUnsynced());
122 60
123 const ModelType type = entry.GetModelType(); 61 const ModelType type = entry.GetModelType();
124 // We special case the nigori node because even though it is considered an 62 // We special case the nigori node because even though it is considered an
125 // "encrypted type", not all nigori node changes require valid encryption 63 // "encrypted type", not all nigori node changes require valid encryption
126 // (ex: sync_tabs). 64 // (ex: sync_tabs).
127 if ((type != NIGORI) && encrypted_types.Has(type) && 65 if ((type != NIGORI) && encrypted_types.Has(type) &&
128 (passphrase_missing || 66 (passphrase_missing ||
129 syncable::EntryNeedsEncryption(encrypted_types, entry))) { 67 syncable::EntryNeedsEncryption(encrypted_types, entry))) {
130 // This entry requires encryption but is not properly encrypted (possibly 68 // This entry requires encryption but is not properly encrypted (possibly
131 // due to the cryptographer not being initialized or the user hasn't 69 // due to the cryptographer not being initialized or the user hasn't
132 // provided the most recent passphrase). 70 // provided the most recent passphrase).
133 DVLOG(1) << "Excluding entry from commit due to lack of encryption " 71 DVLOG(1) << "Excluding entry from commit due to lack of encryption "
134 << entry; 72 << entry;
135 return false; 73 return false;
136 } 74 }
137 75
138 // Ignore it if it's not in our set of requested types. 76 // Ignore it if it's not in our set of requested types.
139 if (!requested_types.Has(type)) 77 if (!requested_types.Has(type))
140 return false; 78 return false;
141 79
142 if (entry.GetIsDel() && !entry.GetId().ServerKnows()) { 80 if (entry.GetIsDel() && !entry.GetId().ServerKnows()) {
143 // New clients (following the resolution of crbug.com/125381) should not 81 // New clients (following the resolution of crbug.com/125381) should not
144 // create such items. Old clients may have left some in the database 82 // create such items. Old clients may have left some in the database
145 // (crbug.com/132905), but we should now be cleaning them on startup. 83 // (crbug.com/132905), but we should now be cleaning them on startup.
146 NOTREACHED() << "Found deleted and unsynced local item: " << entry; 84 NOTREACHED() << "Found deleted and unsynced local item: " << entry;
147 return false; 85 return false;
148 } 86 }
149 87
150 // Extra validity checks. 88 // Extra validity checks.
151 syncable::Id id = entry.GetId(); 89 syncable::Id id = entry.GetId();
152 if (id == entry.GetParentId()) { 90 if (id == entry.GetParentId()) {
153 CHECK(id.IsRoot()) << "Non-root item is self parenting." << entry; 91 CHECK(id.IsRoot()) << "Non-root item is self parenting." << entry;
154 // If the root becomes unsynced it can cause us problems. 92 // If the root becomes unsynced it can cause us problems.
155 NOTREACHED() << "Root item became unsynced " << entry; 93 NOTREACHED() << "Root item became unsynced " << entry;
156 return false; 94 return false;
157 } 95 }
158 96
159 if (entry.IsRoot()) { 97 if (entry.IsRoot()) {
160 NOTREACHED() << "Permanent item became unsynced " << entry; 98 NOTREACHED() << "Permanent item became unsynced " << entry;
161 return false; 99 return false;
162 } 100 }
163 101
164 if (HasAttachmentNotOnServer(entry)) { 102 if (HasAttachmentNotOnServer(entry)) {
165 // This entry is not ready to be sent to the server because it has one or 103 // This entry is not ready to be sent to the server because it has one or
166 // more attachments that have not yet been uploaded to the server. The idea 104 // more attachments that have not yet been uploaded to the server. The idea
167 // here is avoid propagating an entry with dangling attachment references. 105 // here is avoid propagating an entry with dangling attachment references.
168 return false; 106 return false;
169 } 107 }
170 108
171 DVLOG(2) << "Entry is ready for commit: " << entry; 109 DVLOG(2) << "Entry is ready for commit: " << entry;
172 return true; 110 return true;
173 } 111 }
174 112
175 bool SupportsHierarchy(const syncable::Entry& item) { 113 bool SupportsHierarchy(const Entry& item) {
176 // Types with explicit server supported hierarchy only. 114 // Types with explicit server supported hierarchy only.
177 return IsTypeWithServerGeneratedRoot(item.GetModelType()); 115 return IsTypeWithServerGeneratedRoot(item.GetModelType());
178 } 116 }
179 117
180 // Excludes ancestors of deleted conflicted items from
181 // |ready_unsynced_set|.
182 void ExcludeDeletedAncestors(
183 syncable::BaseTransaction* trans,
184 const std::vector<int64_t>& deleted_conflicted_items,
185 std::set<int64_t>* ready_unsynced_set) {
186 for (auto iter = deleted_conflicted_items.begin();
187 iter != deleted_conflicted_items.end(); ++iter) {
188 syncable::Entry item(trans, syncable::GET_BY_HANDLE, *iter);
189 syncable::Id parent_id = item.GetParentId();
190 DCHECK(!parent_id.IsNull());
191
192 while (!parent_id.IsRoot()) {
193 syncable::Entry parent(trans, syncable::GET_BY_ID, parent_id);
194 CHECK(parent.good()) << "Bad user-only parent in item path.";
195 int64_t handle = parent.GetMetahandle();
196
197 if (!parent.GetIsDel())
198 break;
199
200 auto ready_iter = ready_unsynced_set->find(handle);
201 if (ready_iter == ready_unsynced_set->end())
202 break;
203
204 // Remove this entry from |ready_unsynced_set|.
205 ready_unsynced_set->erase(ready_iter);
206 parent_id = parent.GetParentId();
207 }
208 }
209 }
210
211 // Iterates over children of items from |conflicted_items| list that are in 118 // Iterates over children of items from |conflicted_items| list that are in
212 // |ready_unsynced_set|, exludes them from |ready_unsynced_set| and adds them 119 // |ready_unsynced_set|, excludes them from |ready_unsynced_set| and adds them
213 // to |excluded_items| list. 120 // to |excluded_items| list.
214 void ExcludeChildren(syncable::BaseTransaction* trans, 121 void ExcludeChildren(syncable::BaseTransaction* trans,
215 const std::vector<int64_t>& conflicted_items, 122 const std::vector<int64_t>& conflicted_items,
216 std::vector<int64_t>* excluded_items, 123 std::vector<int64_t>* excluded_items,
217 std::set<int64_t>* ready_unsynced_set) { 124 std::set<int64_t>* ready_unsynced_set) {
218 for (auto iter = conflicted_items.begin(); iter != conflicted_items.end(); 125 for (const int64_t& handle : conflicted_items) {
219 ++iter) { 126 Entry entry(trans, syncable::GET_BY_HANDLE, handle);
220 syncable::Entry entry(trans, syncable::GET_BY_HANDLE, *iter);
221 127
222 if (!entry.GetIsDir() || entry.GetIsDel()) 128 if (!entry.GetIsDir() || entry.GetIsDel())
223 continue; 129 continue;
224 130
225 std::vector<int64_t> children; 131 std::vector<int64_t> children;
226 entry.GetChildHandles(&children); 132 entry.GetChildHandles(&children);
227 133
228 for (std::vector<int64_t>::const_iterator child_iter = children.begin(); 134 for (const int64_t& child_handle : children) {
229 child_iter != children.end(); ++child_iter) {
230 // Collect all child handles that are in |ready_unsynced_set|. 135 // Collect all child handles that are in |ready_unsynced_set|.
231 int64_t child_handle = *child_iter;
232 auto ready_iter = ready_unsynced_set->find(child_handle); 136 auto ready_iter = ready_unsynced_set->find(child_handle);
233 if (ready_iter != ready_unsynced_set->end()) { 137 if (ready_iter != ready_unsynced_set->end()) {
234 // Remove this entry from |ready_unsynced_set| and add it 138 // Remove this entry from |ready_unsynced_set| and add it
235 // to |excluded_items|. 139 // to |excluded_items|.
236 ready_unsynced_set->erase(ready_iter); 140 ready_unsynced_set->erase(ready_iter);
237 excluded_items->push_back(child_handle); 141 excluded_items->push_back(child_handle);
238 } 142 }
239 } 143 }
240 } 144 }
241 } 145 }
242 146
243 // Filters |unsynced_handles| to remove all entries that do not belong to the 147 // This class helps to implement OrderCommitIds(). Its members track the
244 // specified |requested_types|, or are not eligible for a commit at this time. 148 // progress of a traversal while its methods extend it. It can return early if
245 void FilterUnreadyEntries(
246 syncable::BaseTransaction* trans,
247 ModelTypeSet requested_types,
248 ModelTypeSet encrypted_types,
249 bool passphrase_missing,
250 const syncable::Directory::Metahandles& unsynced_handles,
251 std::set<int64_t>* ready_unsynced_set) {
252 std::vector<int64_t> deleted_conflicted_items;
253 std::vector<int64_t> conflicted_items;
254
255 // Go over all unsynced handles, filter the ones that might be committed based
256 // on type / encryption, then based on whether they are in conflict add them
257 // to either |ready_unsynced_set| or one of the conflicted lists.
258 for (auto iter = unsynced_handles.begin(); iter != unsynced_handles.end();
259 ++iter) {
260 syncable::Entry entry(trans, syncable::GET_BY_HANDLE, *iter);
261 // TODO(maniscalco): While we check if entry is ready to be committed, we
262 // also need to check that all of its ancestors (parents, transitive) are
263 // ready to be committed. Once attachments can prevent an entry from being
264 // committable, this method must ensure all ancestors are ready for commit
265 // (bug 356273).
266 if (MayEntryCommit(requested_types, encrypted_types, passphrase_missing,
267 entry)) {
268 if (IsEntryInConflict(entry)) {
269 // Conflicting hierarchical entries might prevent their ancestors or
270 // descendants from being committed.
271 if (SupportsHierarchy(entry)) {
272 if (entry.GetIsDel()) {
273 deleted_conflicted_items.push_back(*iter);
274 } else if (entry.GetIsDir()) {
275 // Populate the initial version of |conflicted_items| with folder
276 // items that are in conflict.
277 conflicted_items.push_back(*iter);
278 }
279 }
280 } else {
281 ready_unsynced_set->insert(*iter);
282 }
283 }
284 }
285
286 // If there are any deleted conflicted entries, remove their deleted ancestors
287 // from |ready_unsynced_set| as well.
288 ExcludeDeletedAncestors(trans, deleted_conflicted_items, ready_unsynced_set);
289
290 // Starting with conflicted_items containing conflicted folders go down and
291 // exclude all descendants from |ready_unsynced_set|.
292 while (!conflicted_items.empty()) {
293 std::vector<int64_t> new_list;
294 ExcludeChildren(trans, conflicted_items, &new_list, ready_unsynced_set);
295 conflicted_items.swap(new_list);
296 }
297 }
298
299 // This class helps to implement OrderCommitIds(). Its members track the
300 // progress of a traversal while its methods extend it. It can return early if
301 // the traversal reaches the desired size before the full traversal is complete. 149 // the traversal reaches the desired size before the full traversal is complete.
302 class Traversal { 150 class Traversal {
303 public: 151 public:
304 Traversal(syncable::BaseTransaction* trans, 152 Traversal(syncable::BaseTransaction* trans,
305 int64_t max_entries, 153 int64_t max_entries,
306 syncable::Directory::Metahandles* out); 154 Directory::Metahandles* out);
307 ~Traversal(); 155 ~Traversal();
308 156
309 // First step of traversal building. Adds non-deleted items in order. 157 // First step of traversal building. Adds non-deleted items in order.
310 void AddCreatesAndMoves(const std::set<int64_t>& ready_unsynced_set); 158 void AddCreatesAndMoves(const std::set<int64_t>& ready_unsynced_set);
311 159
312 // Second step of traverals building. Appends deleted items. 160 // Second step of traverals building. Appends deleted items.
313 void AddDeletes(const std::set<int64_t>& ready_unsynced_set); 161 void AddDeletes(const std::set<int64_t>& ready_unsynced_set);
314 162
315 private: 163 private:
316 // The following functions do not modify the traversal directly. They return 164 // The following functions do not modify the traversal directly. They return
317 // their results in the |result| vector instead. 165 // their results in the |result| vector instead.
318 void AddUncommittedParents(const std::set<int64_t>& ready_unsynced_set, 166 void AddUncommittedParents(const std::set<int64_t>& ready_unsynced_set,
319 const syncable::Entry& item, 167 const Entry& item,
320 syncable::Directory::Metahandles* result) const; 168 Directory::Metahandles* result) const;
321 169
322 bool TryAddItem(const std::set<int64_t>& ready_unsynced_set, 170 bool TryAddItem(const std::set<int64_t>& ready_unsynced_set,
323 const syncable::Entry& item, 171 const Entry& item,
324 syncable::Directory::Metahandles* result) const; 172 Directory::Metahandles* result) const;
325 173
326 void AddDeletedParents(const std::set<int64_t>& ready_unsynced_set, 174 void AddDeletedParents(const std::set<int64_t>& ready_unsynced_set,
327 const syncable::Entry& item, 175 const Entry& item,
328 const syncable::Directory::Metahandles& traversed, 176 const Directory::Metahandles& traversed,
329 syncable::Directory::Metahandles* result) const; 177 Directory::Metahandles* result) const;
330 178
331 // Returns true if we've collected enough items. 179 // Returns true if we've collected enough items.
332 bool IsFull() const; 180 bool IsFull() const;
333 181
334 // Returns true if the specified handle is already in the traversal. 182 // Returns true if the specified handle is already in the traversal.
335 bool HaveItem(int64_t handle) const; 183 bool HaveItem(int64_t handle) const;
336 184
337 // Adds the specified handles to the traversal. 185 // Adds the specified handles to the traversal.
338 void AppendManyToTraversal(const syncable::Directory::Metahandles& handles); 186 void AppendManyToTraversal(const Directory::Metahandles& handles);
339 187
340 // Adds the specifed handle to the traversal. 188 // Adds the specified handle to the traversal.
341 void AppendToTraversal(int64_t handle); 189 void AppendToTraversal(int64_t handle);
342 190
343 syncable::Directory::Metahandles* out_; 191 Directory::Metahandles* out_;
344 std::set<int64_t> added_handles_; 192 std::set<int64_t> added_handles_;
345 const size_t max_entries_; 193 const size_t max_entries_;
346 syncable::BaseTransaction* trans_; 194 syncable::BaseTransaction* trans_;
347 195
348 DISALLOW_COPY_AND_ASSIGN(Traversal); 196 DISALLOW_COPY_AND_ASSIGN(Traversal);
349 }; 197 };
350 198
351 Traversal::Traversal(syncable::BaseTransaction* trans, 199 Traversal::Traversal(syncable::BaseTransaction* trans,
352 int64_t max_entries, 200 int64_t max_entries,
353 syncable::Directory::Metahandles* out) 201 Directory::Metahandles* out)
354 : out_(out), max_entries_(max_entries), trans_(trans) {} 202 : out_(out), max_entries_(max_entries), trans_(trans) {}
355 203
356 Traversal::~Traversal() {} 204 Traversal::~Traversal() {}
357 205
358 void Traversal::AddUncommittedParents( 206 void Traversal::AddUncommittedParents(
359 const std::set<int64_t>& ready_unsynced_set, 207 const std::set<int64_t>& ready_unsynced_set,
360 const syncable::Entry& item, 208 const Entry& item,
361 syncable::Directory::Metahandles* result) const { 209 Directory::Metahandles* result) const {
362 DCHECK(SupportsHierarchy(item)); 210 DCHECK(SupportsHierarchy(item));
363 syncable::Directory::Metahandles dependencies; 211 Directory::Metahandles dependencies;
364 syncable::Id parent_id = item.GetParentId(); 212 syncable::Id parent_id = item.GetParentId();
365 213
366 // Climb the tree adding entries leaf -> root. 214 // Climb the tree adding entries leaf -> root.
367 while (!parent_id.ServerKnows()) { 215 while (!parent_id.ServerKnows()) {
368 syncable::Entry parent(trans_, syncable::GET_BY_ID, parent_id); 216 Entry parent(trans_, syncable::GET_BY_ID, parent_id);
369 CHECK(parent.good()) << "Bad user-only parent in item path."; 217 CHECK(parent.good()) << "Bad user-only parent in item path.";
370 int64_t handle = parent.GetMetahandle(); 218 int64_t handle = parent.GetMetahandle();
371 if (HaveItem(handle)) { 219 if (HaveItem(handle)) {
372 // We've already added this parent (and therefore all of its parents). 220 // We've already added this parent (and therefore all of its parents).
373 // We can return early. 221 // We can return early.
374 break; 222 break;
375 } 223 }
376 224
377 if (!TryAddItem(ready_unsynced_set, parent, &dependencies)) { 225 if (!TryAddItem(ready_unsynced_set, parent, &dependencies)) {
378 // The parent isn't in |ready_unsynced_set|. 226 // The parent isn't in |ready_unsynced_set|.
379 break; 227 break;
380 } 228 }
381 229
382 parent_id = parent.GetParentId(); 230 parent_id = parent.GetParentId();
383 } 231 }
384 232
385 // Reverse what we added to get the correct order. 233 // Reverse what we added to get the correct order.
386 result->insert(result->end(), dependencies.rbegin(), dependencies.rend()); 234 result->insert(result->end(), dependencies.rbegin(), dependencies.rend());
387 } 235 }
388 236
389 // Adds the given item to the list if it is unsynced and ready for commit. 237 // Adds the given item to the list if it is unsynced and ready for commit.
390 bool Traversal::TryAddItem(const std::set<int64_t>& ready_unsynced_set, 238 bool Traversal::TryAddItem(const std::set<int64_t>& ready_unsynced_set,
391 const syncable::Entry& item, 239 const Entry& item,
392 syncable::Directory::Metahandles* result) const { 240 Directory::Metahandles* result) const {
393 DCHECK(item.GetIsUnsynced()); 241 DCHECK(item.GetIsUnsynced());
394 int64_t item_handle = item.GetMetahandle(); 242 int64_t item_handle = item.GetMetahandle();
395 if (ready_unsynced_set.count(item_handle) != 0) { 243 if (ready_unsynced_set.count(item_handle) != 0) {
396 result->push_back(item_handle); 244 result->push_back(item_handle);
397 return true; 245 return true;
398 } 246 }
399 return false; 247 return false;
400 } 248 }
401 249
402 // Traverses the tree from bottom to top, adding the deleted parents of the 250 // Traverses the tree from bottom to top, adding the deleted parents of the
403 // given |item|. Stops traversing if it encounters a non-deleted node, or 251 // given |item|. Stops traversing if it encounters a non-deleted node, or
404 // a node that was already listed in the |traversed| list. 252 // a node that was already listed in the |traversed| list.
405 // 253 //
406 // The result list is reversed before it is returned, so the resulting 254 // The result list is reversed before it is returned, so the resulting
407 // traversal is in top to bottom order. Also note that this function appends 255 // traversal is in top to bottom order. Also note that this function appends
408 // to the result list without clearing it. 256 // to the result list without clearing it.
409 void Traversal::AddDeletedParents( 257 void Traversal::AddDeletedParents(const std::set<int64_t>& ready_unsynced_set,
410 const std::set<int64_t>& ready_unsynced_set, 258 const Entry& item,
411 const syncable::Entry& item, 259 const Directory::Metahandles& traversed,
412 const syncable::Directory::Metahandles& traversed, 260 Directory::Metahandles* result) const {
413 syncable::Directory::Metahandles* result) const {
414 DCHECK(SupportsHierarchy(item)); 261 DCHECK(SupportsHierarchy(item));
415 syncable::Directory::Metahandles dependencies; 262 Directory::Metahandles dependencies;
416 syncable::Id parent_id = item.GetParentId(); 263 syncable::Id parent_id = item.GetParentId();
417 264
418 // Climb the tree adding entries leaf -> root. 265 // Climb the tree adding entries leaf -> root.
419 while (!parent_id.IsRoot()) { 266 while (!parent_id.IsRoot()) {
420 syncable::Entry parent(trans_, syncable::GET_BY_ID, parent_id); 267 Entry parent(trans_, syncable::GET_BY_ID, parent_id);
421 268
422 if (!parent.good()) { 269 if (!parent.good()) {
423 // This is valid because the parent could have gone away a long time ago 270 // This is valid because the parent could have gone away a long time ago.
424 // 271 //
425 // Consider the case where a folder is server-unknown and locally 272 // Consider the case where a folder is server-unknown and locally
426 // deleted, and has a child that is server-known, deleted, and unsynced. 273 // deleted, and has a child that is server-known, deleted, and unsynced.
427 // The parent could be dropped from memory at any time, but its child 274 // The parent could be dropped from memory at any time, but its child
428 // needs to be committed first. 275 // needs to be committed first.
429 break; 276 break;
430 } 277 }
431 int64_t handle = parent.GetMetahandle(); 278 int64_t handle = parent.GetMetahandle();
432 if (!parent.GetIsUnsynced()) { 279 if (!parent.GetIsUnsynced()) {
433 // In some rare cases, our parent can be both deleted and unsynced. 280 // In some rare cases, our parent can be both deleted and unsynced.
434 // (ie. the server-unknown parent case). 281 // (ie. the server-unknown parent case).
435 break; 282 break;
436 } 283 }
437 if (!parent.GetIsDel()) { 284 if (!parent.GetIsDel()) {
438 // We're not intersted in non-deleted parents. 285 // We're not interested in non-deleted parents.
439 break; 286 break;
440 } 287 }
441 if (std::find(traversed.begin(), traversed.end(), handle) != 288 if (std::find(traversed.begin(), traversed.end(), handle) !=
442 traversed.end()) { 289 traversed.end()) {
443 // We've already added this parent (and therefore all of its parents). 290 // We've already added this parent (and therefore all of its parents).
444 // We can return early. 291 // We can return early.
445 break; 292 break;
446 } 293 }
447 294
448 if (!TryAddItem(ready_unsynced_set, parent, &dependencies)) { 295 if (!TryAddItem(ready_unsynced_set, parent, &dependencies)) {
449 // The parent isn't in ready_unsynced_set. 296 // The parent isn't in ready_unsynced_set.
450 break; 297 break;
451 } 298 }
452 299
453 parent_id = parent.GetParentId(); 300 parent_id = parent.GetParentId();
454 } 301 }
455 302
456 // Reverse what we added to get the correct order. 303 // Reverse what we added to get the correct order.
457 result->insert(result->end(), dependencies.rbegin(), dependencies.rend()); 304 result->insert(result->end(), dependencies.rbegin(), dependencies.rend());
458 } 305 }
459 306
460 bool Traversal::IsFull() const { 307 bool Traversal::IsFull() const {
461 return out_->size() >= max_entries_; 308 return out_->size() >= max_entries_;
462 } 309 }
463 310
464 bool Traversal::HaveItem(int64_t handle) const { 311 bool Traversal::HaveItem(int64_t handle) const {
465 return added_handles_.find(handle) != added_handles_.end(); 312 return added_handles_.find(handle) != added_handles_.end();
466 } 313 }
467 314
468 void Traversal::AppendManyToTraversal( 315 void Traversal::AppendManyToTraversal(const Directory::Metahandles& handles) {
469 const syncable::Directory::Metahandles& handles) {
470 out_->insert(out_->end(), handles.begin(), handles.end()); 316 out_->insert(out_->end(), handles.begin(), handles.end());
471 added_handles_.insert(handles.begin(), handles.end()); 317 added_handles_.insert(handles.begin(), handles.end());
472 } 318 }
473 319
474 void Traversal::AppendToTraversal(int64_t metahandle) { 320 void Traversal::AppendToTraversal(int64_t handle) {
475 out_->push_back(metahandle); 321 out_->push_back(handle);
476 added_handles_.insert(metahandle); 322 added_handles_.insert(handle);
477 } 323 }
478 324
479 void Traversal::AddCreatesAndMoves( 325 void Traversal::AddCreatesAndMoves(
480 const std::set<int64_t>& ready_unsynced_set) { 326 const std::set<int64_t>& ready_unsynced_set) {
481 // Add moves and creates, and prepend their uncommitted parents. 327 // Add moves and creates, and prepend their uncommitted parents.
482 for (std::set<int64_t>::const_iterator iter = ready_unsynced_set.begin(); 328 for (const int64_t& handle : ready_unsynced_set) {
483 !IsFull() && iter != ready_unsynced_set.end(); ++iter) { 329 if (IsFull()) {
484 int64_t metahandle = *iter; 330 break;
485 if (HaveItem(metahandle)) 331 }
332 if (HaveItem(handle))
486 continue; 333 continue;
487 334
488 syncable::Entry entry(trans_, syncable::GET_BY_HANDLE, metahandle); 335 Entry entry(trans_, syncable::GET_BY_HANDLE, handle);
489 if (!entry.GetIsDel()) { 336 if (!entry.GetIsDel()) {
490 if (SupportsHierarchy(entry)) { 337 if (SupportsHierarchy(entry)) {
491 // We only commit an item + its dependencies if it and all its 338 // We only commit an item + its dependencies if it and all its
492 // dependencies are not in conflict. 339 // dependencies are not in conflict.
493 syncable::Directory::Metahandles item_dependencies; 340 Directory::Metahandles item_dependencies;
494 AddUncommittedParents(ready_unsynced_set, entry, &item_dependencies); 341 AddUncommittedParents(ready_unsynced_set, entry, &item_dependencies);
495 TryAddItem(ready_unsynced_set, entry, &item_dependencies); 342 TryAddItem(ready_unsynced_set, entry, &item_dependencies);
496 AppendManyToTraversal(item_dependencies); 343 AppendManyToTraversal(item_dependencies);
497 } else { 344 } else {
498 // No hierarchy dependencies, just commit the item itself. 345 // No hierarchy dependencies, just commit the item itself.
499 AppendToTraversal(metahandle); 346 AppendToTraversal(handle);
500 } 347 }
501 } 348 }
502 } 349 }
503 350
504 // It's possible that we overcommitted while trying to expand dependent 351 // It's possible that we over committed while trying to expand dependent
505 // items. If so, truncate the set down to the allowed size. 352 // items. If so, truncate the set down to the allowed size. This is safe
353 // because we've ordered such that ancestors come before children.
506 if (out_->size() > max_entries_) 354 if (out_->size() > max_entries_)
507 out_->resize(max_entries_); 355 out_->resize(max_entries_);
508 } 356 }
509 357
510 void Traversal::AddDeletes(const std::set<int64_t>& ready_unsynced_set) { 358 void Traversal::AddDeletes(const std::set<int64_t>& ready_unsynced_set) {
511 syncable::Directory::Metahandles deletion_list; 359 Directory::Metahandles deletion_list;
512 360
513 // Note: we iterate over all the unsynced set, regardless of the max size. 361 // Note: we iterate over all the unsynced set, regardless of the max size.
514 // The max size is only enforced after the top-to-bottom order has been 362 // The max size is only enforced after the top-to-bottom order has been
515 // reversed, in order to ensure children are always deleted before parents. 363 // reversed, in order to ensure children are always deleted before parents.
516 for (std::set<int64_t>::const_iterator iter = ready_unsynced_set.begin(); 364 // We cannot bail early when full because we need to guarantee that children
517 iter != ready_unsynced_set.end(); ++iter) { 365 // are always deleted before parents/ancestors.
518 int64_t metahandle = *iter; 366 for (const int64_t& handle : ready_unsynced_set) {
519 367 if (HaveItem(handle))
520 if (HaveItem(metahandle))
521 continue; 368 continue;
522 369
523 if (std::find(deletion_list.begin(), deletion_list.end(), metahandle) != 370 if (std::find(deletion_list.begin(), deletion_list.end(), handle) !=
524 deletion_list.end()) { 371 deletion_list.end()) {
525 continue; 372 continue;
526 } 373 }
527 374
528 syncable::Entry entry(trans_, syncable::GET_BY_HANDLE, metahandle); 375 Entry entry(trans_, syncable::GET_BY_HANDLE, handle);
529 376
530 if (entry.GetIsDel()) { 377 if (entry.GetIsDel()) {
531 if (SupportsHierarchy(entry)) { 378 if (SupportsHierarchy(entry)) {
532 syncable::Directory::Metahandles parents; 379 Directory::Metahandles parents;
533 AddDeletedParents(ready_unsynced_set, entry, deletion_list, &parents); 380 AddDeletedParents(ready_unsynced_set, entry, deletion_list, &parents);
534 // Append parents and chilren in top to bottom order. 381 // Append parents and children in top to bottom order.
535 deletion_list.insert(deletion_list.end(), parents.begin(), 382 deletion_list.insert(deletion_list.end(), parents.begin(),
536 parents.end()); 383 parents.end());
537 } 384 }
538 deletion_list.push_back(metahandle); 385 deletion_list.push_back(handle);
539 } 386 }
540 } 387 }
541 388
542 // We've been gathering deletions in top to bottom order. Now we reverse the 389 // We've been gathering deletions in top to bottom order. Now we reverse the
543 // order as we prepare the list. 390 // order as we prepare the list.
544 std::reverse(deletion_list.begin(), deletion_list.end()); 391 std::reverse(deletion_list.begin(), deletion_list.end());
545 AppendManyToTraversal(deletion_list); 392 AppendManyToTraversal(deletion_list);
546 393
547 // It's possible that we overcommitted while trying to expand dependent 394 // It's possible that we over committed while trying to expand dependent
548 // items. If so, truncate the set down to the allowed size. 395 // items. If so, truncate the set down to the allowed size. This is safe
396 // because of the reverse above, which should guarantee the leafy nodes are
397 // in the front of the ancestors nodes.
549 if (out_->size() > max_entries_) 398 if (out_->size() > max_entries_)
550 out_->resize(max_entries_); 399 out_->resize(max_entries_);
551 } 400 }
552 401
402 // Excludes ancestors of deleted conflicted items from
403 // |ready_unsynced_set|.
404 void ExcludeDeletedAncestors(
405 syncable::BaseTransaction* trans,
406 const std::vector<int64_t>& deleted_conflicted_items,
407 std::set<int64_t>* ready_unsynced_set) {
408 for (const int64_t& deleted_conflicted_handle : deleted_conflicted_items) {
409 Entry item(trans, syncable::GET_BY_HANDLE, deleted_conflicted_handle);
410 syncable::Id parent_id = item.GetParentId();
411 DCHECK(!parent_id.IsNull());
412
413 while (!parent_id.IsRoot()) {
414 Entry parent(trans, syncable::GET_BY_ID, parent_id);
415 CHECK(parent.good()) << "Bad user-only parent in item path.";
416 int64_t handle = parent.GetMetahandle();
417
418 if (!parent.GetIsDel())
419 break;
420
421 auto ready_iter = ready_unsynced_set->find(handle);
422 if (ready_iter == ready_unsynced_set->end())
423 break;
424
425 // Remove this entry from |ready_unsynced_set|.
426 ready_unsynced_set->erase(ready_iter);
427 parent_id = parent.GetParentId();
428 }
429 }
430 }
431
432 // Filters |unsynced_handles| to remove all entries that do not belong to the
433 // specified |requested_types|, or are not eligible for a commit at this time.
434 void FilterUnreadyEntries(syncable::BaseTransaction* trans,
435 ModelTypeSet requested_types,
436 ModelTypeSet encrypted_types,
437 bool passphrase_missing,
438 const Directory::Metahandles& unsynced_handles,
439 std::set<int64_t>* ready_unsynced_set) {
440 std::vector<int64_t> deleted_conflicted_items;
441 std::vector<int64_t> conflicted_items;
442
443 // Go over all unsynced handles, filter the ones that might be committed based
444 // on type / encryption, then based on whether they are in conflict add them
445 // to either |ready_unsynced_set| or one of the conflicted lists.
446 for (const int64_t& handle : unsynced_handles) {
447 Entry entry(trans, syncable::GET_BY_HANDLE, handle);
448 // TODO(maniscalco): While we check if entry is ready to be committed, we
449 // also need to check that all of its ancestors (parents, transitive) are
450 // ready to be committed. Once attachments can prevent an entry from being
451 // committable, this method must ensure all ancestors are ready for commit
452 // (crbug.com/356273).
453 if (MayEntryCommit(requested_types, encrypted_types, passphrase_missing,
454 entry)) {
455 if (IsEntryInConflict(entry)) {
456 // Conflicting hierarchical entries might prevent their ancestors or
457 // descendants from being committed.
458 if (SupportsHierarchy(entry)) {
459 if (entry.GetIsDel()) {
460 deleted_conflicted_items.push_back(handle);
461 } else if (entry.GetIsDir()) {
462 // Populate the initial version of |conflicted_items| with folder
463 // items that are in conflict.
464 conflicted_items.push_back(handle);
465 }
466 }
467 } else {
468 ready_unsynced_set->insert(handle);
469 }
470 }
471 }
472
473 // If there are any deleted conflicted entries, remove their deleted ancestors
474 // from |ready_unsynced_set| as well.
475 ExcludeDeletedAncestors(trans, deleted_conflicted_items, ready_unsynced_set);
476
477 // Starting with conflicted_items containing conflicted folders go down and
478 // exclude all descendants from |ready_unsynced_set|.
479 while (!conflicted_items.empty()) {
480 std::vector<int64_t> new_list;
481 ExcludeChildren(trans, conflicted_items, &new_list, ready_unsynced_set);
482 conflicted_items.swap(new_list);
483 }
484 }
485
486 // Given a set of commit metahandles that are ready for commit
487 // (|ready_unsynced_set|), sorts these into commit order and places up to
488 // |max_entries| of them in the output parameter |out|.
489 //
490 // See the header file for an explanation of commit ordering.
553 void OrderCommitIds(syncable::BaseTransaction* trans, 491 void OrderCommitIds(syncable::BaseTransaction* trans,
554 size_t max_entries, 492 size_t max_entries,
555 const std::set<int64_t>& ready_unsynced_set, 493 const std::set<int64_t>& ready_unsynced_set,
556 syncable::Directory::Metahandles* out) { 494 Directory::Metahandles* out) {
557 // Commits follow these rules: 495 // Commits follow these rules:
558 // 1. Moves or creates are preceded by needed folder creates, from 496 // 1. Moves or creates are preceded by needed folder creates, from
559 // root to leaf. 497 // root to leaf.
560 // 2. Moves/Creates before deletes. 498 // 2. Moves/Creates before deletes.
561 // 3. Deletes, collapsed. 499 // 3. Deletes, collapsed.
562 // We commit deleted moves under deleted items as moves when collapsing 500 // We commit deleted moves under deleted items as moves when collapsing
563 // delete trees. 501 // delete trees.
564 502
565 Traversal traversal(trans, max_entries, out); 503 Traversal traversal(trans, max_entries, out);
566 504
567 // Add moves and creates, and prepend their uncommitted parents. 505 // Add moves and creates, and prepend their uncommitted parents.
568 traversal.AddCreatesAndMoves(ready_unsynced_set); 506 traversal.AddCreatesAndMoves(ready_unsynced_set);
569 507
570 // Add all deletes. 508 // Add all deletes.
571 traversal.AddDeletes(ready_unsynced_set); 509 traversal.AddDeletes(ready_unsynced_set);
572 } 510 }
573 511
574 } // namespace 512 } // namespace
575 513
514 void GetCommitIdsForType(syncable::BaseTransaction* trans,
515 ModelType type,
516 size_t max_entries,
517 Directory::Metahandles* out) {
518 Directory* dir = trans->directory();
519
520 // Gather the full set of unsynced items and store it in the cycle. They
521 // are not in the correct order for commit.
522 std::set<int64_t> ready_unsynced_set;
523 Directory::Metahandles all_unsynced_handles;
524 GetUnsyncedEntries(trans, &all_unsynced_handles);
525
526 ModelTypeSet encrypted_types;
527 bool passphrase_missing = false;
528 Cryptographer* cryptographer = dir->GetCryptographer(trans);
529 if (cryptographer) {
530 encrypted_types = dir->GetNigoriHandler()->GetEncryptedTypes(trans);
531 passphrase_missing = cryptographer->has_pending_keys();
532 }
533
534 // We filter out all unready entries from the set of unsynced handles. This
535 // new set of ready and unsynced items is then what we use to determine what
536 // is a candidate for commit. The caller is responsible for ensuring that no
537 // throttled types are included among the requested_types.
538 FilterUnreadyEntries(trans, ModelTypeSet(type), encrypted_types,
539 passphrase_missing, all_unsynced_handles,
540 &ready_unsynced_set);
541
542 OrderCommitIds(trans, max_entries, ready_unsynced_set, out);
543
544 for (const int64_t& handle : *out) {
545 DVLOG(1) << "Debug commit batch result:" << handle;
546 }
547 }
548
576 } // namespace syncer 549 } // namespace syncer
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698