// Copyright 2012 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/update_applicator.h" #include <vector> #include "base/logging.h" #include "sync/engine/syncer_util.h" #include "sync/syncable/entry.h" #include "sync/syncable/mutable_entry.h" #include "sync/syncable/syncable_id.h" #include "sync/syncable/syncable_write_transaction.h" using std::vector; namespace syncer { using syncable::ID; UpdateApplicator::UpdateApplicator(Cryptographer* cryptographer) : cryptographer_(cryptographer), updates_applied_(0), encryption_conflicts_(0), hierarchy_conflicts_(0) { } UpdateApplicator::~UpdateApplicator() { } // Attempt to apply all updates, using multiple passes if necessary. // // Some updates must be applied in order. For example, children must be created // after their parent folder is created. This function runs an O(n^2) algorithm // that will keep trying until there is nothing left to apply, or it stops // making progress, which would indicate that the hierarchy is invalid. // // The update applicator also has to deal with simple conflicts, which occur // when an item is modified on both the server and the local model. We remember // their IDs so they can be passed to the conflict resolver after all the other // applications are complete. // // Finally, there are encryption conflicts, which can occur when we don't have // access to all the Nigori keys. There's nothing we can do about them here. void UpdateApplicator::AttemptApplications( syncable::WriteTransaction* trans, const std::vector<int64>& handles) { std::vector<int64> to_apply = handles; DVLOG(1) << "UpdateApplicator running over " << to_apply.size() << " items."; while (!to_apply.empty()) { std::vector<int64> to_reapply; for (std::vector<int64>::iterator i = to_apply.begin(); i != to_apply.end(); ++i) { syncable::MutableEntry entry(trans, syncable::GET_BY_HANDLE, *i); UpdateAttemptResponse result = AttemptToUpdateEntry( trans, &entry, cryptographer_); switch (result) { case SUCCESS: updates_applied_++; break; case CONFLICT_SIMPLE: simple_conflict_ids_.insert(entry.GetId()); break; case CONFLICT_ENCRYPTION: encryption_conflicts_++; break; case CONFLICT_HIERARCHY: // The decision to classify these as hierarchy conflcits is tentative. // If we make any progress this round, we'll clear the hierarchy // conflict count and attempt to reapply these updates. to_reapply.push_back(*i); break; default: NOTREACHED(); break; } } if (to_reapply.size() == to_apply.size()) { // We made no progress. Must be stubborn hierarchy conflicts. hierarchy_conflicts_ = to_apply.size(); break; } // We made some progress, so prepare for what might be another iteration. // If everything went well, to_reapply will be empty and we'll break out on // the while condition. to_apply.swap(to_reapply); to_reapply.clear(); } } } // namespace syncer