Index: sync/engine/get_commit_ids.cc |
diff --git a/sync/engine/get_commit_ids.cc b/sync/engine/get_commit_ids.cc |
deleted file mode 100644 |
index a4c7f6b7f7944f97b1c2b44a37af5115608e7aec..0000000000000000000000000000000000000000 |
--- a/sync/engine/get_commit_ids.cc |
+++ /dev/null |
@@ -1,590 +0,0 @@ |
-// Copyright 2013 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "sync/engine/get_commit_ids.h" |
- |
-#include <stddef.h> |
-#include <stdint.h> |
- |
-#include <set> |
-#include <vector> |
- |
-#include "base/macros.h" |
-#include "sync/engine/syncer_util.h" |
-#include "sync/syncable/directory.h" |
-#include "sync/syncable/entry.h" |
-#include "sync/syncable/nigori_handler.h" |
-#include "sync/syncable/nigori_util.h" |
-#include "sync/syncable/syncable_base_transaction.h" |
-#include "sync/syncable/syncable_util.h" |
-#include "sync/util/cryptographer.h" |
- |
-using std::set; |
-using std::vector; |
- |
-namespace syncer { |
- |
-namespace { |
- |
-// Forward-declare some helper functions. This gives us more options for |
-// ordering the function defintions within this file. |
- |
-// Filters |unsynced_handles| to remove all entries that do not belong to the |
-// specified |requested_types|, or are not eligible for a commit at this time. |
-void FilterUnreadyEntries( |
- syncable::BaseTransaction* trans, |
- ModelTypeSet requested_types, |
- ModelTypeSet encrypted_types, |
- bool passphrase_missing, |
- const syncable::Directory::Metahandles& unsynced_handles, |
- std::set<int64_t>* ready_unsynced_set); |
- |
-// Given a set of commit metahandles that are ready for commit |
-// (|ready_unsynced_set|), sorts these into commit order and places up to |
-// |max_entries| of them in the output parameter |out|. |
-// |
-// See the header file for an explanation of commit ordering. |
-void OrderCommitIds(syncable::BaseTransaction* trans, |
- size_t max_entries, |
- const std::set<int64_t>& ready_unsynced_set, |
- std::vector<int64_t>* out); |
- |
-} // namespace |
- |
-void GetCommitIdsForType( |
- syncable::BaseTransaction* trans, |
- ModelType type, |
- size_t max_entries, |
- syncable::Directory::Metahandles* out) { |
- syncable::Directory* dir = trans->directory(); |
- |
- // Gather the full set of unsynced items and store it in the session. They |
- // are not in the correct order for commit. |
- std::set<int64_t> ready_unsynced_set; |
- syncable::Directory::Metahandles all_unsynced_handles; |
- GetUnsyncedEntries(trans, &all_unsynced_handles); |
- |
- ModelTypeSet encrypted_types; |
- bool passphrase_missing = false; |
- Cryptographer* cryptographer = dir->GetCryptographer(trans); |
- if (cryptographer) { |
- encrypted_types = dir->GetNigoriHandler()->GetEncryptedTypes(trans); |
- passphrase_missing = cryptographer->has_pending_keys(); |
- } |
- |
- // We filter out all unready entries from the set of unsynced handles. This |
- // new set of ready and unsynced items is then what we use to determine what |
- // is a candidate for commit. The caller is responsible for ensuring that no |
- // throttled types are included among the requested_types. |
- FilterUnreadyEntries(trans, |
- ModelTypeSet(type), |
- encrypted_types, |
- passphrase_missing, |
- all_unsynced_handles, |
- &ready_unsynced_set); |
- |
- OrderCommitIds(trans, max_entries, ready_unsynced_set, out); |
- |
- for (size_t i = 0; i < out->size(); i++) { |
- DVLOG(1) << "Debug commit batch result:" << (*out)[i]; |
- } |
-} |
- |
-namespace { |
- |
-bool IsEntryInConflict(const syncable::Entry& entry) { |
- if (entry.GetIsUnsynced() && |
- entry.GetServerVersion() > 0 && |
- (entry.GetServerVersion() > entry.GetBaseVersion())) { |
- // The local and server versions don't match. The item must be in |
- // conflict, so there's no point in attempting to commit. |
- DCHECK(entry.GetIsUnappliedUpdate()); |
- DVLOG(1) << "Excluding entry from commit due to version mismatch " |
- << entry; |
- return true; |
- } |
- return false; |
-} |
- |
-// Return true if this entry has any attachments that haven't yet been uploaded |
-// to the server. |
-bool HasAttachmentNotOnServer(const syncable::Entry& entry) { |
- const sync_pb::AttachmentMetadata& metadata = entry.GetAttachmentMetadata(); |
- for (int i = 0; i < metadata.record_size(); ++i) { |
- if (!metadata.record(i).is_on_server()) { |
- return true; |
- } |
- } |
- return false; |
-} |
- |
-// An entry may not commit if any are true: |
-// 1. It requires encryption (either the type is encrypted but a passphrase |
-// is missing from the cryptographer, or the entry itself wasn't properly |
-// encrypted). |
-// 2. It's type is currently throttled. |
-// 3. It's a delete but has not been committed. |
-bool MayEntryCommit(ModelTypeSet requested_types, |
- ModelTypeSet encrypted_types, |
- bool passphrase_missing, |
- const syncable::Entry& entry) { |
- DCHECK(entry.GetIsUnsynced()); |
- |
- const ModelType type = entry.GetModelType(); |
- // We special case the nigori node because even though it is considered an |
- // "encrypted type", not all nigori node changes require valid encryption |
- // (ex: sync_tabs). |
- if ((type != NIGORI) && encrypted_types.Has(type) && |
- (passphrase_missing || |
- syncable::EntryNeedsEncryption(encrypted_types, entry))) { |
- // This entry requires encryption but is not properly encrypted (possibly |
- // due to the cryptographer not being initialized or the user hasn't |
- // provided the most recent passphrase). |
- DVLOG(1) << "Excluding entry from commit due to lack of encryption " |
- << entry; |
- return false; |
- } |
- |
- // Ignore it if it's not in our set of requested types. |
- if (!requested_types.Has(type)) |
- return false; |
- |
- if (entry.GetIsDel() && !entry.GetId().ServerKnows()) { |
- // New clients (following the resolution of crbug.com/125381) should not |
- // create such items. Old clients may have left some in the database |
- // (crbug.com/132905), but we should now be cleaning them on startup. |
- NOTREACHED() << "Found deleted and unsynced local item: " << entry; |
- return false; |
- } |
- |
- // Extra validity checks. |
- syncable::Id id = entry.GetId(); |
- if (id == entry.GetParentId()) { |
- CHECK(id.IsRoot()) << "Non-root item is self parenting." << entry; |
- // If the root becomes unsynced it can cause us problems. |
- NOTREACHED() << "Root item became unsynced " << entry; |
- return false; |
- } |
- |
- if (entry.IsRoot()) { |
- NOTREACHED() << "Permanent item became unsynced " << entry; |
- return false; |
- } |
- |
- if (HasAttachmentNotOnServer(entry)) { |
- // This entry is not ready to be sent to the server because it has one or |
- // more attachments that have not yet been uploaded to the server. The idea |
- // here is avoid propagating an entry with dangling attachment references. |
- return false; |
- } |
- |
- DVLOG(2) << "Entry is ready for commit: " << entry; |
- return true; |
-} |
- |
-bool SupportsHierarchy(const syncable::Entry& item) { |
- // Types with explicit server supported hierarchy only. |
- return IsTypeWithServerGeneratedRoot(item.GetModelType()); |
-} |
- |
-// Excludes ancestors of deleted conflicted items from |
-// |ready_unsynced_set|. |
-void ExcludeDeletedAncestors( |
- syncable::BaseTransaction* trans, |
- const std::vector<int64_t>& deleted_conflicted_items, |
- std::set<int64_t>* ready_unsynced_set) { |
- for (auto iter = deleted_conflicted_items.begin(); |
- iter != deleted_conflicted_items.end(); ++iter) { |
- syncable::Entry item(trans, syncable::GET_BY_HANDLE, *iter); |
- syncable::Id parent_id = item.GetParentId(); |
- DCHECK(!parent_id.IsNull()); |
- |
- while (!parent_id.IsRoot()) { |
- syncable::Entry parent(trans, syncable::GET_BY_ID, parent_id); |
- CHECK(parent.good()) << "Bad user-only parent in item path."; |
- int64_t handle = parent.GetMetahandle(); |
- |
- if (!parent.GetIsDel()) |
- break; |
- |
- auto ready_iter = ready_unsynced_set->find(handle); |
- if (ready_iter == ready_unsynced_set->end()) |
- break; |
- |
- // Remove this entry from |ready_unsynced_set|. |
- ready_unsynced_set->erase(ready_iter); |
- parent_id = parent.GetParentId(); |
- } |
- } |
-} |
- |
-// Iterates over children of items from |conflicted_items| list that are in |
-// |ready_unsynced_set|, exludes them from |ready_unsynced_set| and adds them |
-// to |excluded_items| list. |
-void ExcludeChildren(syncable::BaseTransaction* trans, |
- const std::vector<int64_t>& conflicted_items, |
- std::vector<int64_t>* excluded_items, |
- std::set<int64_t>* ready_unsynced_set) { |
- for (auto iter = conflicted_items.begin(); iter != conflicted_items.end(); |
- ++iter) { |
- syncable::Entry entry(trans, syncable::GET_BY_HANDLE, *iter); |
- |
- if (!entry.GetIsDir() || entry.GetIsDel()) |
- continue; |
- |
- std::vector<int64_t> children; |
- entry.GetChildHandles(&children); |
- |
- for (std::vector<int64_t>::const_iterator child_iter = children.begin(); |
- child_iter != children.end(); ++child_iter) { |
- // Collect all child handles that are in |ready_unsynced_set|. |
- int64_t child_handle = *child_iter; |
- auto ready_iter = ready_unsynced_set->find(child_handle); |
- if (ready_iter != ready_unsynced_set->end()) { |
- // Remove this entry from |ready_unsynced_set| and add it |
- // to |excluded_items|. |
- ready_unsynced_set->erase(ready_iter); |
- excluded_items->push_back(child_handle); |
- } |
- } |
- } |
-} |
- |
-// Filters |unsynced_handles| to remove all entries that do not belong to the |
-// specified |requested_types|, or are not eligible for a commit at this time. |
-void FilterUnreadyEntries( |
- syncable::BaseTransaction* trans, |
- ModelTypeSet requested_types, |
- ModelTypeSet encrypted_types, |
- bool passphrase_missing, |
- const syncable::Directory::Metahandles& unsynced_handles, |
- std::set<int64_t>* ready_unsynced_set) { |
- std::vector<int64_t> deleted_conflicted_items; |
- std::vector<int64_t> conflicted_items; |
- |
- // Go over all unsynced handles, filter the ones that might be committed based |
- // on type / encryption, then based on whether they are in conflict add them |
- // to either |ready_unsynced_set| or one of the conflicted lists. |
- for (auto iter = unsynced_handles.begin(); iter != unsynced_handles.end(); |
- ++iter) { |
- syncable::Entry entry(trans, syncable::GET_BY_HANDLE, *iter); |
- // TODO(maniscalco): While we check if entry is ready to be committed, we |
- // also need to check that all of its ancestors (parents, transitive) are |
- // ready to be committed. Once attachments can prevent an entry from being |
- // committable, this method must ensure all ancestors are ready for commit |
- // (bug 356273). |
- if (MayEntryCommit(requested_types, encrypted_types, passphrase_missing, |
- entry)) { |
- if (IsEntryInConflict(entry)) { |
- // Conflicting hierarchical entries might prevent their ancestors or |
- // descendants from being committed. |
- if (SupportsHierarchy(entry)) { |
- if (entry.GetIsDel()) { |
- deleted_conflicted_items.push_back(*iter); |
- } else if (entry.GetIsDir()) { |
- // Populate the initial version of |conflicted_items| with folder |
- // items that are in conflict. |
- conflicted_items.push_back(*iter); |
- } |
- } |
- } else { |
- ready_unsynced_set->insert(*iter); |
- } |
- } |
- } |
- |
- // If there are any deleted conflicted entries, remove their deleted ancestors |
- // from |ready_unsynced_set| as well. |
- ExcludeDeletedAncestors(trans, deleted_conflicted_items, ready_unsynced_set); |
- |
- // Starting with conflicted_items containing conflicted folders go down and |
- // exclude all descendants from |ready_unsynced_set|. |
- while (!conflicted_items.empty()) { |
- std::vector<int64_t> new_list; |
- ExcludeChildren(trans, conflicted_items, &new_list, ready_unsynced_set); |
- conflicted_items.swap(new_list); |
- } |
-} |
- |
-// This class helps to implement OrderCommitIds(). Its members track the |
-// progress of a traversal while its methods extend it. It can return early if |
-// the traversal reaches the desired size before the full traversal is complete. |
-class Traversal { |
- public: |
- Traversal(syncable::BaseTransaction* trans, |
- int64_t max_entries, |
- syncable::Directory::Metahandles* out); |
- ~Traversal(); |
- |
- // First step of traversal building. Adds non-deleted items in order. |
- void AddCreatesAndMoves(const std::set<int64_t>& ready_unsynced_set); |
- |
- // Second step of traverals building. Appends deleted items. |
- void AddDeletes(const std::set<int64_t>& ready_unsynced_set); |
- |
- private: |
- // The following functions do not modify the traversal directly. They return |
- // their results in the |result| vector instead. |
- void AddUncommittedParents(const std::set<int64_t>& ready_unsynced_set, |
- const syncable::Entry& item, |
- syncable::Directory::Metahandles* result) const; |
- |
- bool TryAddItem(const std::set<int64_t>& ready_unsynced_set, |
- const syncable::Entry& item, |
- syncable::Directory::Metahandles* result) const; |
- |
- void AddDeletedParents(const std::set<int64_t>& ready_unsynced_set, |
- const syncable::Entry& item, |
- const syncable::Directory::Metahandles& traversed, |
- syncable::Directory::Metahandles* result) const; |
- |
- // Returns true if we've collected enough items. |
- bool IsFull() const; |
- |
- // Returns true if the specified handle is already in the traversal. |
- bool HaveItem(int64_t handle) const; |
- |
- // Adds the specified handles to the traversal. |
- void AppendManyToTraversal(const syncable::Directory::Metahandles& handles); |
- |
- // Adds the specifed handle to the traversal. |
- void AppendToTraversal(int64_t handle); |
- |
- syncable::Directory::Metahandles* out_; |
- std::set<int64_t> added_handles_; |
- const size_t max_entries_; |
- syncable::BaseTransaction* trans_; |
- |
- DISALLOW_COPY_AND_ASSIGN(Traversal); |
-}; |
- |
-Traversal::Traversal(syncable::BaseTransaction* trans, |
- int64_t max_entries, |
- syncable::Directory::Metahandles* out) |
- : out_(out), max_entries_(max_entries), trans_(trans) {} |
- |
-Traversal::~Traversal() {} |
- |
-void Traversal::AddUncommittedParents( |
- const std::set<int64_t>& ready_unsynced_set, |
- const syncable::Entry& item, |
- syncable::Directory::Metahandles* result) const { |
- DCHECK(SupportsHierarchy(item)); |
- syncable::Directory::Metahandles dependencies; |
- syncable::Id parent_id = item.GetParentId(); |
- |
- // Climb the tree adding entries leaf -> root. |
- while (!parent_id.ServerKnows()) { |
- syncable::Entry parent(trans_, syncable::GET_BY_ID, parent_id); |
- CHECK(parent.good()) << "Bad user-only parent in item path."; |
- int64_t handle = parent.GetMetahandle(); |
- if (HaveItem(handle)) { |
- // We've already added this parent (and therefore all of its parents). |
- // We can return early. |
- break; |
- } |
- |
- if (!TryAddItem(ready_unsynced_set, parent, &dependencies)) { |
- // The parent isn't in |ready_unsynced_set|. |
- break; |
- } |
- |
- parent_id = parent.GetParentId(); |
- } |
- |
- // Reverse what we added to get the correct order. |
- result->insert(result->end(), dependencies.rbegin(), dependencies.rend()); |
-} |
- |
-// Adds the given item to the list if it is unsynced and ready for commit. |
-bool Traversal::TryAddItem(const std::set<int64_t>& ready_unsynced_set, |
- const syncable::Entry& item, |
- syncable::Directory::Metahandles* result) const { |
- DCHECK(item.GetIsUnsynced()); |
- int64_t item_handle = item.GetMetahandle(); |
- if (ready_unsynced_set.count(item_handle) != 0) { |
- result->push_back(item_handle); |
- return true; |
- } |
- return false; |
-} |
- |
-// Traverses the tree from bottom to top, adding the deleted parents of the |
-// given |item|. Stops traversing if it encounters a non-deleted node, or |
-// a node that was already listed in the |traversed| list. |
-// |
-// The result list is reversed before it is returned, so the resulting |
-// traversal is in top to bottom order. Also note that this function appends |
-// to the result list without clearing it. |
-void Traversal::AddDeletedParents( |
- const std::set<int64_t>& ready_unsynced_set, |
- const syncable::Entry& item, |
- const syncable::Directory::Metahandles& traversed, |
- syncable::Directory::Metahandles* result) const { |
- DCHECK(SupportsHierarchy(item)); |
- syncable::Directory::Metahandles dependencies; |
- syncable::Id parent_id = item.GetParentId(); |
- |
- // Climb the tree adding entries leaf -> root. |
- while (!parent_id.IsRoot()) { |
- syncable::Entry parent(trans_, syncable::GET_BY_ID, parent_id); |
- |
- if (!parent.good()) { |
- // This is valid because the parent could have gone away a long time ago |
- // |
- // Consider the case where a folder is server-unknown and locally |
- // deleted, and has a child that is server-known, deleted, and unsynced. |
- // The parent could be dropped from memory at any time, but its child |
- // needs to be committed first. |
- break; |
- } |
- int64_t handle = parent.GetMetahandle(); |
- if (!parent.GetIsUnsynced()) { |
- // In some rare cases, our parent can be both deleted and unsynced. |
- // (ie. the server-unknown parent case). |
- break; |
- } |
- if (!parent.GetIsDel()) { |
- // We're not intersted in non-deleted parents. |
- break; |
- } |
- if (std::find(traversed.begin(), traversed.end(), handle) != |
- traversed.end()) { |
- // We've already added this parent (and therefore all of its parents). |
- // We can return early. |
- break; |
- } |
- |
- if (!TryAddItem(ready_unsynced_set, parent, &dependencies)) { |
- // The parent isn't in ready_unsynced_set. |
- break; |
- } |
- |
- parent_id = parent.GetParentId(); |
- } |
- |
- // Reverse what we added to get the correct order. |
- result->insert(result->end(), dependencies.rbegin(), dependencies.rend()); |
-} |
- |
-bool Traversal::IsFull() const { |
- return out_->size() >= max_entries_; |
-} |
- |
-bool Traversal::HaveItem(int64_t handle) const { |
- return added_handles_.find(handle) != added_handles_.end(); |
-} |
- |
-void Traversal::AppendManyToTraversal( |
- const syncable::Directory::Metahandles& handles) { |
- out_->insert(out_->end(), handles.begin(), handles.end()); |
- added_handles_.insert(handles.begin(), handles.end()); |
-} |
- |
-void Traversal::AppendToTraversal(int64_t metahandle) { |
- out_->push_back(metahandle); |
- added_handles_.insert(metahandle); |
-} |
- |
-void Traversal::AddCreatesAndMoves( |
- const std::set<int64_t>& ready_unsynced_set) { |
- // Add moves and creates, and prepend their uncommitted parents. |
- for (std::set<int64_t>::const_iterator iter = ready_unsynced_set.begin(); |
- !IsFull() && iter != ready_unsynced_set.end(); ++iter) { |
- int64_t metahandle = *iter; |
- if (HaveItem(metahandle)) |
- continue; |
- |
- syncable::Entry entry(trans_, |
- syncable::GET_BY_HANDLE, |
- metahandle); |
- if (!entry.GetIsDel()) { |
- if (SupportsHierarchy(entry)) { |
- // We only commit an item + its dependencies if it and all its |
- // dependencies are not in conflict. |
- syncable::Directory::Metahandles item_dependencies; |
- AddUncommittedParents(ready_unsynced_set, entry, &item_dependencies); |
- TryAddItem(ready_unsynced_set, entry, &item_dependencies); |
- AppendManyToTraversal(item_dependencies); |
- } else { |
- // No hierarchy dependencies, just commit the item itself. |
- AppendToTraversal(metahandle); |
- } |
- } |
- } |
- |
- // It's possible that we overcommitted while trying to expand dependent |
- // items. If so, truncate the set down to the allowed size. |
- if (out_->size() > max_entries_) |
- out_->resize(max_entries_); |
-} |
- |
-void Traversal::AddDeletes(const std::set<int64_t>& ready_unsynced_set) { |
- syncable::Directory::Metahandles deletion_list; |
- |
- // Note: we iterate over all the unsynced set, regardless of the max size. |
- // The max size is only enforced after the top-to-bottom order has been |
- // reversed, in order to ensure children are always deleted before parents. |
- for (std::set<int64_t>::const_iterator iter = ready_unsynced_set.begin(); |
- iter != ready_unsynced_set.end(); ++iter) { |
- int64_t metahandle = *iter; |
- |
- if (HaveItem(metahandle)) |
- continue; |
- |
- if (std::find(deletion_list.begin(), deletion_list.end(), metahandle) != |
- deletion_list.end()) { |
- continue; |
- } |
- |
- syncable::Entry entry(trans_, syncable::GET_BY_HANDLE, |
- metahandle); |
- |
- if (entry.GetIsDel()) { |
- if (SupportsHierarchy(entry)) { |
- syncable::Directory::Metahandles parents; |
- AddDeletedParents(ready_unsynced_set, entry, deletion_list, &parents); |
- // Append parents and chilren in top to bottom order. |
- deletion_list.insert(deletion_list.end(), parents.begin(), |
- parents.end()); |
- } |
- deletion_list.push_back(metahandle); |
- } |
- } |
- |
- // We've been gathering deletions in top to bottom order. Now we reverse the |
- // order as we prepare the list. |
- std::reverse(deletion_list.begin(), deletion_list.end()); |
- AppendManyToTraversal(deletion_list); |
- |
- // It's possible that we overcommitted while trying to expand dependent |
- // items. If so, truncate the set down to the allowed size. |
- if (out_->size() > max_entries_) |
- out_->resize(max_entries_); |
-} |
- |
-void OrderCommitIds(syncable::BaseTransaction* trans, |
- size_t max_entries, |
- const std::set<int64_t>& ready_unsynced_set, |
- syncable::Directory::Metahandles* out) { |
- // Commits follow these rules: |
- // 1. Moves or creates are preceded by needed folder creates, from |
- // root to leaf. |
- // 2. Moves/Creates before deletes. |
- // 3. Deletes, collapsed. |
- // We commit deleted moves under deleted items as moves when collapsing |
- // delete trees. |
- |
- Traversal traversal(trans, max_entries, out); |
- |
- // Add moves and creates, and prepend their uncommitted parents. |
- traversal.AddCreatesAndMoves(ready_unsynced_set); |
- |
- // Add all deletes. |
- traversal.AddDeletes(ready_unsynced_set); |
-} |
- |
-} // namespace |
- |
-} // namespace syncer |