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/syncable/model_neutral_mutable_entry.h"
11 #include "sync/syncable/syncable_model_neutral_write_transaction.h"
15 using syncable::GET_BY_HANDLE
;
16 using syncable::SYNCER
;
18 DirectoryCommitContribution::~DirectoryCommitContribution() {
19 DCHECK(!syncing_bits_set_
);
23 scoped_ptr
<DirectoryCommitContribution
> DirectoryCommitContribution::Build(
24 syncable::Directory
* dir
,
27 DirectoryTypeDebugInfoEmitter
* debug_info_emitter
) {
28 DCHECK(debug_info_emitter
);
30 std::vector
<int64
> metahandles
;
32 syncable::ModelNeutralWriteTransaction
trans(FROM_HERE
, SYNCER
, dir
);
33 GetCommitIdsForType(&trans
, type
, max_entries
, &metahandles
);
35 if (metahandles
.empty())
36 return scoped_ptr
<DirectoryCommitContribution
>();
38 google::protobuf::RepeatedPtrField
<sync_pb::SyncEntity
> entities
;
39 for (std::vector
<int64
>::iterator it
= metahandles
.begin();
40 it
!= metahandles
.end(); ++it
) {
41 sync_pb::SyncEntity
* entity
= entities
.Add();
42 syncable::ModelNeutralMutableEntry
entry(&trans
, GET_BY_HANDLE
, *it
);
43 commit_util::BuildCommitItem(entry
, entity
);
44 entry
.PutSyncing(true);
47 sync_pb::DataTypeContext context
;
48 dir
->GetDataTypeContext(&trans
, type
, &context
);
50 return scoped_ptr
<DirectoryCommitContribution
>(
51 new DirectoryCommitContribution(
59 void DirectoryCommitContribution::AddToCommitMessage(
60 sync_pb::ClientToServerMessage
* msg
) {
61 DCHECK(syncing_bits_set_
);
62 sync_pb::CommitMessage
* commit_message
= msg
->mutable_commit();
63 entries_start_index_
= commit_message
->entries_size();
64 std::copy(entities_
.begin(),
66 RepeatedPtrFieldBackInserter(commit_message
->mutable_entries()));
67 if (!context_
.context().empty())
68 commit_message
->add_client_contexts()->Swap(&context_
);
71 SyncerError
DirectoryCommitContribution::ProcessCommitResponse(
72 const sync_pb::ClientToServerResponse
& response
,
73 sessions::StatusController
* status
) {
74 DCHECK(syncing_bits_set_
);
75 const sync_pb::CommitResponse
& commit_response
= response
.commit();
77 int transient_error_commits
= 0;
78 int conflicting_commits
= 0;
79 int error_commits
= 0;
82 std::set
<syncable::Id
> deleted_folders
;
84 syncable::ModelNeutralWriteTransaction
trans(FROM_HERE
, SYNCER
, dir_
);
85 for (size_t i
= 0; i
< metahandles_
.size(); ++i
) {
86 sync_pb::CommitResponse::ResponseType response_type
=
87 commit_util::ProcessSingleCommitResponse(
89 commit_response
.entryresponse(entries_start_index_
+ i
),
93 switch (response_type
) {
94 case sync_pb::CommitResponse::INVALID_MESSAGE
:
97 case sync_pb::CommitResponse::CONFLICT
:
98 ++conflicting_commits
;
99 status
->increment_num_server_conflicts();
101 case sync_pb::CommitResponse::SUCCESS
:
104 syncable::Entry
e(&trans
, GET_BY_HANDLE
, metahandles_
[i
]);
105 if (e
.GetModelType() == BOOKMARKS
)
106 status
->increment_num_successful_bookmark_commits();
108 status
->increment_num_successful_commits();
110 case sync_pb::CommitResponse::OVER_QUOTA
:
111 // We handle over quota like a retry, which is same as transient.
112 case sync_pb::CommitResponse::RETRY
:
113 case sync_pb::CommitResponse::TRANSIENT_ERROR
:
114 ++transient_error_commits
;
117 LOG(FATAL
) << "Bad return from ProcessSingleCommitResponse";
120 MarkDeletedChildrenSynced(dir_
, &trans
, &deleted_folders
);
123 int commit_count
= static_cast<int>(metahandles_
.size());
124 if (commit_count
== successes
) {
126 } else if (error_commits
> 0) {
127 return SERVER_RETURN_UNKNOWN_ERROR
;
128 } else if (transient_error_commits
> 0) {
129 return SERVER_RETURN_TRANSIENT_ERROR
;
130 } else if (conflicting_commits
> 0) {
131 // This means that the server already has an item with this version, but
132 // we haven't seen that update yet.
134 // A well-behaved client should respond to this by proceeding to the
135 // download updates phase, fetching the conflicting items, then attempting
136 // to resolve the conflict. That's not what this client does.
138 // We don't currently have any code to support that exceptional control
139 // flow. Instead, we abort the current sync cycle and start a new one. The
140 // end result is the same.
141 return SERVER_RETURN_CONFLICT
;
143 LOG(FATAL
) << "Inconsistent counts when processing commit response";
148 void DirectoryCommitContribution::CleanUp() {
149 DCHECK(syncing_bits_set_
);
153 size_t DirectoryCommitContribution::GetNumEntries() const {
154 return metahandles_
.size();
157 DirectoryCommitContribution::DirectoryCommitContribution(
158 const std::vector
<int64
>& metahandles
,
159 const google::protobuf::RepeatedPtrField
<sync_pb::SyncEntity
>& entities
,
160 const sync_pb::DataTypeContext
& context
,
161 syncable::Directory
* dir
,
162 DirectoryTypeDebugInfoEmitter
* debug_info_emitter
)
164 metahandles_(metahandles
),
167 entries_start_index_(0xDEADBEEF),
168 syncing_bits_set_(true),
169 debug_info_emitter_(debug_info_emitter
) {}
171 void DirectoryCommitContribution::UnsetSyncingBits() {
172 syncable::ModelNeutralWriteTransaction
trans(FROM_HERE
, SYNCER
, dir_
);
173 for (std::vector
<int64
>::const_iterator it
= metahandles_
.begin();
174 it
!= metahandles_
.end(); ++it
) {
175 syncable::ModelNeutralMutableEntry
entry(&trans
, GET_BY_HANDLE
, *it
);
176 entry
.PutSyncing(false);
178 syncing_bits_set_
= false;
181 } // namespace syncer