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/commit.h" | 5 #include "sync/engine/commit.h" |
6 | 6 |
7 #include "base/debug/trace_event.h" | 7 #include "base/debug/trace_event.h" |
8 #include "sync/engine/build_commit_command.h" | 8 #include "sync/engine/build_commit_command.h" |
9 #include "sync/engine/get_commit_ids.h" | |
10 #include "sync/engine/process_commit_response_command.h" | 9 #include "sync/engine/process_commit_response_command.h" |
10 #include "sync/engine/sync_directory_commit_contribution.h" | |
11 #include "sync/engine/syncer.h" | 11 #include "sync/engine/syncer.h" |
12 #include "sync/engine/syncer_proto_util.h" | 12 #include "sync/engine/syncer_proto_util.h" |
13 #include "sync/sessions/sync_session.h" | 13 #include "sync/sessions/sync_session.h" |
14 #include "sync/syncable/mutable_entry.h" | 14 #include "sync/syncable/directory.h" |
15 #include "sync/syncable/syncable_model_neutral_write_transaction.h" | |
16 | 15 |
17 namespace syncer { | 16 namespace syncer { |
18 | 17 |
19 using sessions::SyncSession; | 18 SyncerError BuildAndPostCommits(ModelTypeSet requested_types, |
20 using sessions::StatusController; | 19 Syncer* syncer, |
21 using syncable::SYNCER; | 20 sessions::SyncSession* session) { |
22 using syncable::ModelNeutralWriteTransaction; | |
23 | |
24 namespace { | |
25 | |
26 // Sets the SYNCING bits of all items in the commit set to value_to_set. | |
27 void SetAllSyncingBitsToValue(ModelNeutralWriteTransaction* trans, | |
28 const sessions::OrderedCommitSet& commit_set, | |
29 bool value_to_set) { | |
30 const std::vector<int64>& commit_handles = commit_set.GetAllCommitHandles(); | |
31 for (std::vector<int64>::const_iterator it = commit_handles.begin(); | |
32 it != commit_handles.end(); ++it) { | |
33 syncable::ModelNeutralMutableEntry entry( | |
34 trans, syncable::GET_BY_HANDLE, *it); | |
35 if (entry.good()) { | |
36 entry.PutSyncing(value_to_set); | |
37 } | |
38 } | |
39 } | |
40 | |
41 // Sets the SYNCING bits for all items in the OrderedCommitSet. | |
42 void SetSyncingBits(ModelNeutralWriteTransaction* trans, | |
43 const sessions::OrderedCommitSet& commit_set) { | |
44 SetAllSyncingBitsToValue(trans, commit_set, true); | |
45 } | |
46 | |
47 // Clears the SYNCING bits for all items in the OrderedCommitSet. | |
48 void ClearSyncingBits(syncable::Directory* dir, | |
49 const sessions::OrderedCommitSet& commit_set) { | |
50 ModelNeutralWriteTransaction trans(FROM_HERE, SYNCER, dir); | |
51 SetAllSyncingBitsToValue(&trans, commit_set, false); | |
52 } | |
53 | |
54 // Helper function that finds sync items that are ready to be committed to the | |
55 // server and serializes them into a commit message protobuf. It will return | |
56 // false iff there are no entries ready to be committed at this time. | |
57 // | |
58 // The OrderedCommitSet parameter is an output parameter which will contain | |
59 // the set of all items which are to be committed. The number of items in | |
60 // the set shall not exceed the maximum batch size. (The default batch size | |
61 // is currently 25, though it can be overwritten by the server.) | |
62 // | |
63 // The ClientToServerMessage parameter is an output parameter which will contain | |
64 // the commit message which should be sent to the server. It is valid iff the | |
65 // return value of this function is true. | |
66 bool PrepareCommitMessage( | |
67 sessions::SyncSession* session, | |
68 ModelTypeSet requested_types, | |
69 sessions::OrderedCommitSet* commit_set, | |
70 sync_pb::ClientToServerMessage* commit_message, | |
71 ExtensionsActivity::Records* extensions_activity_buffer) { | |
72 TRACE_EVENT0("sync", "PrepareCommitMessage"); | |
73 | |
74 commit_set->Clear(); | |
75 commit_message->Clear(); | |
76 | |
77 ModelNeutralWriteTransaction trans( | |
78 FROM_HERE, SYNCER, session->context()->directory()); | |
79 | |
80 // Fetch the items to commit. | |
81 const size_t batch_size = session->context()->max_commit_batch_size(); | |
82 GetCommitIds(&trans, requested_types, batch_size, commit_set); | |
83 | |
84 DVLOG(1) << "Commit message will contain " << commit_set->Size() << " items."; | |
85 if (commit_set->Empty()) { | |
86 return false; | |
87 } | |
88 | |
89 // Serialize the message. | |
90 BuildCommitCommand build_commit_command(&trans, | |
91 *commit_set, | |
92 commit_message, | |
93 extensions_activity_buffer); | |
94 build_commit_command.Execute(session); | |
95 | |
96 SetSyncingBits(&trans, *commit_set); | |
97 return true; | |
98 } | |
99 | |
100 SyncerError BuildAndPostCommitsImpl(ModelTypeSet requested_types, | |
101 Syncer* syncer, | |
102 sessions::SyncSession* session, | |
103 sessions::OrderedCommitSet* commit_set) { | |
104 ModelTypeSet commit_request_types; | |
105 while (!syncer->ExitRequested()) { | 21 while (!syncer->ExitRequested()) { |
106 sync_pb::ClientToServerMessage commit_message; | 22 scoped_ptr<Commit> commit( |
107 ExtensionsActivity::Records extensions_activity_buffer; | 23 Commit::Init( |
108 | 24 requested_types, |
109 if (!PrepareCommitMessage(session, | 25 session->context()->commit_contributor_map(), |
110 requested_types, | 26 session->context()->max_commit_batch_size(), |
111 commit_set, | 27 session->context()->account_name(), |
112 &commit_message, | 28 session->context()->directory()->cache_guid(), |
113 &extensions_activity_buffer)) { | 29 session->context()->extensions_activity())); |
30 if (!commit) { | |
114 break; | 31 break; |
115 } | 32 } |
116 | 33 |
117 commit_request_types.PutAll(commit_set->Types()); | 34 SyncerError error = commit->PostAndProcessResponse( |
118 session->mutable_status_controller()->set_commit_request_types( | 35 session, |
119 commit_request_types); | 36 session->mutable_status_controller(), |
120 | 37 session->context()->extensions_activity()); |
121 sync_pb::ClientToServerResponse commit_response; | 38 if (error != SYNCER_OK) { |
122 | 39 return error; |
123 DVLOG(1) << "Sending commit message."; | |
124 TRACE_EVENT_BEGIN0("sync", "PostCommit"); | |
125 const SyncerError post_result = SyncerProtoUtil::PostClientToServerMessage( | |
126 &commit_message, &commit_response, session); | |
127 TRACE_EVENT_END0("sync", "PostCommit"); | |
128 | |
129 // TODO(rlarocque): Put all the post-commit logic in one place. | |
130 // See crbug.com/196338. | |
131 | |
132 if (post_result != SYNCER_OK) { | |
133 LOG(WARNING) << "Post commit failed"; | |
134 return post_result; | |
135 } | 40 } |
136 | |
137 if (!commit_response.has_commit()) { | |
138 LOG(WARNING) << "Commit response has no commit body!"; | |
139 return SERVER_RESPONSE_VALIDATION_FAILED; | |
140 } | |
141 | |
142 const size_t num_responses = commit_response.commit().entryresponse_size(); | |
143 if (num_responses != commit_set->Size()) { | |
144 LOG(ERROR) | |
145 << "Commit response has wrong number of entries! " | |
146 << "Expected: " << commit_set->Size() << ", " | |
147 << "Got: " << num_responses; | |
148 return SERVER_RESPONSE_VALIDATION_FAILED; | |
149 } | |
150 | |
151 TRACE_EVENT_BEGIN0("sync", "ProcessCommitResponse"); | |
152 ProcessCommitResponseCommand process_response_command( | |
153 *commit_set, commit_message, commit_response); | |
154 const SyncerError processing_result = | |
155 process_response_command.Execute(session); | |
156 TRACE_EVENT_END0("sync", "ProcessCommitResponse"); | |
157 | |
158 // If the commit failed, return the data to the ExtensionsActivityMonitor. | |
159 if (session->status_controller(). | |
160 model_neutral_state().num_successful_bookmark_commits == 0) { | |
161 ExtensionsActivity* extensions_activity = | |
162 session->context()->extensions_activity(); | |
163 extensions_activity->PutRecords(extensions_activity_buffer); | |
164 } | |
165 | |
166 if (processing_result != SYNCER_OK) { | |
167 return processing_result; | |
168 } | |
169 session->SendEventNotification(SyncEngineEvent::STATUS_CHANGED); | |
170 } | 41 } |
171 | 42 |
172 return SYNCER_OK; | 43 return SYNCER_OK; |
173 } | 44 } |
174 | 45 |
175 } // namespace | 46 Commit* Commit::Init( |
47 ModelTypeSet requested_types, | |
48 CommitContributorMap* contributor_map, | |
49 size_t max_entries, | |
50 const std::string& account_name, | |
51 const std::string& cache_guid, | |
52 ExtensionsActivity* extensions_activity) { | |
53 // Gather per-type contributions. | |
54 ContributionMap contributions; | |
55 size_t num_entries = 0; | |
56 for (ModelTypeSet::Iterator it = requested_types.First(); | |
Nicolas Zea
2013/10/09 00:17:39
worth DCHECKING that requested_types.size() == con
rlarocque
2013/10/09 20:00:19
That's not necessarily true. I believe throttling
| |
57 it.Good(); it.Inc()) { | |
58 CommitContributorMap::iterator cm_it = contributor_map->find(it.Get()); | |
59 if (cm_it == contributor_map->end()) { | |
60 NOTREACHED() | |
61 << "Could not find requested type " << ModelTypeToString(it.Get()) | |
62 << " in contributor map."; | |
63 continue; | |
64 } | |
65 size_t spaces_remaining = max_entries - num_entries; | |
66 SyncDirectoryCommitContribution* contribution = | |
67 cm_it->second->GetContribution(spaces_remaining); | |
68 if (contribution) { | |
69 num_entries += contribution->GetNumEntries(); | |
70 contributions.insert(std::make_pair(it.Get(), contribution)); | |
Nicolas Zea
2013/10/09 00:17:39
break if num_entries == max_entries?
rlarocque
2013/10/09 20:00:19
Done.
| |
71 } | |
72 } | |
176 | 73 |
74 // Give up if no one had anything to commit. | |
75 if (contributions.empty()) | |
76 return NULL; | |
177 | 77 |
178 SyncerError BuildAndPostCommits(ModelTypeSet requested_types, | 78 sync_pb::ClientToServerMessage message; |
179 Syncer* syncer, | 79 message.set_message_contents(sync_pb::ClientToServerMessage::COMMIT); |
180 sessions::SyncSession* session) { | 80 message.set_share(account_name); |
181 sessions::OrderedCommitSet commit_set; | 81 |
182 SyncerError result = | 82 sync_pb::CommitMessage* commit_message = message.mutable_commit(); |
183 BuildAndPostCommitsImpl(requested_types, syncer, session, &commit_set); | 83 commit_message->set_cache_guid(cache_guid); |
184 if (result != SYNCER_OK) { | 84 |
185 ClearSyncingBits(session->context()->directory(), commit_set); | 85 // Set extensions activity if bookmark commits are present. |
86 ExtensionsActivity::Records extensions_activity_buffer; | |
87 ContributionMap::iterator it = contributions.find(syncer::BOOKMARKS); | |
88 if (it != contributions.end() && it->second->GetNumEntries() != 0) { | |
89 BuildCommitCommand::AddExtensionsActivityToMessage( | |
90 extensions_activity, | |
91 &extensions_activity_buffer, | |
92 commit_message); | |
186 } | 93 } |
187 return result; | 94 |
95 // Set the client config params. | |
96 ModelTypeSet enabled_types; | |
97 for (CommitContributorMap::iterator it = contributor_map->begin(); | |
98 it != contributor_map->end(); ++it) { | |
99 enabled_types.Put(it->first); | |
Nicolas Zea
2013/10/09 00:17:39
just use requested types?
rlarocque
2013/10/09 20:00:19
Maybe. Should we list throttled types among the l
| |
100 } | |
101 BuildCommitCommand::AddClientConfigParamsToMessage(enabled_types, | |
102 commit_message); | |
103 | |
104 // Finally, serialize all our contributions. | |
105 for (std::map<ModelType, SyncDirectoryCommitContribution*>::iterator it = | |
106 contributions.begin(); it != contributions.end(); ++it) { | |
Nicolas Zea
2013/10/09 00:17:39
indent four more spaces
rlarocque
2013/10/09 20:00:19
Done.
| |
107 it->second->AddToCommitMessage(&message); | |
108 } | |
109 | |
110 // If we made it this far, then we've successfully prepared a commit message. | |
111 return new Commit(contributions, message, extensions_activity_buffer); | |
112 } | |
113 | |
114 Commit::Commit(std::map<ModelType, | |
115 SyncDirectoryCommitContribution*> contributions, | |
116 sync_pb::ClientToServerMessage message, | |
117 ExtensionsActivity::Records extensions_activity_buffer) | |
118 : contributions_(contributions), | |
119 deleter_(&contributions_), | |
120 message_(message), | |
121 extensions_activity_buffer_(extensions_activity_buffer) { | |
122 } | |
123 | |
124 Commit::~Commit() { | |
125 } | |
126 | |
127 SyncerError Commit::PostAndProcessResponse( | |
128 sessions::SyncSession* session, | |
129 sessions::StatusController* status, | |
130 ExtensionsActivity* extensions_activity) { | |
131 ModelTypeSet request_types; | |
132 for (ContributionMap::const_iterator it = contributions_.begin(); | |
133 it != contributions_.end(); ++it) { | |
134 request_types.Put(it->first); | |
Nicolas Zea
2013/10/09 00:17:39
possibly extract this logic into a helper function
rlarocque
2013/10/09 20:00:19
ContributionMap is internal to Commit, and this se
| |
135 } | |
136 session->mutable_status_controller()->set_commit_request_types(request_types); | |
137 | |
138 DVLOG(1) << "Sending commit message."; | |
139 TRACE_EVENT_BEGIN0("sync", "PostCommit"); | |
140 const SyncerError post_result = SyncerProtoUtil::PostClientToServerMessage( | |
141 &message_, &response_, session); | |
142 TRACE_EVENT_END0("sync", "PostCommit"); | |
143 | |
144 if (post_result != SYNCER_OK) { | |
145 LOG(WARNING) << "Post commit failed"; | |
146 return post_result; | |
147 } | |
148 | |
149 if (!response_.has_commit()) { | |
150 LOG(WARNING) << "Commit response has no commit body!"; | |
151 return SERVER_RESPONSE_VALIDATION_FAILED; | |
152 } | |
153 | |
154 size_t message_entries = message_.commit().entries_size(); | |
155 size_t response_entries = response_.commit().entryresponse_size(); | |
156 if (message_entries != response_entries) { | |
157 LOG(ERROR) | |
158 << "Commit response has wrong number of entries! " | |
159 << "Expected: " << message_entries << ", " | |
160 << "Got: " << response_entries; | |
161 return SERVER_RESPONSE_VALIDATION_FAILED; | |
162 } | |
163 | |
164 // Let the contributors process the responses to each of their requests. | |
165 SyncerError processing_result = SYNCER_OK; | |
166 for (std::map<ModelType, SyncDirectoryCommitContribution*>::iterator it = | |
167 contributions_.begin(); it != contributions_.end(); ++it) { | |
168 TRACE_EVENT1("sync", "ProcessCommitResponse", | |
169 "type", ModelTypeToString(it->first)); | |
170 SyncerError type_result = | |
171 it->second->ProcessCommitResponse(response_, status); | |
172 if (processing_result == SYNCER_OK && type_result != SYNCER_OK) { | |
173 processing_result = type_result; | |
174 } | |
175 } | |
176 | |
177 // Handle bookmarks' special extensions activity stats. | |
178 if (session->status_controller(). | |
179 model_neutral_state().num_successful_bookmark_commits == 0) { | |
Nicolas Zea
2013/10/09 00:17:39
indent by four more?
rlarocque
2013/10/09 20:00:19
Done.
| |
180 extensions_activity->PutRecords(extensions_activity_buffer_); | |
181 } | |
182 | |
183 return processing_result; | |
188 } | 184 } |
189 | 185 |
190 } // namespace syncer | 186 } // namespace syncer |
OLD | NEW |