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.
5 #include "sync/engine/directory_commit_contribution.h"
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"
16 using syncable::GET_BY_HANDLE
;
17 using syncable::SYNCER
;
19 DirectoryCommitContribution::~DirectoryCommitContribution() {
20 DCHECK(!syncing_bits_set_
);
24 scoped_ptr
<DirectoryCommitContribution
> DirectoryCommitContribution::Build(
25 syncable::Directory
* dir
,
28 DirectoryTypeDebugInfoEmitter
* debug_info_emitter
) {
29 DCHECK(debug_info_emitter
);
31 std::vector
<int64
> metahandles
;
33 syncable::ModelNeutralWriteTransaction
trans(FROM_HERE
, SYNCER
, dir
);
34 GetCommitIdsForType(&trans
, type
, max_entries
, &metahandles
);
36 if (metahandles
.empty())
37 return scoped_ptr
<DirectoryCommitContribution
>();
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);
48 sync_pb::DataTypeContext context
;
49 dir
->GetDataTypeContext(&trans
, type
, &context
);
51 return scoped_ptr
<DirectoryCommitContribution
>(
52 new DirectoryCommitContribution(
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(),
67 RepeatedPtrFieldBackInserter(commit_message
->mutable_entries()));
68 if (!context_
.context().empty())
69 commit_message
->add_client_contexts()->Swap(&context_
);
71 CommitCounters
* counters
= debug_info_emitter_
->GetMutableCommitCounters();
72 counters
->num_commits_attempted
+= entities_
.size();
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();
81 int transient_error_commits
= 0;
82 int conflicting_commits
= 0;
83 int error_commits
= 0;
86 std::set
<syncable::Id
> deleted_folders
;
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(
93 commit_response
.entryresponse(entries_start_index_
+ i
),
97 switch (response_type
) {
98 case sync_pb::CommitResponse::INVALID_MESSAGE
:
101 case sync_pb::CommitResponse::CONFLICT
:
102 ++conflicting_commits
;
103 status
->increment_num_server_conflicts();
105 case sync_pb::CommitResponse::SUCCESS
:
108 syncable::Entry
e(&trans
, GET_BY_HANDLE
, metahandles_
[i
]);
109 if (e
.GetModelType() == BOOKMARKS
)
110 status
->increment_num_successful_bookmark_commits();
112 status
->increment_num_successful_commits();
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
;
121 LOG(FATAL
) << "Bad return from ProcessSingleCommitResponse";
124 MarkDeletedChildrenSynced(dir_
, &trans
, &deleted_folders
);
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
;
132 int commit_count
= static_cast<int>(metahandles_
.size());
133 if (commit_count
== successes
) {
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.
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.
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
;
152 LOG(FATAL
) << "Inconsistent counts when processing commit response";
157 void DirectoryCommitContribution::CleanUp() {
158 DCHECK(syncing_bits_set_
);
160 debug_info_emitter_
->EmitCommitCountersUpdate();
161 debug_info_emitter_
->EmitStatusCountersUpdate();
164 size_t DirectoryCommitContribution::GetNumEntries() const {
165 return metahandles_
.size();
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
)
175 metahandles_(metahandles
),
178 entries_start_index_(0xDEADBEEF),
179 syncing_bits_set_(true),
180 debug_info_emitter_(debug_info_emitter
) {}
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 // TODO(sync): this seems like it could be harmful if a sync cycle doesn't
188 // complete but the Cleanup method is called anyways. It appears these are
189 // unset on the assumption that the sync cycle must have finished properly,
190 // although that's actually up to the commit response handling logic.
191 entry
.PutDirtySync(false);
192 entry
.PutSyncing(false);
194 syncing_bits_set_
= false;
197 } // namespace syncer