OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/sync/engine/syncer.h" | |
6 | |
7 #include "base/debug/trace_event.h" | |
8 #include "base/location.h" | |
9 #include "base/logging.h" | |
10 #include "base/message_loop.h" | |
11 #include "base/time.h" | |
12 #include "build/build_config.h" | |
13 #include "chrome/browser/sync/engine/apply_updates_command.h" | |
14 #include "chrome/browser/sync/engine/build_commit_command.h" | |
15 #include "chrome/browser/sync/engine/cleanup_disabled_types_command.h" | |
16 #include "chrome/browser/sync/engine/clear_data_command.h" | |
17 #include "chrome/browser/sync/engine/conflict_resolver.h" | |
18 #include "chrome/browser/sync/engine/download_updates_command.h" | |
19 #include "chrome/browser/sync/engine/get_commit_ids_command.h" | |
20 #include "chrome/browser/sync/engine/net/server_connection_manager.h" | |
21 #include "chrome/browser/sync/engine/post_commit_message_command.h" | |
22 #include "chrome/browser/sync/engine/process_commit_response_command.h" | |
23 #include "chrome/browser/sync/engine/process_updates_command.h" | |
24 #include "chrome/browser/sync/engine/resolve_conflicts_command.h" | |
25 #include "chrome/browser/sync/engine/store_timestamps_command.h" | |
26 #include "chrome/browser/sync/engine/syncer_types.h" | |
27 #include "chrome/browser/sync/engine/syncproto.h" | |
28 #include "chrome/browser/sync/engine/verify_updates_command.h" | |
29 #include "chrome/browser/sync/syncable/syncable-inl.h" | |
30 #include "chrome/browser/sync/syncable/syncable.h" | |
31 | |
32 using base::Time; | |
33 using base::TimeDelta; | |
34 using sync_pb::ClientCommand; | |
35 using syncable::Blob; | |
36 using syncable::IS_UNAPPLIED_UPDATE; | |
37 using syncable::SERVER_CTIME; | |
38 using syncable::SERVER_IS_DEL; | |
39 using syncable::SERVER_IS_DIR; | |
40 using syncable::SERVER_MTIME; | |
41 using syncable::SERVER_NON_UNIQUE_NAME; | |
42 using syncable::SERVER_PARENT_ID; | |
43 using syncable::SERVER_POSITION_IN_PARENT; | |
44 using syncable::SERVER_SPECIFICS; | |
45 using syncable::SERVER_VERSION; | |
46 using syncable::SYNCER; | |
47 using syncable::WriteTransaction; | |
48 | |
49 namespace browser_sync { | |
50 | |
51 using sessions::ScopedSessionContextConflictResolver; | |
52 using sessions::StatusController; | |
53 using sessions::SyncSession; | |
54 using sessions::ConflictProgress; | |
55 | |
56 #define ENUM_CASE(x) case x: return #x | |
57 const char* SyncerStepToString(const SyncerStep step) | |
58 { | |
59 switch (step) { | |
60 ENUM_CASE(SYNCER_BEGIN); | |
61 ENUM_CASE(CLEANUP_DISABLED_TYPES); | |
62 ENUM_CASE(DOWNLOAD_UPDATES); | |
63 ENUM_CASE(PROCESS_CLIENT_COMMAND); | |
64 ENUM_CASE(VERIFY_UPDATES); | |
65 ENUM_CASE(PROCESS_UPDATES); | |
66 ENUM_CASE(STORE_TIMESTAMPS); | |
67 ENUM_CASE(APPLY_UPDATES); | |
68 ENUM_CASE(BUILD_COMMIT_REQUEST); | |
69 ENUM_CASE(POST_COMMIT_MESSAGE); | |
70 ENUM_CASE(PROCESS_COMMIT_RESPONSE); | |
71 ENUM_CASE(RESOLVE_CONFLICTS); | |
72 ENUM_CASE(APPLY_UPDATES_TO_RESOLVE_CONFLICTS); | |
73 ENUM_CASE(CLEAR_PRIVATE_DATA); | |
74 ENUM_CASE(SYNCER_END); | |
75 } | |
76 NOTREACHED(); | |
77 return ""; | |
78 } | |
79 #undef ENUM_CASE | |
80 | |
81 Syncer::Syncer() | |
82 : early_exit_requested_(false) { | |
83 } | |
84 | |
85 Syncer::~Syncer() {} | |
86 | |
87 bool Syncer::ExitRequested() { | |
88 base::AutoLock lock(early_exit_requested_lock_); | |
89 return early_exit_requested_; | |
90 } | |
91 | |
92 void Syncer::RequestEarlyExit() { | |
93 base::AutoLock lock(early_exit_requested_lock_); | |
94 early_exit_requested_ = true; | |
95 } | |
96 | |
97 void Syncer::SyncShare(sessions::SyncSession* session, | |
98 SyncerStep first_step, | |
99 SyncerStep last_step) { | |
100 ScopedSessionContextConflictResolver scoped(session->context(), | |
101 &resolver_); | |
102 session->mutable_status_controller()->UpdateStartTime(); | |
103 SyncerStep current_step = first_step; | |
104 | |
105 SyncerStep next_step = current_step; | |
106 while (!ExitRequested()) { | |
107 TRACE_EVENT1("sync", "SyncerStateMachine", | |
108 "state", SyncerStepToString(current_step)); | |
109 DVLOG(1) << "Syncer step:" << SyncerStepToString(current_step); | |
110 | |
111 switch (current_step) { | |
112 case SYNCER_BEGIN: | |
113 // This isn't perfect, as we can end up bundling extensions activity | |
114 // intended for the next session into the current one. We could do a | |
115 // test-and-reset as with the source, but note that also falls short if | |
116 // the commit request fails (e.g. due to lost connection), as we will | |
117 // fall all the way back to the syncer thread main loop in that case, | |
118 // creating a new session when a connection is established, losing the | |
119 // records set here on the original attempt. This should provide us | |
120 // with the right data "most of the time", and we're only using this | |
121 // for analysis purposes, so Law of Large Numbers FTW. | |
122 session->context()->extensions_monitor()->GetAndClearRecords( | |
123 session->mutable_extensions_activity()); | |
124 session->context()->PruneUnthrottledTypes(base::TimeTicks::Now()); | |
125 session->SendEventNotification(SyncEngineEvent::SYNC_CYCLE_BEGIN); | |
126 | |
127 next_step = CLEANUP_DISABLED_TYPES; | |
128 break; | |
129 case CLEANUP_DISABLED_TYPES: { | |
130 CleanupDisabledTypesCommand cleanup; | |
131 cleanup.Execute(session); | |
132 next_step = DOWNLOAD_UPDATES; | |
133 break; | |
134 } | |
135 case DOWNLOAD_UPDATES: { | |
136 // TODO(akalin): We may want to propagate this switch up | |
137 // eventually. | |
138 #if defined(OS_ANDROID) | |
139 const bool kCreateMobileBookmarksFolder = true; | |
140 #else | |
141 const bool kCreateMobileBookmarksFolder = false; | |
142 #endif | |
143 DownloadUpdatesCommand download_updates(kCreateMobileBookmarksFolder); | |
144 session->mutable_status_controller()->set_last_download_updates_result( | |
145 download_updates.Execute(session)); | |
146 next_step = PROCESS_CLIENT_COMMAND; | |
147 break; | |
148 } | |
149 case PROCESS_CLIENT_COMMAND: { | |
150 ProcessClientCommand(session); | |
151 next_step = VERIFY_UPDATES; | |
152 break; | |
153 } | |
154 case VERIFY_UPDATES: { | |
155 VerifyUpdatesCommand verify_updates; | |
156 verify_updates.Execute(session); | |
157 next_step = PROCESS_UPDATES; | |
158 break; | |
159 } | |
160 case PROCESS_UPDATES: { | |
161 ProcessUpdatesCommand process_updates; | |
162 process_updates.Execute(session); | |
163 next_step = STORE_TIMESTAMPS; | |
164 break; | |
165 } | |
166 case STORE_TIMESTAMPS: { | |
167 StoreTimestampsCommand store_timestamps; | |
168 store_timestamps.Execute(session); | |
169 // We should download all of the updates before attempting to process | |
170 // them. | |
171 if (session->status_controller().ServerSaysNothingMoreToDownload() || | |
172 !session->status_controller().download_updates_succeeded()) { | |
173 next_step = APPLY_UPDATES; | |
174 } else { | |
175 next_step = DOWNLOAD_UPDATES; | |
176 } | |
177 break; | |
178 } | |
179 case APPLY_UPDATES: { | |
180 ApplyUpdatesCommand apply_updates; | |
181 apply_updates.Execute(session); | |
182 if (last_step == APPLY_UPDATES) { | |
183 // We're in configuration mode, but we still need to run the | |
184 // SYNCER_END step. | |
185 last_step = SYNCER_END; | |
186 next_step = SYNCER_END; | |
187 } else { | |
188 next_step = BUILD_COMMIT_REQUEST; | |
189 } | |
190 break; | |
191 } | |
192 // These two steps are combined since they are executed within the same | |
193 // write transaction. | |
194 case BUILD_COMMIT_REQUEST: { | |
195 syncable::Directory* dir = session->context()->directory(); | |
196 WriteTransaction trans(FROM_HERE, SYNCER, dir); | |
197 sessions::ScopedSetSessionWriteTransaction set_trans(session, &trans); | |
198 | |
199 DVLOG(1) << "Getting the Commit IDs"; | |
200 GetCommitIdsCommand get_commit_ids_command( | |
201 session->context()->max_commit_batch_size()); | |
202 get_commit_ids_command.Execute(session); | |
203 | |
204 if (!session->status_controller().commit_ids().empty()) { | |
205 DVLOG(1) << "Building a commit message"; | |
206 BuildCommitCommand build_commit_command; | |
207 build_commit_command.Execute(session); | |
208 | |
209 next_step = POST_COMMIT_MESSAGE; | |
210 } else { | |
211 next_step = RESOLVE_CONFLICTS; | |
212 } | |
213 | |
214 break; | |
215 } | |
216 case POST_COMMIT_MESSAGE: { | |
217 PostCommitMessageCommand post_commit_command; | |
218 session->mutable_status_controller()->set_last_post_commit_result( | |
219 post_commit_command.Execute(session)); | |
220 next_step = PROCESS_COMMIT_RESPONSE; | |
221 break; | |
222 } | |
223 case PROCESS_COMMIT_RESPONSE: { | |
224 ProcessCommitResponseCommand process_response_command; | |
225 session->mutable_status_controller()-> | |
226 set_last_process_commit_response_result( | |
227 process_response_command.Execute(session)); | |
228 next_step = RESOLVE_CONFLICTS; | |
229 break; | |
230 } | |
231 case RESOLVE_CONFLICTS: { | |
232 StatusController* status = session->mutable_status_controller(); | |
233 status->reset_conflicts_resolved(); | |
234 ResolveConflictsCommand resolve_conflicts_command; | |
235 resolve_conflicts_command.Execute(session); | |
236 | |
237 // Has ConflictingUpdates includes both resolvable and unresolvable | |
238 // conflicts. If we have either, we want to attempt to reapply. | |
239 if (status->HasConflictingUpdates()) | |
240 next_step = APPLY_UPDATES_TO_RESOLVE_CONFLICTS; | |
241 else | |
242 next_step = SYNCER_END; | |
243 break; | |
244 } | |
245 case APPLY_UPDATES_TO_RESOLVE_CONFLICTS: { | |
246 StatusController* status = session->mutable_status_controller(); | |
247 DVLOG(1) << "Applying updates to resolve conflicts"; | |
248 ApplyUpdatesCommand apply_updates; | |
249 | |
250 // We only care to resolve conflicts again if we made progress on the | |
251 // simple conflicts. | |
252 int before_blocking_conflicting_updates = | |
253 status->TotalNumSimpleConflictingItems(); | |
254 apply_updates.Execute(session); | |
255 int after_blocking_conflicting_updates = | |
256 status->TotalNumSimpleConflictingItems(); | |
257 // If the following call sets the conflicts_resolved value to true, | |
258 // SyncSession::HasMoreToSync() will send us into another sync cycle | |
259 // after this one completes. | |
260 // | |
261 // TODO(rlarocque, 109072): Make conflict resolution not require | |
262 // extra sync cycles/GetUpdates. | |
263 status->update_conflicts_resolved(before_blocking_conflicting_updates > | |
264 after_blocking_conflicting_updates); | |
265 next_step = SYNCER_END; | |
266 break; | |
267 } | |
268 case CLEAR_PRIVATE_DATA: { | |
269 ClearDataCommand clear_data_command; | |
270 clear_data_command.Execute(session); | |
271 next_step = SYNCER_END; | |
272 break; | |
273 } | |
274 case SYNCER_END: { | |
275 session->SendEventNotification(SyncEngineEvent::SYNC_CYCLE_ENDED); | |
276 next_step = SYNCER_END; | |
277 break; | |
278 } | |
279 default: | |
280 LOG(ERROR) << "Unknown command: " << current_step; | |
281 } | |
282 DVLOG(2) << "last step: " << SyncerStepToString(last_step) << ", " | |
283 << "current step: " << SyncerStepToString(current_step) << ", " | |
284 << "next step: " << SyncerStepToString(next_step) << ", " | |
285 << "snapshot: " << session->TakeSnapshot().ToString(); | |
286 if (last_step == current_step) | |
287 break; | |
288 current_step = next_step; | |
289 } | |
290 } | |
291 | |
292 void Syncer::ProcessClientCommand(sessions::SyncSession* session) { | |
293 const ClientToServerResponse& response = | |
294 session->status_controller().updates_response(); | |
295 if (!response.has_client_command()) | |
296 return; | |
297 const ClientCommand& command = response.client_command(); | |
298 | |
299 // The server limits the number of items a client can commit in one batch. | |
300 if (command.has_max_commit_batch_size()) { | |
301 session->context()->set_max_commit_batch_size( | |
302 command.max_commit_batch_size()); | |
303 } | |
304 if (command.has_set_sync_long_poll_interval()) { | |
305 session->delegate()->OnReceivedLongPollIntervalUpdate( | |
306 TimeDelta::FromSeconds(command.set_sync_long_poll_interval())); | |
307 } | |
308 if (command.has_set_sync_poll_interval()) { | |
309 session->delegate()->OnReceivedShortPollIntervalUpdate( | |
310 TimeDelta::FromSeconds(command.set_sync_poll_interval())); | |
311 } | |
312 | |
313 if (command.has_sessions_commit_delay_seconds()) { | |
314 session->delegate()->OnReceivedSessionsCommitDelay( | |
315 TimeDelta::FromSeconds(command.sessions_commit_delay_seconds())); | |
316 } | |
317 } | |
318 | |
319 void CopyServerFields(syncable::Entry* src, syncable::MutableEntry* dest) { | |
320 dest->Put(SERVER_NON_UNIQUE_NAME, src->Get(SERVER_NON_UNIQUE_NAME)); | |
321 dest->Put(SERVER_PARENT_ID, src->Get(SERVER_PARENT_ID)); | |
322 dest->Put(SERVER_MTIME, src->Get(SERVER_MTIME)); | |
323 dest->Put(SERVER_CTIME, src->Get(SERVER_CTIME)); | |
324 dest->Put(SERVER_VERSION, src->Get(SERVER_VERSION)); | |
325 dest->Put(SERVER_IS_DIR, src->Get(SERVER_IS_DIR)); | |
326 dest->Put(SERVER_IS_DEL, src->Get(SERVER_IS_DEL)); | |
327 dest->Put(IS_UNAPPLIED_UPDATE, src->Get(IS_UNAPPLIED_UPDATE)); | |
328 dest->Put(SERVER_SPECIFICS, src->Get(SERVER_SPECIFICS)); | |
329 dest->Put(SERVER_POSITION_IN_PARENT, src->Get(SERVER_POSITION_IN_PARENT)); | |
330 } | |
331 | |
332 void ClearServerData(syncable::MutableEntry* entry) { | |
333 entry->Put(SERVER_NON_UNIQUE_NAME, ""); | |
334 entry->Put(SERVER_PARENT_ID, syncable::GetNullId()); | |
335 entry->Put(SERVER_MTIME, Time()); | |
336 entry->Put(SERVER_CTIME, Time()); | |
337 entry->Put(SERVER_VERSION, 0); | |
338 entry->Put(SERVER_IS_DIR, false); | |
339 entry->Put(SERVER_IS_DEL, false); | |
340 entry->Put(IS_UNAPPLIED_UPDATE, false); | |
341 entry->Put(SERVER_SPECIFICS, sync_pb::EntitySpecifics::default_instance()); | |
342 entry->Put(SERVER_POSITION_IN_PARENT, 0); | |
343 } | |
344 | |
345 } // namespace browser_sync | |
OLD | NEW |