OLD | NEW |
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 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/commit_util.h" |
6 | 6 |
7 #include <cstddef> | 7 #include <limits> |
8 #include <set> | 8 #include <set> |
9 #include <string> | 9 #include <string> |
10 #include <vector> | 10 #include <vector> |
11 | 11 |
12 #include "base/basictypes.h" | 12 #include "base/strings/string_util.h" |
13 #include "base/location.h" | |
14 #include "sync/engine/syncer_proto_util.h" | 13 #include "sync/engine/syncer_proto_util.h" |
15 #include "sync/engine/syncer_util.h" | |
16 #include "sync/internal_api/public/base/unique_position.h" | 14 #include "sync/internal_api/public/base/unique_position.h" |
| 15 #include "sync/protocol/bookmark_specifics.pb.h" |
| 16 #include "sync/protocol/sync.pb.h" |
17 #include "sync/sessions/sync_session.h" | 17 #include "sync/sessions/sync_session.h" |
| 18 #include "sync/syncable/directory.h" |
18 #include "sync/syncable/entry.h" | 19 #include "sync/syncable/entry.h" |
19 #include "sync/syncable/model_neutral_mutable_entry.h" | 20 #include "sync/syncable/model_neutral_mutable_entry.h" |
20 #include "sync/syncable/syncable_model_neutral_write_transaction.h" | 21 #include "sync/syncable/syncable_base_transaction.h" |
| 22 #include "sync/syncable/syncable_base_write_transaction.h" |
| 23 #include "sync/syncable/syncable_changes_version.h" |
21 #include "sync/syncable/syncable_proto_util.h" | 24 #include "sync/syncable/syncable_proto_util.h" |
22 #include "sync/syncable/syncable_read_transaction.h" | |
23 #include "sync/syncable/syncable_util.h" | 25 #include "sync/syncable/syncable_util.h" |
24 #include "sync/util/time.h" | 26 #include "sync/util/time.h" |
25 | 27 |
26 using std::set; | 28 using std::set; |
27 using std::string; | 29 using std::string; |
28 using std::vector; | 30 using std::vector; |
29 using sync_pb::CommitResponse; | |
30 | 31 |
31 namespace syncer { | 32 namespace syncer { |
32 | 33 |
33 using sessions::OrderedCommitSet; | |
34 using sessions::StatusController; | |
35 using sessions::SyncSession; | 34 using sessions::SyncSession; |
36 using syncable::ModelNeutralWriteTransaction; | |
37 using syncable::ModelNeutralMutableEntry; | |
38 using syncable::Entry; | 35 using syncable::Entry; |
39 using syncable::BASE_VERSION; | |
40 using syncable::GET_BY_ID; | |
41 using syncable::GET_BY_HANDLE; | |
42 using syncable::ID; | |
43 using syncable::IS_DEL; | 36 using syncable::IS_DEL; |
44 using syncable::IS_DIR; | |
45 using syncable::IS_UNAPPLIED_UPDATE; | 37 using syncable::IS_UNAPPLIED_UPDATE; |
46 using syncable::IS_UNSYNCED; | 38 using syncable::IS_UNSYNCED; |
47 using syncable::PARENT_ID; | 39 using syncable::Id; |
48 using syncable::SERVER_IS_DEL; | 40 using syncable::SPECIFICS; |
49 using syncable::SERVER_PARENT_ID; | 41 using syncable::UNIQUE_POSITION; |
50 using syncable::SERVER_VERSION; | |
51 using syncable::SYNCER; | |
52 using syncable::SYNCING; | |
53 | 42 |
54 ProcessCommitResponseCommand::ProcessCommitResponseCommand( | 43 namespace commit_util { |
55 const sessions::OrderedCommitSet& commit_set, | 44 |
56 const sync_pb::ClientToServerMessage& commit_message, | 45 void AddExtensionsActivityToMessage( |
57 const sync_pb::ClientToServerResponse& commit_response) | 46 ExtensionsActivity* activity, |
58 : commit_set_(commit_set), | 47 ExtensionsActivity::Records* extensions_activity_buffer, |
59 commit_message_(commit_message), | 48 sync_pb::CommitMessage* message) { |
60 commit_response_(commit_response) { | 49 // This isn't perfect, since the set of extensions activity may not correlate |
| 50 // exactly with the items being committed. That's OK as long as we're looking |
| 51 // for a rough estimate of extensions activity, not an precise mapping of |
| 52 // which commits were triggered by which extension. |
| 53 // |
| 54 // We will push this list of extensions activity back into the |
| 55 // ExtensionsActivityMonitor if this commit fails. That's why we must keep a |
| 56 // copy of these records in the session. |
| 57 activity->GetAndClearRecords(extensions_activity_buffer); |
| 58 |
| 59 const ExtensionsActivity::Records& records = *extensions_activity_buffer; |
| 60 for (ExtensionsActivity::Records::const_iterator it = |
| 61 records.begin(); |
| 62 it != records.end(); ++it) { |
| 63 sync_pb::ChromiumExtensionsActivity* activity_message = |
| 64 message->add_extensions_activity(); |
| 65 activity_message->set_extension_id(it->second.extension_id); |
| 66 activity_message->set_bookmark_writes_since_last_commit( |
| 67 it->second.bookmark_write_count); |
| 68 } |
61 } | 69 } |
62 | 70 |
63 ProcessCommitResponseCommand::~ProcessCommitResponseCommand() {} | 71 void AddClientConfigParamsToMessage( |
| 72 ModelTypeSet enabled_types, |
| 73 sync_pb::CommitMessage* message) { |
| 74 sync_pb::ClientConfigParams* config_params = message->mutable_config_params(); |
| 75 for (ModelTypeSet::Iterator it = enabled_types.First(); it.Good(); it.Inc()) { |
| 76 if (ProxyTypes().Has(it.Get())) |
| 77 continue; |
| 78 int field_number = GetSpecificsFieldNumberFromModelType(it.Get()); |
| 79 config_params->mutable_enabled_type_ids()->Add(field_number); |
| 80 } |
| 81 config_params->set_tabs_datatype_enabled( |
| 82 enabled_types.Has(syncer::PROXY_TABS)); |
| 83 } |
64 | 84 |
65 SyncerError ProcessCommitResponseCommand::ExecuteImpl(SyncSession* session) { | 85 namespace { |
66 syncable::Directory* dir = session->context()->directory(); | 86 void SetEntrySpecifics(const Entry& meta_entry, |
67 StatusController* status = session->mutable_status_controller(); | 87 sync_pb::SyncEntity* sync_entry) { |
68 const CommitResponse& cr = commit_response_.commit(); | 88 // Add the new style extension and the folder bit. |
69 const sync_pb::CommitMessage& commit_message = commit_message_.commit(); | 89 sync_entry->mutable_specifics()->CopyFrom(meta_entry.GetSpecifics()); |
| 90 sync_entry->set_folder(meta_entry.GetIsDir()); |
70 | 91 |
71 int transient_error_commits = 0; | 92 CHECK(!sync_entry->specifics().password().has_client_only_encrypted_data()); |
72 int conflicting_commits = 0; | 93 DCHECK_EQ(meta_entry.GetModelType(), GetModelType(*sync_entry)); |
73 int error_commits = 0; | 94 } |
74 int successes = 0; | 95 } // namespace |
75 | 96 |
76 set<syncable::Id> deleted_folders; | 97 void BuildCommitItem( |
| 98 const syncable::Entry& meta_entry, |
| 99 sync_pb::SyncEntity* sync_entry) { |
| 100 syncable::Id id = meta_entry.GetId(); |
| 101 sync_entry->set_id_string(SyncableIdToProto(id)); |
77 | 102 |
78 { // Scope for ModelNeutralWriteTransaction. | 103 string name = meta_entry.GetNonUniqueName(); |
79 ModelNeutralWriteTransaction trans(FROM_HERE, SYNCER, dir); | 104 CHECK(!name.empty()); // Make sure this isn't an update. |
80 for (size_t i = 0; i < commit_set_.Size(); i++) { | 105 // Note: Truncation is also performed in WriteNode::SetTitle(..). But this |
81 CommitResponse::ResponseType response_type = ProcessSingleCommitResponse( | 106 // call is still necessary to handle any title changes that might originate |
82 &trans, | 107 // elsewhere, or already be persisted in the directory. |
83 cr.entryresponse(i), | 108 TruncateUTF8ToByteSize(name, 255, &name); |
84 commit_message.entries(i), | 109 sync_entry->set_name(name); |
85 commit_set_.GetCommitHandleAt(i), | |
86 &deleted_folders); | |
87 switch (response_type) { | |
88 case CommitResponse::INVALID_MESSAGE: | |
89 ++error_commits; | |
90 break; | |
91 case CommitResponse::CONFLICT: | |
92 ++conflicting_commits; | |
93 status->increment_num_server_conflicts(); | |
94 break; | |
95 case CommitResponse::SUCCESS: | |
96 // TODO(sync): worry about sync_rate_ rate calc? | |
97 ++successes; | |
98 if (commit_set_.GetModelTypeAt(i) == BOOKMARKS) | |
99 status->increment_num_successful_bookmark_commits(); | |
100 status->increment_num_successful_commits(); | |
101 break; | |
102 case CommitResponse::OVER_QUOTA: | |
103 // We handle over quota like a retry, which is same as transient. | |
104 case CommitResponse::RETRY: | |
105 case CommitResponse::TRANSIENT_ERROR: | |
106 ++transient_error_commits; | |
107 break; | |
108 default: | |
109 LOG(FATAL) << "Bad return from ProcessSingleCommitResponse"; | |
110 } | |
111 } | |
112 | 110 |
113 MarkDeletedChildrenSynced(dir, &trans, &deleted_folders); | 111 // Set the non_unique_name. If we do, the server ignores |
| 112 // the |name| value (using |non_unique_name| instead), and will return |
| 113 // in the CommitResponse a unique name if one is generated. |
| 114 // We send both because it may aid in logging. |
| 115 sync_entry->set_non_unique_name(name); |
| 116 |
| 117 if (!meta_entry.GetUniqueClientTag().empty()) { |
| 118 sync_entry->set_client_defined_unique_tag( |
| 119 meta_entry.GetUniqueClientTag()); |
114 } | 120 } |
115 | 121 |
116 int commit_count = static_cast<int>(commit_set_.Size()); | 122 // Deleted items with server-unknown parent ids can be a problem so we set |
117 if (commit_count == successes) { | 123 // the parent to 0. (TODO(sync): Still true in protocol?). |
118 return SYNCER_OK; | 124 Id new_parent_id; |
119 } else if (error_commits > 0) { | 125 if (meta_entry.GetIsDel() && |
120 return SERVER_RETURN_UNKNOWN_ERROR; | 126 !meta_entry.GetParentId().ServerKnows()) { |
121 } else if (transient_error_commits > 0) { | 127 new_parent_id = syncable::BaseTransaction::root_id(); |
122 return SERVER_RETURN_TRANSIENT_ERROR; | |
123 } else if (conflicting_commits > 0) { | |
124 // This means that the server already has an item with this version, but | |
125 // we haven't seen that update yet. | |
126 // | |
127 // A well-behaved client should respond to this by proceeding to the | |
128 // download updates phase, fetching the conflicting items, then attempting | |
129 // to resolve the conflict. That's not what this client does. | |
130 // | |
131 // We don't currently have any code to support that exceptional control | |
132 // flow. Instead, we abort the current sync cycle and start a new one. The | |
133 // end result is the same. | |
134 return SERVER_RETURN_CONFLICT; | |
135 } else { | 128 } else { |
136 LOG(FATAL) << "Inconsistent counts when processing commit response"; | 129 new_parent_id = meta_entry.GetParentId(); |
137 return SYNCER_OK; | 130 } |
| 131 sync_entry->set_parent_id_string(SyncableIdToProto(new_parent_id)); |
| 132 |
| 133 // If our parent has changed, send up the old one so the server |
| 134 // can correctly deal with multiple parents. |
| 135 // TODO(nick): With the server keeping track of the primary sync parent, |
| 136 // it should not be necessary to provide the old_parent_id: the version |
| 137 // number should suffice. |
| 138 if (new_parent_id != meta_entry.GetServerParentId() && |
| 139 0 != meta_entry.GetBaseVersion() && |
| 140 syncable::CHANGES_VERSION != meta_entry.GetBaseVersion()) { |
| 141 sync_entry->set_old_parent_id( |
| 142 SyncableIdToProto(meta_entry.GetServerParentId())); |
| 143 } |
| 144 |
| 145 int64 version = meta_entry.GetBaseVersion(); |
| 146 if (syncable::CHANGES_VERSION == version || 0 == version) { |
| 147 // Undeletions are only supported for items that have a client tag. |
| 148 DCHECK(!id.ServerKnows() || |
| 149 !meta_entry.GetUniqueClientTag().empty()) |
| 150 << meta_entry; |
| 151 |
| 152 // Version 0 means to create or undelete an object. |
| 153 sync_entry->set_version(0); |
| 154 } else { |
| 155 DCHECK(id.ServerKnows()) << meta_entry; |
| 156 sync_entry->set_version(meta_entry.GetBaseVersion()); |
| 157 } |
| 158 sync_entry->set_ctime(TimeToProtoTime(meta_entry.GetCtime())); |
| 159 sync_entry->set_mtime(TimeToProtoTime(meta_entry.GetMtime())); |
| 160 |
| 161 // Deletion is final on the server, let's move things and then delete them. |
| 162 if (meta_entry.GetIsDel()) { |
| 163 sync_entry->set_deleted(true); |
| 164 } else { |
| 165 if (meta_entry.GetSpecifics().has_bookmark()) { |
| 166 // Both insert_after_item_id and position_in_parent fields are set only |
| 167 // for legacy reasons. See comments in sync.proto for more information. |
| 168 const Id& prev_id = meta_entry.GetPredecessorId(); |
| 169 string prev_id_string = |
| 170 prev_id.IsRoot() ? string() : prev_id.GetServerId(); |
| 171 sync_entry->set_insert_after_item_id(prev_id_string); |
| 172 sync_entry->set_position_in_parent( |
| 173 meta_entry.GetUniquePosition().ToInt64()); |
| 174 meta_entry.GetUniquePosition().ToProto( |
| 175 sync_entry->mutable_unique_position()); |
| 176 } |
| 177 SetEntrySpecifics(meta_entry, sync_entry); |
138 } | 178 } |
139 } | 179 } |
140 | 180 |
| 181 |
| 182 // Helpers for ProcessSingleCommitResponse. |
| 183 namespace { |
| 184 |
141 void LogServerError(const sync_pb::CommitResponse_EntryResponse& res) { | 185 void LogServerError(const sync_pb::CommitResponse_EntryResponse& res) { |
142 if (res.has_error_message()) | 186 if (res.has_error_message()) |
143 LOG(WARNING) << " " << res.error_message(); | 187 LOG(WARNING) << " " << res.error_message(); |
144 else | 188 else |
145 LOG(WARNING) << " No detailed error message returned from server"; | 189 LOG(WARNING) << " No detailed error message returned from server"; |
146 } | 190 } |
147 | 191 |
148 CommitResponse::ResponseType | 192 const string& GetResultingPostCommitName( |
149 ProcessCommitResponseCommand::ProcessSingleCommitResponse( | |
150 syncable::ModelNeutralWriteTransaction* trans, | |
151 const sync_pb::CommitResponse_EntryResponse& server_entry, | |
152 const sync_pb::SyncEntity& commit_request_entry, | |
153 const int64 metahandle, | |
154 set<syncable::Id>* deleted_folders) { | |
155 ModelNeutralMutableEntry local_entry(trans, GET_BY_HANDLE, metahandle); | |
156 CHECK(local_entry.good()); | |
157 bool syncing_was_set = local_entry.GetSyncing(); | |
158 local_entry.PutSyncing(false); | |
159 | |
160 CommitResponse::ResponseType response = (CommitResponse::ResponseType) | |
161 server_entry.response_type(); | |
162 if (!CommitResponse::ResponseType_IsValid(response)) { | |
163 LOG(ERROR) << "Commit response has unknown response type! Possibly out " | |
164 "of date client?"; | |
165 return CommitResponse::INVALID_MESSAGE; | |
166 } | |
167 if (CommitResponse::TRANSIENT_ERROR == response) { | |
168 DVLOG(1) << "Transient Error Committing: " << local_entry; | |
169 LogServerError(server_entry); | |
170 return CommitResponse::TRANSIENT_ERROR; | |
171 } | |
172 if (CommitResponse::INVALID_MESSAGE == response) { | |
173 LOG(ERROR) << "Error Commiting: " << local_entry; | |
174 LogServerError(server_entry); | |
175 return response; | |
176 } | |
177 if (CommitResponse::CONFLICT == response) { | |
178 DVLOG(1) << "Conflict Committing: " << local_entry; | |
179 return response; | |
180 } | |
181 if (CommitResponse::RETRY == response) { | |
182 DVLOG(1) << "Retry Committing: " << local_entry; | |
183 return response; | |
184 } | |
185 if (CommitResponse::OVER_QUOTA == response) { | |
186 LOG(WARNING) << "Hit deprecated OVER_QUOTA Committing: " << local_entry; | |
187 return response; | |
188 } | |
189 if (!server_entry.has_id_string()) { | |
190 LOG(ERROR) << "Commit response has no id"; | |
191 return CommitResponse::INVALID_MESSAGE; | |
192 } | |
193 | |
194 // Implied by the IsValid call above, but here for clarity. | |
195 DCHECK_EQ(CommitResponse::SUCCESS, response) << response; | |
196 // Check to see if we've been given the ID of an existing entry. If so treat | |
197 // it as an error response and retry later. | |
198 const syncable::Id& server_entry_id = | |
199 SyncableIdFromProto(server_entry.id_string()); | |
200 if (local_entry.GetId() != server_entry_id) { | |
201 Entry e(trans, GET_BY_ID, server_entry_id); | |
202 if (e.good()) { | |
203 LOG(ERROR) | |
204 << "Got duplicate id when commiting id: " | |
205 << local_entry.GetId() | |
206 << ". Treating as an error return"; | |
207 return CommitResponse::INVALID_MESSAGE; | |
208 } | |
209 } | |
210 | |
211 if (server_entry.version() == 0) { | |
212 LOG(WARNING) << "Server returned a zero version on a commit response."; | |
213 } | |
214 | |
215 ProcessSuccessfulCommitResponse(commit_request_entry, server_entry, | |
216 local_entry.GetId(), &local_entry, syncing_was_set, deleted_folders); | |
217 return response; | |
218 } | |
219 | |
220 const string& ProcessCommitResponseCommand::GetResultingPostCommitName( | |
221 const sync_pb::SyncEntity& committed_entry, | 193 const sync_pb::SyncEntity& committed_entry, |
222 const sync_pb::CommitResponse_EntryResponse& entry_response) { | 194 const sync_pb::CommitResponse_EntryResponse& entry_response) { |
223 const string& response_name = | 195 const string& response_name = |
224 SyncerProtoUtil::NameFromCommitEntryResponse(entry_response); | 196 SyncerProtoUtil::NameFromCommitEntryResponse(entry_response); |
225 if (!response_name.empty()) | 197 if (!response_name.empty()) |
226 return response_name; | 198 return response_name; |
227 return SyncerProtoUtil::NameFromSyncEntity(committed_entry); | 199 return SyncerProtoUtil::NameFromSyncEntity(committed_entry); |
228 } | 200 } |
229 | 201 |
230 bool ProcessCommitResponseCommand::UpdateVersionAfterCommit( | 202 bool UpdateVersionAfterCommit( |
231 const sync_pb::SyncEntity& committed_entry, | 203 const sync_pb::SyncEntity& committed_entry, |
232 const sync_pb::CommitResponse_EntryResponse& entry_response, | 204 const sync_pb::CommitResponse_EntryResponse& entry_response, |
233 const syncable::Id& pre_commit_id, | 205 const syncable::Id& pre_commit_id, |
234 syncable::ModelNeutralMutableEntry* local_entry) { | 206 syncable::ModelNeutralMutableEntry* local_entry) { |
235 int64 old_version = local_entry->GetBaseVersion(); | 207 int64 old_version = local_entry->GetBaseVersion(); |
236 int64 new_version = entry_response.version(); | 208 int64 new_version = entry_response.version(); |
237 bool bad_commit_version = false; | 209 bool bad_commit_version = false; |
238 if (committed_entry.deleted() && | 210 if (committed_entry.deleted() && |
239 !local_entry->GetUniqueClientTag().empty()) { | 211 !local_entry->GetUniqueClientTag().empty()) { |
240 // If the item was deleted, and it's undeletable (uses the client tag), | 212 // If the item was deleted, and it's undeletable (uses the client tag), |
(...skipping 16 matching lines...) Expand all Loading... |
257 // Update the base version and server version. The base version must change | 229 // Update the base version and server version. The base version must change |
258 // here, even if syncing_was_set is false; that's because local changes were | 230 // here, even if syncing_was_set is false; that's because local changes were |
259 // on top of the successfully committed version. | 231 // on top of the successfully committed version. |
260 local_entry->PutBaseVersion(new_version); | 232 local_entry->PutBaseVersion(new_version); |
261 DVLOG(1) << "Commit is changing base version of " << local_entry->GetId() | 233 DVLOG(1) << "Commit is changing base version of " << local_entry->GetId() |
262 << " to: " << new_version; | 234 << " to: " << new_version; |
263 local_entry->PutServerVersion(new_version); | 235 local_entry->PutServerVersion(new_version); |
264 return true; | 236 return true; |
265 } | 237 } |
266 | 238 |
267 bool ProcessCommitResponseCommand::ChangeIdAfterCommit( | 239 bool ChangeIdAfterCommit( |
268 const sync_pb::CommitResponse_EntryResponse& entry_response, | 240 const sync_pb::CommitResponse_EntryResponse& entry_response, |
269 const syncable::Id& pre_commit_id, | 241 const syncable::Id& pre_commit_id, |
270 syncable::ModelNeutralMutableEntry* local_entry) { | 242 syncable::ModelNeutralMutableEntry* local_entry) { |
271 syncable::BaseWriteTransaction* trans = local_entry->base_write_transaction(); | 243 syncable::BaseWriteTransaction* trans = local_entry->base_write_transaction(); |
272 const syncable::Id& entry_response_id = | 244 const syncable::Id& entry_response_id = |
273 SyncableIdFromProto(entry_response.id_string()); | 245 SyncableIdFromProto(entry_response.id_string()); |
274 if (entry_response_id != pre_commit_id) { | 246 if (entry_response_id != pre_commit_id) { |
275 if (pre_commit_id.ServerKnows()) { | 247 if (pre_commit_id.ServerKnows()) { |
276 // The server can sometimes generate a new ID on commit; for example, | 248 // The server can sometimes generate a new ID on commit; for example, |
277 // when committing an undeletion. | 249 // when committing an undeletion. |
278 DVLOG(1) << " ID changed while committing an old entry. " | 250 DVLOG(1) << " ID changed while committing an old entry. " |
279 << pre_commit_id << " became " << entry_response_id << "."; | 251 << pre_commit_id << " became " << entry_response_id << "."; |
280 } | 252 } |
281 ModelNeutralMutableEntry same_id(trans, GET_BY_ID, entry_response_id); | 253 syncable::ModelNeutralMutableEntry same_id( |
| 254 trans, |
| 255 syncable::GET_BY_ID, |
| 256 entry_response_id); |
282 // We should trap this before this function. | 257 // We should trap this before this function. |
283 if (same_id.good()) { | 258 if (same_id.good()) { |
284 LOG(ERROR) << "ID clash with id " << entry_response_id | 259 LOG(ERROR) << "ID clash with id " << entry_response_id |
285 << " during commit " << same_id; | 260 << " during commit " << same_id; |
286 return false; | 261 return false; |
287 } | 262 } |
288 ChangeEntryIDAndUpdateChildren(trans, local_entry, entry_response_id); | 263 ChangeEntryIDAndUpdateChildren(trans, local_entry, entry_response_id); |
289 DVLOG(1) << "Changing ID to " << entry_response_id; | 264 DVLOG(1) << "Changing ID to " << entry_response_id; |
290 } | 265 } |
291 return true; | 266 return true; |
292 } | 267 } |
293 | 268 |
294 void ProcessCommitResponseCommand::UpdateServerFieldsAfterCommit( | 269 void UpdateServerFieldsAfterCommit( |
295 const sync_pb::SyncEntity& committed_entry, | 270 const sync_pb::SyncEntity& committed_entry, |
296 const sync_pb::CommitResponse_EntryResponse& entry_response, | 271 const sync_pb::CommitResponse_EntryResponse& entry_response, |
297 syncable::ModelNeutralMutableEntry* local_entry) { | 272 syncable::ModelNeutralMutableEntry* local_entry) { |
298 | 273 |
299 // We just committed an entry successfully, and now we want to make our view | 274 // We just committed an entry successfully, and now we want to make our view |
300 // of the server state consistent with the server state. We must be careful; | 275 // of the server state consistent with the server state. We must be careful; |
301 // |entry_response| and |committed_entry| have some identically named | 276 // |entry_response| and |committed_entry| have some identically named |
302 // fields. We only want to consider fields from |committed_entry| when there | 277 // fields. We only want to consider fields from |committed_entry| when there |
303 // is not an overriding field in the |entry_response|. We do not want to | 278 // is not an overriding field in the |entry_response|. We do not want to |
304 // update the server data from the local data in the entry -- it's possible | 279 // update the server data from the local data in the entry -- it's possible |
(...skipping 30 matching lines...) Expand all Loading... |
335 GetResultingPostCommitName(committed_entry, entry_response)); | 310 GetResultingPostCommitName(committed_entry, entry_response)); |
336 | 311 |
337 if (local_entry->GetIsUnappliedUpdate()) { | 312 if (local_entry->GetIsUnappliedUpdate()) { |
338 // This shouldn't happen; an unapplied update shouldn't be committed, and | 313 // This shouldn't happen; an unapplied update shouldn't be committed, and |
339 // if it were, the commit should have failed. But if it does happen: we've | 314 // if it were, the commit should have failed. But if it does happen: we've |
340 // just overwritten the update info, so clear the flag. | 315 // just overwritten the update info, so clear the flag. |
341 local_entry->PutIsUnappliedUpdate(false); | 316 local_entry->PutIsUnappliedUpdate(false); |
342 } | 317 } |
343 } | 318 } |
344 | 319 |
345 void ProcessCommitResponseCommand::ProcessSuccessfulCommitResponse( | 320 void ProcessSuccessfulCommitResponse( |
346 const sync_pb::SyncEntity& committed_entry, | 321 const sync_pb::SyncEntity& committed_entry, |
347 const sync_pb::CommitResponse_EntryResponse& entry_response, | 322 const sync_pb::CommitResponse_EntryResponse& entry_response, |
348 const syncable::Id& pre_commit_id, | 323 const syncable::Id& pre_commit_id, |
349 syncable::ModelNeutralMutableEntry* local_entry, | 324 syncable::ModelNeutralMutableEntry* local_entry, |
350 bool syncing_was_set, set<syncable::Id>* deleted_folders) { | 325 bool syncing_was_set, set<syncable::Id>* deleted_folders) { |
351 DCHECK(local_entry->GetIsUnsynced()); | 326 DCHECK(local_entry->GetIsUnsynced()); |
352 | 327 |
353 // Update SERVER_VERSION and BASE_VERSION. | 328 // Update SERVER_VERSION and BASE_VERSION. |
354 if (!UpdateVersionAfterCommit(committed_entry, entry_response, pre_commit_id, | 329 if (!UpdateVersionAfterCommit(committed_entry, entry_response, pre_commit_id, |
355 local_entry)) { | 330 local_entry)) { |
(...skipping 21 matching lines...) Expand all Loading... |
377 // Make a note of any deleted folders, whose children would have | 352 // Make a note of any deleted folders, whose children would have |
378 // been recursively deleted. | 353 // been recursively deleted. |
379 // TODO(nick): Here, commit_message.deleted() would be more correct than | 354 // TODO(nick): Here, commit_message.deleted() would be more correct than |
380 // local_entry->GetIsDel(). For example, an item could be renamed, and then | 355 // local_entry->GetIsDel(). For example, an item could be renamed, and then |
381 // deleted during the commit of the rename. Unit test & fix. | 356 // deleted during the commit of the rename. Unit test & fix. |
382 if (local_entry->GetIsDir() && local_entry->GetIsDel()) { | 357 if (local_entry->GetIsDir() && local_entry->GetIsDel()) { |
383 deleted_folders->insert(local_entry->GetId()); | 358 deleted_folders->insert(local_entry->GetId()); |
384 } | 359 } |
385 } | 360 } |
386 | 361 |
| 362 } // namespace |
| 363 |
| 364 sync_pb::CommitResponse::ResponseType |
| 365 ProcessSingleCommitResponse( |
| 366 syncable::BaseWriteTransaction* trans, |
| 367 const sync_pb::CommitResponse_EntryResponse& server_entry, |
| 368 const sync_pb::SyncEntity& commit_request_entry, |
| 369 int64 metahandle, |
| 370 set<syncable::Id>* deleted_folders) { |
| 371 syncable::ModelNeutralMutableEntry local_entry( |
| 372 trans, |
| 373 syncable::GET_BY_HANDLE, |
| 374 metahandle); |
| 375 CHECK(local_entry.good()); |
| 376 bool syncing_was_set = local_entry.GetSyncing(); |
| 377 local_entry.PutSyncing(false); |
| 378 |
| 379 sync_pb::CommitResponse::ResponseType response = server_entry.response_type(); |
| 380 if (!sync_pb::CommitResponse::ResponseType_IsValid(response)) { |
| 381 LOG(ERROR) << "Commit response has unknown response type! Possibly out " |
| 382 "of date client?"; |
| 383 return sync_pb::CommitResponse::INVALID_MESSAGE; |
| 384 } |
| 385 if (sync_pb::CommitResponse::TRANSIENT_ERROR == response) { |
| 386 DVLOG(1) << "Transient Error Committing: " << local_entry; |
| 387 LogServerError(server_entry); |
| 388 return sync_pb::CommitResponse::TRANSIENT_ERROR; |
| 389 } |
| 390 if (sync_pb::CommitResponse::INVALID_MESSAGE == response) { |
| 391 LOG(ERROR) << "Error Commiting: " << local_entry; |
| 392 LogServerError(server_entry); |
| 393 return response; |
| 394 } |
| 395 if (sync_pb::CommitResponse::CONFLICT == response) { |
| 396 DVLOG(1) << "Conflict Committing: " << local_entry; |
| 397 return response; |
| 398 } |
| 399 if (sync_pb::CommitResponse::RETRY == response) { |
| 400 DVLOG(1) << "Retry Committing: " << local_entry; |
| 401 return response; |
| 402 } |
| 403 if (sync_pb::CommitResponse::OVER_QUOTA == response) { |
| 404 LOG(WARNING) << "Hit deprecated OVER_QUOTA Committing: " << local_entry; |
| 405 return response; |
| 406 } |
| 407 if (!server_entry.has_id_string()) { |
| 408 LOG(ERROR) << "Commit response has no id"; |
| 409 return sync_pb::CommitResponse::INVALID_MESSAGE; |
| 410 } |
| 411 |
| 412 // Implied by the IsValid call above, but here for clarity. |
| 413 DCHECK_EQ(sync_pb::CommitResponse::SUCCESS, response) << response; |
| 414 // Check to see if we've been given the ID of an existing entry. If so treat |
| 415 // it as an error response and retry later. |
| 416 const syncable::Id& server_entry_id = |
| 417 SyncableIdFromProto(server_entry.id_string()); |
| 418 if (local_entry.GetId() != server_entry_id) { |
| 419 Entry e(trans, syncable::GET_BY_ID, server_entry_id); |
| 420 if (e.good()) { |
| 421 LOG(ERROR) |
| 422 << "Got duplicate id when commiting id: " |
| 423 << local_entry.GetId() |
| 424 << ". Treating as an error return"; |
| 425 return sync_pb::CommitResponse::INVALID_MESSAGE; |
| 426 } |
| 427 } |
| 428 |
| 429 if (server_entry.version() == 0) { |
| 430 LOG(WARNING) << "Server returned a zero version on a commit response."; |
| 431 } |
| 432 |
| 433 ProcessSuccessfulCommitResponse(commit_request_entry, server_entry, |
| 434 local_entry.GetId(), &local_entry, syncing_was_set, deleted_folders); |
| 435 return response; |
| 436 } |
| 437 |
| 438 } // namespace commit_util |
| 439 |
387 } // namespace syncer | 440 } // namespace syncer |
OLD | NEW |