Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(13)

Side by Side Diff: sync/engine/process_commit_response_command.cc

Issue 10038041: sync: Loop committing items without downloading updates (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "sync/engine/process_commit_response_command.h" 5 #include "sync/engine/process_commit_response_command.h"
6 6
7 #include <cstddef> 7 #include <cstddef>
8 #include <set> 8 #include <set>
9 #include <string> 9 #include <string>
10 #include <vector> 10 #include <vector>
(...skipping 30 matching lines...) Expand all
41 using syncable::SYNCER; 41 using syncable::SYNCER;
42 using syncable::SYNCING; 42 using syncable::SYNCING;
43 43
44 namespace browser_sync { 44 namespace browser_sync {
45 45
46 using sessions::OrderedCommitSet; 46 using sessions::OrderedCommitSet;
47 using sessions::StatusController; 47 using sessions::StatusController;
48 using sessions::SyncSession; 48 using sessions::SyncSession;
49 using sessions::ConflictProgress; 49 using sessions::ConflictProgress;
50 50
51 ProcessCommitResponseCommand::ProcessCommitResponseCommand() {} 51 ProcessCommitResponseCommand::ProcessCommitResponseCommand(
52 const sessions::OrderedCommitSet& commit_set,
53 const ClientToServerMessage& commit_message,
54 const ClientToServerResponse& commit_response)
55 : commit_set_(commit_set),
56 commit_message_(commit_message),
57 commit_response_(commit_response) {
58 }
59
52 ProcessCommitResponseCommand::~ProcessCommitResponseCommand() {} 60 ProcessCommitResponseCommand::~ProcessCommitResponseCommand() {}
53 61
54 std::set<ModelSafeGroup> ProcessCommitResponseCommand::GetGroupsToChange( 62 std::set<ModelSafeGroup> ProcessCommitResponseCommand::GetGroupsToChange(
55 const sessions::SyncSession& session) const { 63 const sessions::SyncSession& session) const {
56 std::set<ModelSafeGroup> groups_with_commits; 64 std::set<ModelSafeGroup> groups_with_commits;
57 65
58 syncable::Directory* dir = session.context()->directory(); 66 syncable::Directory* dir = session.context()->directory();
59 syncable::ReadTransaction trans(FROM_HERE, dir); 67 syncable::ReadTransaction trans(FROM_HERE, dir);
60 const StatusController& status = session.status_controller(); 68 for (size_t i = 0; i < commit_set_.Size(); ++i) {
61 for (size_t i = 0; i < status.commit_ids().size(); ++i) {
62 groups_with_commits.insert( 69 groups_with_commits.insert(
63 GetGroupForModelType(status.GetUnrestrictedCommitModelTypeAt(i), 70 GetGroupForModelType(commit_set_.GetModelTypeAt(i),
64 session.routing_info())); 71 session.routing_info()));
65 } 72 }
66 73
67 return groups_with_commits; 74 return groups_with_commits;
68 } 75 }
69 76
70 SyncerError ProcessCommitResponseCommand::ModelNeutralExecuteImpl( 77 SyncerError ProcessCommitResponseCommand::ModelNeutralExecuteImpl(
71 sessions::SyncSession* session) { 78 SyncSession* session) {
72 const StatusController& status = session->status_controller(); 79 syncable::Directory* dir = session->context()->directory();
73 const ClientToServerResponse& response(status.commit_response()); 80 const vector<syncable::Id>& commit_ids = commit_set_.GetAllCommitIds();
74 const vector<syncable::Id>& commit_ids(status.commit_ids());
75 81
76 if (!response.has_commit()) { 82 if (!commit_response_.has_commit()) {
77 // TODO(sync): What if we didn't try to commit anything?
78 LOG(WARNING) << "Commit response has no commit body!"; 83 LOG(WARNING) << "Commit response has no commit body!";
84 ClearSyncingBits(dir, commit_ids);
79 return SERVER_RESPONSE_VALIDATION_FAILED; 85 return SERVER_RESPONSE_VALIDATION_FAILED;
80 } 86 }
81 87
82 const CommitResponse& cr = response.commit(); 88 const CommitResponse& cr = commit_response_.commit();
83 int commit_count = commit_ids.size(); 89 int commit_count = commit_set_.Size();
84 if (cr.entryresponse_size() != commit_count) { 90 if (cr.entryresponse_size() != commit_count) {
85 LOG(ERROR) << "Commit response has wrong number of entries! Expected:" << 91 LOG(ERROR) << "Commit response has wrong number of entries! Expected:" <<
86 commit_count << " Got:" << cr.entryresponse_size(); 92 commit_count << " Got:" << cr.entryresponse_size();
87 for (int i = 0 ; i < cr.entryresponse_size() ; i++) { 93 for (int i = 0 ; i < cr.entryresponse_size() ; i++) {
88 LOG(ERROR) << "Response #" << i << " Value: " << 94 LOG(ERROR) << "Response #" << i << " Value: " <<
89 cr.entryresponse(i).response_type(); 95 cr.entryresponse(i).response_type();
90 if (cr.entryresponse(i).has_error_message()) 96 if (cr.entryresponse(i).has_error_message())
91 LOG(ERROR) << " " << cr.entryresponse(i).error_message(); 97 LOG(ERROR) << " " << cr.entryresponse(i).error_message();
92 } 98 }
99 ClearSyncingBits(dir, commit_ids);
93 return SERVER_RESPONSE_VALIDATION_FAILED; 100 return SERVER_RESPONSE_VALIDATION_FAILED;
94 } 101 }
95 return SYNCER_OK; 102 return SYNCER_OK;
96 } 103 }
97 104
98 SyncerError ProcessCommitResponseCommand::ModelChangingExecuteImpl( 105 SyncerError ProcessCommitResponseCommand::ModelChangingExecuteImpl(
99 SyncSession* session) { 106 SyncSession* session) {
100 SyncerError result = ProcessCommitResponse(session); 107 SyncerError result = ProcessCommitResponse(session);
101 ExtensionsActivityMonitor* monitor = session->context()->extensions_monitor(); 108 ExtensionsActivityMonitor* monitor = session->context()->extensions_monitor();
102 if (session->status_controller().HasBookmarkCommitActivity() && 109
103 session->status_controller().syncer_status() 110 // This is to be run on one model only: the bookmark model.
111 if (session->status_controller()
112 .ActiveGroupRestrictionIncludesModel(syncable::BOOKMARKS)) {
113 // If the commit failed, return the data to the ExtensionsActivityMonitor.
114 if (session->status_controller().syncer_status()
104 .num_successful_bookmark_commits == 0) { 115 .num_successful_bookmark_commits == 0) {
105 monitor->PutRecords(session->extensions_activity()); 116 monitor->PutRecords(session->extensions_activity());
117 }
118 // Clear our cached data in either case.
106 session->mutable_extensions_activity()->clear(); 119 session->mutable_extensions_activity()->clear();
107 } 120 }
121
108 return result; 122 return result;
109 } 123 }
110 124
111 SyncerError ProcessCommitResponseCommand::ProcessCommitResponse( 125 SyncerError ProcessCommitResponseCommand::ProcessCommitResponse(
112 SyncSession* session) { 126 SyncSession* session) {
113 syncable::Directory* dir = session->context()->directory(); 127 syncable::Directory* dir = session->context()->directory();
128 StatusController* status = session->mutable_status_controller();
129 const CommitResponse& cr = commit_response_.commit();
130 const sync_pb::CommitMessage& commit_message = commit_message_.commit();
114 131
115 StatusController* status = session->mutable_status_controller();
116 const ClientToServerResponse& response(status->commit_response());
117 const CommitResponse& cr = response.commit();
118 const sync_pb::CommitMessage& commit_message =
119 status->commit_message().commit();
120
121 // If we try to commit a parent and child together and the parent conflicts
122 // the child will have a bad parent causing an error. As this is not a
123 // critical error, we trap it and don't LOG(ERROR). To enable this we keep
124 // a map of conflicting new folders.
125 int transient_error_commits = 0; 132 int transient_error_commits = 0;
126 int conflicting_commits = 0; 133 int conflicting_commits = 0;
127 int error_commits = 0; 134 int error_commits = 0;
128 int successes = 0; 135 int successes = 0;
129 set<syncable::Id> conflicting_new_folder_ids; 136
130 set<syncable::Id> deleted_folders; 137 set<syncable::Id> deleted_folders;
131 ConflictProgress* conflict_progress = status->mutable_conflict_progress(); 138 ConflictProgress* conflict_progress = status->mutable_conflict_progress();
132 OrderedCommitSet::Projection proj = status->commit_id_projection(); 139 OrderedCommitSet::Projection proj = status->commit_id_projection(
140 commit_set_);
141
133 if (!proj.empty()) { // Scope for WriteTransaction. 142 if (!proj.empty()) { // Scope for WriteTransaction.
134 WriteTransaction trans(FROM_HERE, SYNCER, dir); 143 WriteTransaction trans(FROM_HERE, SYNCER, dir);
135 for (size_t i = 0; i < proj.size(); i++) { 144 for (size_t i = 0; i < proj.size(); i++) {
136 CommitResponse::ResponseType response_type = 145 CommitResponse::ResponseType response_type = ProcessSingleCommitResponse(
137 ProcessSingleCommitResponse(&trans, cr.entryresponse(proj[i]), 146 &trans,
138 commit_message.entries(proj[i]), 147 cr.entryresponse(proj[i]),
139 status->GetCommitIdAt(proj[i]), 148 commit_message.entries(proj[i]),
140 &conflicting_new_folder_ids, 149 commit_set_.GetCommitIdAt(proj[i]),
141 &deleted_folders); 150 &deleted_folders);
142 switch (response_type) { 151 switch (response_type) {
143 case CommitResponse::INVALID_MESSAGE: 152 case CommitResponse::INVALID_MESSAGE:
144 ++error_commits; 153 ++error_commits;
145 break; 154 break;
146 case CommitResponse::CONFLICT: 155 case CommitResponse::CONFLICT:
147 ++conflicting_commits; 156 ++conflicting_commits;
148 // Only server CONFLICT responses will activate conflict resolution.
149 conflict_progress->AddServerConflictingItemById( 157 conflict_progress->AddServerConflictingItemById(
150 status->GetCommitIdAt(proj[i])); 158 commit_set_.GetCommitIdAt(proj[i]));
151 break; 159 break;
152 case CommitResponse::SUCCESS: 160 case CommitResponse::SUCCESS:
153 // TODO(sync): worry about sync_rate_ rate calc? 161 // TODO(sync): worry about sync_rate_ rate calc?
154 ++successes; 162 ++successes;
155 if (status->GetCommitModelTypeAt(proj[i]) == syncable::BOOKMARKS) 163 if (commit_set_.GetModelTypeAt(proj[i]) == syncable::BOOKMARKS)
156 status->increment_num_successful_bookmark_commits(); 164 status->increment_num_successful_bookmark_commits();
157 status->increment_num_successful_commits(); 165 status->increment_num_successful_commits();
158 break; 166 break;
159 case CommitResponse::OVER_QUOTA: 167 case CommitResponse::OVER_QUOTA:
160 // We handle over quota like a retry, which is same as transient. 168 // We handle over quota like a retry, which is same as transient.
161 case CommitResponse::RETRY: 169 case CommitResponse::RETRY:
162 case CommitResponse::TRANSIENT_ERROR: 170 case CommitResponse::TRANSIENT_ERROR:
163 ++transient_error_commits; 171 ++transient_error_commits;
164 break; 172 break;
165 default: 173 default:
166 LOG(FATAL) << "Bad return from ProcessSingleCommitResponse"; 174 LOG(FATAL) << "Bad return from ProcessSingleCommitResponse";
167 } 175 }
168 } 176 }
169 } 177 }
170 178
171 SyncerUtil::MarkDeletedChildrenSynced(dir, &deleted_folders); 179 SyncerUtil::MarkDeletedChildrenSynced(dir, &deleted_folders);
172 180
173 int commit_count = static_cast<int>(proj.size()); 181 int commit_count = static_cast<int>(proj.size());
174 if (commit_count == (successes + conflicting_commits)) { 182 if (commit_count == successes) {
175 // We consider conflicting commits as a success because things will work out
176 // on their own when we receive them. Flags will be set so that
177 // HasMoreToSync() will cause SyncScheduler to enter another sync cycle to
178 // handle this condition.
179 return SYNCER_OK; 183 return SYNCER_OK;
180 } else if (error_commits > 0) { 184 } else if (error_commits > 0) {
181 return SERVER_RETURN_UNKNOWN_ERROR; 185 return SERVER_RETURN_UNKNOWN_ERROR;
182 } else if (transient_error_commits > 0) { 186 } else if (transient_error_commits > 0) {
183 return SERVER_RETURN_TRANSIENT_ERROR; 187 return SERVER_RETURN_TRANSIENT_ERROR;
188 } else if (conflicting_commits > 0) {
189 // This means that the server already has an item with this version, but
190 // we haven't seen that update yet. The correct response is to go back
191 // to the download upates phase and try to fetch the conflicting update
192 // so we can resolve it.
193 //
194 // The easiest and safest way to handle this is to treat it as a transient
195 // error. This will cause us to abort this sync cycle, wait a little while,
196 // then try again starting with a GetUpdate.
197 //
198 // TODO: Remove this option when the CONFLICT return value is fully
199 // deprecated.
200 return SERVER_RETURN_TRANSIENT_ERROR;
rlarocque 2012/04/13 02:02:22 You could argue that there should be some other re
184 } else { 201 } else {
185 LOG(FATAL) << "Inconsistent counts when processing commit response"; 202 LOG(FATAL) << "Inconsistent counts when processing commit response";
186 return SYNCER_OK; 203 return SYNCER_OK;
187 } 204 }
188 } 205 }
189 206
190 void LogServerError(const CommitResponse_EntryResponse& res) { 207 void LogServerError(const CommitResponse_EntryResponse& res) {
191 if (res.has_error_message()) 208 if (res.has_error_message())
192 LOG(WARNING) << " " << res.error_message(); 209 LOG(WARNING) << " " << res.error_message();
193 else 210 else
194 LOG(WARNING) << " No detailed error message returned from server"; 211 LOG(WARNING) << " No detailed error message returned from server";
195 } 212 }
196 213
197 CommitResponse::ResponseType 214 CommitResponse::ResponseType
198 ProcessCommitResponseCommand::ProcessSingleCommitResponse( 215 ProcessCommitResponseCommand::ProcessSingleCommitResponse(
199 syncable::WriteTransaction* trans, 216 syncable::WriteTransaction* trans,
200 const sync_pb::CommitResponse_EntryResponse& pb_server_entry, 217 const sync_pb::CommitResponse_EntryResponse& pb_server_entry,
201 const sync_pb::SyncEntity& commit_request_entry, 218 const sync_pb::SyncEntity& commit_request_entry,
202 const syncable::Id& pre_commit_id, 219 const syncable::Id& pre_commit_id,
203 std::set<syncable::Id>* conflicting_new_folder_ids,
204 set<syncable::Id>* deleted_folders) { 220 set<syncable::Id>* deleted_folders) {
205 221
206 const CommitResponse_EntryResponse& server_entry = 222 const CommitResponse_EntryResponse& server_entry =
207 *static_cast<const CommitResponse_EntryResponse*>(&pb_server_entry); 223 *static_cast<const CommitResponse_EntryResponse*>(&pb_server_entry);
208 MutableEntry local_entry(trans, GET_BY_ID, pre_commit_id); 224 MutableEntry local_entry(trans, GET_BY_ID, pre_commit_id);
209 CHECK(local_entry.good()); 225 CHECK(local_entry.good());
210 bool syncing_was_set = local_entry.Get(SYNCING); 226 bool syncing_was_set = local_entry.Get(SYNCING);
211 local_entry.Put(SYNCING, false); 227 local_entry.Put(SYNCING, false);
212 228
213 CommitResponse::ResponseType response = (CommitResponse::ResponseType) 229 CommitResponse::ResponseType response = (CommitResponse::ResponseType)
214 server_entry.response_type(); 230 server_entry.response_type();
215 if (!CommitResponse::ResponseType_IsValid(response)) { 231 if (!CommitResponse::ResponseType_IsValid(response)) {
216 LOG(ERROR) << "Commit response has unknown response type! Possibly out " 232 LOG(ERROR) << "Commit response has unknown response type! Possibly out "
217 "of date client?"; 233 "of date client?";
218 return CommitResponse::INVALID_MESSAGE; 234 return CommitResponse::INVALID_MESSAGE;
219 } 235 }
220 if (CommitResponse::TRANSIENT_ERROR == response) { 236 if (CommitResponse::TRANSIENT_ERROR == response) {
221 DVLOG(1) << "Transient Error Committing: " << local_entry; 237 DVLOG(1) << "Transient Error Committing: " << local_entry;
222 LogServerError(server_entry); 238 LogServerError(server_entry);
223 return CommitResponse::TRANSIENT_ERROR; 239 return CommitResponse::TRANSIENT_ERROR;
224 } 240 }
225 if (CommitResponse::INVALID_MESSAGE == response) { 241 if (CommitResponse::INVALID_MESSAGE == response) {
226 LOG(ERROR) << "Error Commiting: " << local_entry; 242 LOG(ERROR) << "Error Commiting: " << local_entry;
227 LogServerError(server_entry); 243 LogServerError(server_entry);
228 return response; 244 return response;
229 } 245 }
230 if (CommitResponse::CONFLICT == response) { 246 if (CommitResponse::CONFLICT == response) {
231 DVLOG(1) << "Conflict Committing: " << local_entry; 247 DVLOG(1) << "Conflict Committing: " << local_entry;
232 // TODO(nick): conflicting_new_folder_ids is a purposeless anachronism.
233 if (!pre_commit_id.ServerKnows() && local_entry.Get(IS_DIR)) {
234 conflicting_new_folder_ids->insert(pre_commit_id);
235 }
236 return response; 248 return response;
237 } 249 }
238 if (CommitResponse::RETRY == response) { 250 if (CommitResponse::RETRY == response) {
239 DVLOG(1) << "Retry Committing: " << local_entry; 251 DVLOG(1) << "Retry Committing: " << local_entry;
240 return response; 252 return response;
241 } 253 }
242 if (CommitResponse::OVER_QUOTA == response) { 254 if (CommitResponse::OVER_QUOTA == response) {
243 LOG(WARNING) << "Hit deprecated OVER_QUOTA Committing: " << local_entry; 255 LOG(WARNING) << "Hit deprecated OVER_QUOTA Committing: " << local_entry;
244 return response; 256 return response;
245 } 257 }
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after
472 // Make a note of any deleted folders, whose children would have 484 // Make a note of any deleted folders, whose children would have
473 // been recursively deleted. 485 // been recursively deleted.
474 // TODO(nick): Here, commit_message.deleted() would be more correct than 486 // TODO(nick): Here, commit_message.deleted() would be more correct than
475 // local_entry->Get(IS_DEL). For example, an item could be renamed, and then 487 // local_entry->Get(IS_DEL). For example, an item could be renamed, and then
476 // deleted during the commit of the rename. Unit test & fix. 488 // deleted during the commit of the rename. Unit test & fix.
477 if (local_entry->Get(IS_DIR) && local_entry->Get(IS_DEL)) { 489 if (local_entry->Get(IS_DIR) && local_entry->Get(IS_DEL)) {
478 deleted_folders->insert(local_entry->Get(ID)); 490 deleted_folders->insert(local_entry->Get(ID));
479 } 491 }
480 } 492 }
481 493
494 void ProcessCommitResponseCommand::ClearSyncingBits(
495 syncable::Directory *dir,
496 const vector<syncable::Id>& commit_ids) {
497 // This is part of the cleanup in the case of a failed commit. Normally we
498 // would unset the SYNCING bit when processing the commit response. In the
499 // failure case we don't process the response, so we need to clear those bits
500 // here.
501 syncable::WriteTransaction trans(FROM_HERE, syncable::SYNCER, dir);
502 for (size_t i = 0; i < commit_ids.size(); i++) {
503 syncable::MutableEntry entry(&trans, syncable::GET_BY_ID, commit_ids[i]);
504 if (entry.good()) {
505 entry.Put(syncable::SYNCING, false);
506 } else {
507 LOG(WARNING) << "Id: " << commit_ids[i] << " disappeared";
508 }
509 }
510 }
511
482 } // namespace browser_sync 512 } // namespace browser_sync
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698