| OLD | NEW |
| 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 |
| OLD | NEW |