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