1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
|
// Copyright 2014 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 "components/sync/engine_impl/directory_commit_contribution.h"
#include <algorithm>
#include <set>
#include "components/sync/engine/cycle/commit_counters.h"
#include "components/sync/engine_impl/commit_util.h"
#include "components/sync/engine_impl/get_commit_ids.h"
#include "components/sync/engine_impl/syncer_util.h"
#include "components/sync/syncable/model_neutral_mutable_entry.h"
#include "components/sync/syncable/syncable_model_neutral_write_transaction.h"
namespace syncer {
using syncable::GET_BY_HANDLE;
using syncable::SYNCER;
DirectoryCommitContribution::~DirectoryCommitContribution() {
DCHECK(!syncing_bits_set_);
}
// static.
std::unique_ptr<DirectoryCommitContribution> DirectoryCommitContribution::Build(
syncable::Directory* dir,
ModelType type,
size_t max_entries,
DataTypeDebugInfoEmitter* debug_info_emitter) {
DCHECK(debug_info_emitter);
std::vector<int64_t> metahandles;
syncable::ModelNeutralWriteTransaction trans(FROM_HERE, SYNCER, dir);
GetCommitIdsForType(&trans, type, max_entries, &metahandles);
if (metahandles.empty())
return std::unique_ptr<DirectoryCommitContribution>();
google::protobuf::RepeatedPtrField<sync_pb::SyncEntity> entities;
for (std::vector<int64_t>::iterator it = metahandles.begin();
it != metahandles.end(); ++it) {
sync_pb::SyncEntity* entity = entities.Add();
syncable::ModelNeutralMutableEntry entry(&trans, GET_BY_HANDLE, *it);
commit_util::BuildCommitItem(entry, entity);
entry.PutSyncing(true);
}
sync_pb::DataTypeContext context;
dir->GetDataTypeContext(&trans, type, &context);
return std::unique_ptr<DirectoryCommitContribution>(
new DirectoryCommitContribution(metahandles, entities, context, dir,
debug_info_emitter));
}
void DirectoryCommitContribution::AddToCommitMessage(
sync_pb::ClientToServerMessage* msg) {
DCHECK(syncing_bits_set_);
sync_pb::CommitMessage* commit_message = msg->mutable_commit();
entries_start_index_ = commit_message->entries_size();
std::copy(entities_.begin(), entities_.end(),
RepeatedPtrFieldBackInserter(commit_message->mutable_entries()));
if (!context_.context().empty())
commit_message->add_client_contexts()->Swap(&context_);
CommitCounters* counters = debug_info_emitter_->GetMutableCommitCounters();
counters->num_commits_attempted += entities_.size();
}
SyncerError DirectoryCommitContribution::ProcessCommitResponse(
const sync_pb::ClientToServerResponse& response,
StatusController* status) {
DCHECK(syncing_bits_set_);
const sync_pb::CommitResponse& commit_response = response.commit();
int transient_error_commits = 0;
int conflicting_commits = 0;
int error_commits = 0;
int successes = 0;
std::set<syncable::Id> deleted_folders;
{
syncable::ModelNeutralWriteTransaction trans(FROM_HERE, SYNCER, dir_);
for (size_t i = 0; i < metahandles_.size(); ++i) {
sync_pb::CommitResponse::ResponseType response_type =
commit_util::ProcessSingleCommitResponse(
&trans, commit_response.entryresponse(entries_start_index_ + i),
entities_.Get(i), metahandles_[i], &deleted_folders);
switch (response_type) {
case sync_pb::CommitResponse::INVALID_MESSAGE:
++error_commits;
break;
case sync_pb::CommitResponse::CONFLICT:
++conflicting_commits;
status->increment_num_server_conflicts();
break;
case sync_pb::CommitResponse::SUCCESS:
++successes;
{
syncable::Entry e(&trans, GET_BY_HANDLE, metahandles_[i]);
if (e.GetModelType() == BOOKMARKS)
status->increment_num_successful_bookmark_commits();
}
status->increment_num_successful_commits();
break;
case sync_pb::CommitResponse::OVER_QUOTA:
// We handle over quota like a retry, which is same as transient.
case sync_pb::CommitResponse::RETRY:
case sync_pb::CommitResponse::TRANSIENT_ERROR:
++transient_error_commits;
break;
default:
LOG(FATAL) << "Bad return from ProcessSingleCommitResponse";
}
}
MarkDeletedChildrenSynced(dir_, &trans, &deleted_folders);
}
CommitCounters* counters = debug_info_emitter_->GetMutableCommitCounters();
counters->num_commits_success += successes;
counters->num_commits_conflict += conflicting_commits;
counters->num_commits_error += transient_error_commits;
int commit_count = static_cast<int>(metahandles_.size());
if (commit_count == successes) {
return SYNCER_OK;
} else if (error_commits > 0) {
return SERVER_RETURN_UNKNOWN_ERROR;
} else if (transient_error_commits > 0) {
return SERVER_RETURN_TRANSIENT_ERROR;
} else if (conflicting_commits > 0) {
// This means that the server already has an item with this version, but
// we haven't seen that update yet.
//
// A well-behaved client should respond to this by proceeding to the
// download updates phase, fetching the conflicting items, then attempting
// to resolve the conflict. That's not what this client does.
//
// We don't currently have any code to support that exceptional control
// flow. Instead, we abort the current sync cycle and start a new one. The
// end result is the same.
return SERVER_RETURN_CONFLICT;
} else {
LOG(FATAL) << "Inconsistent counts when processing commit response";
return SYNCER_OK;
}
}
void DirectoryCommitContribution::CleanUp() {
DCHECK(syncing_bits_set_);
UnsetSyncingBits();
debug_info_emitter_->EmitCommitCountersUpdate();
debug_info_emitter_->EmitStatusCountersUpdate();
}
size_t DirectoryCommitContribution::GetNumEntries() const {
return metahandles_.size();
}
DirectoryCommitContribution::DirectoryCommitContribution(
const std::vector<int64_t>& metahandles,
const google::protobuf::RepeatedPtrField<sync_pb::SyncEntity>& entities,
const sync_pb::DataTypeContext& context,
syncable::Directory* dir,
DataTypeDebugInfoEmitter* debug_info_emitter)
: dir_(dir),
metahandles_(metahandles),
entities_(entities),
context_(context),
entries_start_index_(0xDEADBEEF),
syncing_bits_set_(true),
debug_info_emitter_(debug_info_emitter) {}
void DirectoryCommitContribution::UnsetSyncingBits() {
syncable::ModelNeutralWriteTransaction trans(FROM_HERE, SYNCER, dir_);
for (std::vector<int64_t>::const_iterator it = metahandles_.begin();
it != metahandles_.end(); ++it) {
syncable::ModelNeutralMutableEntry entry(&trans, GET_BY_HANDLE, *it);
// TODO(sync): this seems like it could be harmful if a sync cycle doesn't
// complete but the Cleanup method is called anyways. It appears these are
// unset on the assumption that the sync cycle must have finished properly,
// although that's actually up to the commit response handling logic.
entry.PutDirtySync(false);
entry.PutSyncing(false);
}
syncing_bits_set_ = false;
}
} // namespace syncer
|