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

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