OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 "chrome/browser/sync_file_system/drive_backend/conflict_resolver.h" | 5 #include "chrome/browser/sync_file_system/drive_backend/conflict_resolver.h" |
6 | 6 |
7 #include "base/callback.h" | 7 #include "base/callback.h" |
8 #include "base/format_macros.h" | 8 #include "base/format_macros.h" |
9 #include "base/location.h" | 9 #include "base/location.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/strings/stringprintf.h" |
11 #include "chrome/browser/drive/drive_api_util.h" | 12 #include "chrome/browser/drive/drive_api_util.h" |
12 #include "chrome/browser/drive/drive_service_interface.h" | 13 #include "chrome/browser/drive/drive_service_interface.h" |
13 #include "chrome/browser/drive/drive_uploader.h" | 14 #include "chrome/browser/drive/drive_uploader.h" |
14 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h" | 15 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h" |
15 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h" | 16 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h" |
16 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h" | 17 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h" |
17 #include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h" | 18 #include "chrome/browser/sync_file_system/drive_backend/sync_engine_context.h" |
| 19 #include "chrome/browser/sync_file_system/drive_backend/sync_task_manager.h" |
| 20 #include "chrome/browser/sync_file_system/drive_backend/sync_task_token.h" |
18 #include "chrome/browser/sync_file_system/logger.h" | 21 #include "chrome/browser/sync_file_system/logger.h" |
19 #include "google_apis/drive/drive_api_parser.h" | 22 #include "google_apis/drive/drive_api_parser.h" |
20 | 23 |
21 namespace sync_file_system { | 24 namespace sync_file_system { |
22 namespace drive_backend { | 25 namespace drive_backend { |
23 | 26 |
24 ConflictResolver::ConflictResolver(SyncEngineContext* sync_context) | 27 ConflictResolver::ConflictResolver(SyncEngineContext* sync_context) |
25 : sync_context_(sync_context), | 28 : sync_context_(sync_context), |
26 weak_ptr_factory_(this) {} | 29 weak_ptr_factory_(this) {} |
27 | 30 |
28 ConflictResolver::~ConflictResolver() {} | 31 ConflictResolver::~ConflictResolver() {} |
29 | 32 |
30 void ConflictResolver::RunExclusive(const SyncStatusCallback& callback) { | 33 void ConflictResolver::RunPreflight(scoped_ptr<SyncTaskToken> token) { |
| 34 token->InitializeTaskLog("Conflict Resolution"); |
| 35 |
| 36 scoped_ptr<BlockingFactor> blocking_factor(new BlockingFactor); |
| 37 blocking_factor->exclusive = true; |
| 38 SyncTaskManager::UpdateBlockingFactor( |
| 39 token.Pass(), blocking_factor.Pass(), |
| 40 base::Bind(&ConflictResolver::RunExclusive, |
| 41 weak_ptr_factory_.GetWeakPtr())); |
| 42 } |
| 43 |
| 44 void ConflictResolver::RunExclusive(scoped_ptr<SyncTaskToken> token) { |
31 if (!IsContextReady()) { | 45 if (!IsContextReady()) { |
32 NOTREACHED(); | 46 NOTREACHED(); |
33 callback.Run(SYNC_STATUS_FAILED); | 47 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED); |
34 return; | 48 return; |
35 } | 49 } |
36 | 50 |
37 // Conflict resolution should be invoked on clean tree. | 51 // Conflict resolution should be invoked on clean tree. |
38 if (metadata_database()->HasDirtyTracker()) { | 52 if (metadata_database()->HasDirtyTracker()) { |
39 NOTREACHED(); | 53 NOTREACHED(); |
40 callback.Run(SYNC_STATUS_FAILED); | 54 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED); |
41 return; | 55 return; |
42 } | 56 } |
43 | 57 |
44 TrackerIDSet trackers; | 58 TrackerIDSet trackers; |
45 if (metadata_database()->GetMultiParentFileTrackers( | 59 if (metadata_database()->GetMultiParentFileTrackers( |
46 &target_file_id_, &trackers)) { | 60 &target_file_id_, &trackers)) { |
47 DCHECK_LT(1u, trackers.size()); | 61 DCHECK_LT(1u, trackers.size()); |
48 if (!trackers.has_active()) { | 62 if (!trackers.has_active()) { |
49 NOTREACHED(); | 63 NOTREACHED(); |
50 callback.Run(SYNC_STATUS_FAILED); | 64 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED); |
51 return; | 65 return; |
52 } | 66 } |
53 | 67 |
54 util::Log(logging::LOG_VERBOSE, FROM_HERE, | 68 token->RecordLog(base::StringPrintf( |
55 "[ConflictResolver] Detected multi-parent trackers " | 69 "Detected multi-parent trackers (active tracker_id=%" PRId64 ")", |
56 "(active tracker_id=%" PRId64 ")", | 70 trackers.active_tracker())); |
57 trackers.active_tracker()); | |
58 | 71 |
59 DCHECK(trackers.has_active()); | 72 DCHECK(trackers.has_active()); |
60 for (TrackerIDSet::const_iterator itr = trackers.begin(); | 73 for (TrackerIDSet::const_iterator itr = trackers.begin(); |
61 itr != trackers.end(); ++itr) { | 74 itr != trackers.end(); ++itr) { |
62 FileTracker tracker; | 75 FileTracker tracker; |
63 if (!metadata_database()->FindTrackerByTrackerID(*itr, &tracker)) { | 76 if (!metadata_database()->FindTrackerByTrackerID(*itr, &tracker)) { |
64 NOTREACHED(); | 77 NOTREACHED(); |
65 continue; | 78 continue; |
66 } | 79 } |
67 | 80 |
68 if (tracker.active()) | 81 if (tracker.active()) |
69 continue; | 82 continue; |
70 | 83 |
71 FileTracker parent_tracker; | 84 FileTracker parent_tracker; |
72 bool should_success = metadata_database()->FindTrackerByTrackerID( | 85 bool should_success = metadata_database()->FindTrackerByTrackerID( |
73 tracker.parent_tracker_id(), &parent_tracker); | 86 tracker.parent_tracker_id(), &parent_tracker); |
74 if (!should_success) { | 87 if (!should_success) { |
75 NOTREACHED(); | 88 NOTREACHED(); |
76 callback.Run(SYNC_STATUS_FAILED); | 89 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED); |
77 return; | 90 return; |
78 } | 91 } |
79 parents_to_remove_.push_back(parent_tracker.file_id()); | 92 parents_to_remove_.push_back(parent_tracker.file_id()); |
80 } | 93 } |
81 DetachFromNonPrimaryParents(callback); | 94 DetachFromNonPrimaryParents(token.Pass()); |
82 return; | 95 return; |
83 } | 96 } |
84 | 97 |
85 if (metadata_database()->GetConflictingTrackers(&trackers)) { | 98 if (metadata_database()->GetConflictingTrackers(&trackers)) { |
86 target_file_id_ = PickPrimaryFile(trackers); | 99 target_file_id_ = PickPrimaryFile(trackers); |
87 DCHECK(!target_file_id_.empty()); | 100 DCHECK(!target_file_id_.empty()); |
88 int64 primary_tracker_id = -1; | 101 int64 primary_tracker_id = -1; |
89 for (TrackerIDSet::const_iterator itr = trackers.begin(); | 102 for (TrackerIDSet::const_iterator itr = trackers.begin(); |
90 itr != trackers.end(); ++itr) { | 103 itr != trackers.end(); ++itr) { |
91 FileTracker tracker; | 104 FileTracker tracker; |
92 if (!metadata_database()->FindTrackerByTrackerID(*itr, &tracker)) { | 105 if (!metadata_database()->FindTrackerByTrackerID(*itr, &tracker)) { |
93 NOTREACHED(); | 106 NOTREACHED(); |
94 continue; | 107 continue; |
95 } | 108 } |
96 if (tracker.file_id() != target_file_id_) { | 109 if (tracker.file_id() != target_file_id_) { |
97 non_primary_file_ids_.push_back( | 110 non_primary_file_ids_.push_back( |
98 std::make_pair(tracker.file_id(), tracker.synced_details().etag())); | 111 std::make_pair(tracker.file_id(), tracker.synced_details().etag())); |
99 } else { | 112 } else { |
100 primary_tracker_id = tracker.tracker_id(); | 113 primary_tracker_id = tracker.tracker_id(); |
101 } | 114 } |
102 } | 115 } |
103 | 116 |
104 util::Log(logging::LOG_VERBOSE, FROM_HERE, | 117 token->RecordLog(base::StringPrintf( |
105 "[ConflictResolver] Detected %" PRIuS " conflicting trackers " | 118 "Detected %" PRIuS " conflicting trackers " |
106 "(primary tracker_id=%" PRId64 ")", | 119 "(primary tracker_id=%" PRId64 ")", |
107 non_primary_file_ids_.size(), primary_tracker_id); | 120 non_primary_file_ids_.size(), primary_tracker_id)); |
108 | 121 |
109 RemoveNonPrimaryFiles(callback); | 122 RemoveNonPrimaryFiles(token.Pass()); |
110 return; | 123 return; |
111 } | 124 } |
112 | 125 |
113 callback.Run(SYNC_STATUS_NO_CONFLICT); | 126 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_NO_CONFLICT); |
114 } | 127 } |
115 | 128 |
116 void ConflictResolver::DetachFromNonPrimaryParents( | 129 void ConflictResolver::DetachFromNonPrimaryParents( |
117 const SyncStatusCallback& callback) { | 130 scoped_ptr<SyncTaskToken> token) { |
118 DCHECK(!parents_to_remove_.empty()); | 131 DCHECK(!parents_to_remove_.empty()); |
119 | 132 |
120 // TODO(tzik): Check if ETag match is available for | 133 // TODO(tzik): Check if ETag match is available for |
121 // RemoteResourceFromDirectory. | 134 // RemoteResourceFromDirectory. |
122 std::string parent_folder_id = parents_to_remove_.back(); | 135 std::string parent_folder_id = parents_to_remove_.back(); |
123 parents_to_remove_.pop_back(); | 136 parents_to_remove_.pop_back(); |
| 137 |
| 138 token->RecordLog(base::StringPrintf( |
| 139 "Detach %s from %s", |
| 140 target_file_id_.c_str(), parent_folder_id.c_str())); |
| 141 |
124 drive_service()->RemoveResourceFromDirectory( | 142 drive_service()->RemoveResourceFromDirectory( |
125 parent_folder_id, target_file_id_, | 143 parent_folder_id, target_file_id_, |
126 base::Bind(&ConflictResolver::DidDetachFromParent, | 144 base::Bind(&ConflictResolver::DidDetachFromParent, |
127 weak_ptr_factory_.GetWeakPtr(), | 145 weak_ptr_factory_.GetWeakPtr(), |
128 callback)); | 146 base::Passed(&token))); |
129 util::Log(logging::LOG_VERBOSE, FROM_HERE, | |
130 "[ConflictResolver] Detach %s from %s", | |
131 target_file_id_.c_str(), parent_folder_id.c_str()); | |
132 } | 147 } |
133 | 148 |
134 void ConflictResolver::DidDetachFromParent(const SyncStatusCallback& callback, | 149 void ConflictResolver::DidDetachFromParent(scoped_ptr<SyncTaskToken> token, |
135 google_apis::GDataErrorCode error) { | 150 google_apis::GDataErrorCode error) { |
136 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); | 151 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); |
137 if (status != SYNC_STATUS_OK) { | 152 if (status != SYNC_STATUS_OK) { |
138 callback.Run(status); | 153 SyncTaskManager::NotifyTaskDone(token.Pass(), status); |
139 return; | 154 return; |
140 } | 155 } |
141 | 156 |
142 if (!parents_to_remove_.empty()) { | 157 if (!parents_to_remove_.empty()) { |
143 DetachFromNonPrimaryParents(callback); | 158 DetachFromNonPrimaryParents(token.Pass()); |
144 return; | 159 return; |
145 } | 160 } |
146 | 161 |
147 callback.Run(SYNC_STATUS_OK); | 162 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK); |
148 } | 163 } |
149 | 164 |
150 std::string ConflictResolver::PickPrimaryFile(const TrackerIDSet& trackers) { | 165 std::string ConflictResolver::PickPrimaryFile(const TrackerIDSet& trackers) { |
151 scoped_ptr<FileMetadata> primary; | 166 scoped_ptr<FileMetadata> primary; |
152 for (TrackerIDSet::const_iterator itr = trackers.begin(); | 167 for (TrackerIDSet::const_iterator itr = trackers.begin(); |
153 itr != trackers.end(); ++itr) { | 168 itr != trackers.end(); ++itr) { |
154 FileTracker tracker; | 169 FileTracker tracker; |
155 if (!metadata_database()->FindTrackerByTrackerID(*itr, &tracker)) { | 170 if (!metadata_database()->FindTrackerByTrackerID(*itr, &tracker)) { |
156 NOTREACHED(); | 171 NOTREACHED(); |
157 continue; | 172 continue; |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
205 primary = file_metadata.Pass(); | 220 primary = file_metadata.Pass(); |
206 continue; | 221 continue; |
207 } | 222 } |
208 } | 223 } |
209 | 224 |
210 if (primary) | 225 if (primary) |
211 return primary->file_id(); | 226 return primary->file_id(); |
212 return std::string(); | 227 return std::string(); |
213 } | 228 } |
214 | 229 |
215 void ConflictResolver::RemoveNonPrimaryFiles( | 230 void ConflictResolver::RemoveNonPrimaryFiles(scoped_ptr<SyncTaskToken> token) { |
216 const SyncStatusCallback& callback) { | |
217 DCHECK(!non_primary_file_ids_.empty()); | 231 DCHECK(!non_primary_file_ids_.empty()); |
218 | 232 |
219 std::string file_id = non_primary_file_ids_.back().first; | 233 std::string file_id = non_primary_file_ids_.back().first; |
220 std::string etag = non_primary_file_ids_.back().second; | 234 std::string etag = non_primary_file_ids_.back().second; |
221 non_primary_file_ids_.pop_back(); | 235 non_primary_file_ids_.pop_back(); |
222 | 236 |
223 DCHECK_NE(target_file_id_, file_id); | 237 DCHECK_NE(target_file_id_, file_id); |
224 | 238 |
225 util::Log(logging::LOG_VERBOSE, FROM_HERE, | 239 token->RecordLog(base::StringPrintf( |
226 "[ConflictResolver] Remove non-primary file %s", file_id.c_str()); | 240 "Remove non-primary file %s", file_id.c_str())); |
227 | 241 |
228 // TODO(tzik): Check if the file is a folder, and merge its contents into | 242 // TODO(tzik): Check if the file is a folder, and merge its contents into |
229 // the folder identified by |target_file_id_|. | 243 // the folder identified by |target_file_id_|. |
230 drive_service()->DeleteResource( | 244 drive_service()->DeleteResource( |
231 file_id, etag, | 245 file_id, etag, |
232 base::Bind(&ConflictResolver::DidRemoveFile, | 246 base::Bind(&ConflictResolver::DidRemoveFile, |
233 weak_ptr_factory_.GetWeakPtr(), | 247 weak_ptr_factory_.GetWeakPtr(), |
234 callback, file_id)); | 248 base::Passed(&token), file_id)); |
235 } | 249 } |
236 | 250 |
237 void ConflictResolver::DidRemoveFile(const SyncStatusCallback& callback, | 251 void ConflictResolver::DidRemoveFile(scoped_ptr<SyncTaskToken> token, |
238 const std::string& file_id, | 252 const std::string& file_id, |
239 google_apis::GDataErrorCode error) { | 253 google_apis::GDataErrorCode error) { |
240 if (error == google_apis::HTTP_PRECONDITION || | 254 if (error == google_apis::HTTP_PRECONDITION || |
241 error == google_apis::HTTP_CONFLICT) { | 255 error == google_apis::HTTP_CONFLICT) { |
242 UpdateFileMetadata(file_id, callback); | 256 UpdateFileMetadata(file_id, token.Pass()); |
243 return; | 257 return; |
244 } | 258 } |
245 | 259 |
246 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); | 260 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); |
247 if (status != SYNC_STATUS_OK && error != google_apis::HTTP_NOT_FOUND) { | 261 if (status != SYNC_STATUS_OK && error != google_apis::HTTP_NOT_FOUND) { |
248 callback.Run(status); | 262 SyncTaskManager::NotifyTaskDone(token.Pass(), status); |
249 return; | 263 return; |
250 } | 264 } |
251 | 265 |
252 deleted_file_ids_.push_back(file_id); | 266 deleted_file_ids_.push_back(file_id); |
253 if (!non_primary_file_ids_.empty()) { | 267 if (!non_primary_file_ids_.empty()) { |
254 RemoveNonPrimaryFiles(callback); | 268 RemoveNonPrimaryFiles(token.Pass()); |
255 return; | 269 return; |
256 } | 270 } |
257 | 271 |
258 metadata_database()->UpdateByDeletedRemoteFileList( | 272 metadata_database()->UpdateByDeletedRemoteFileList( |
259 deleted_file_ids_, callback); | 273 deleted_file_ids_, SyncTaskToken::WrapToCallback(token.Pass())); |
260 } | 274 } |
261 | 275 |
262 bool ConflictResolver::IsContextReady() { | 276 bool ConflictResolver::IsContextReady() { |
263 return sync_context_->GetDriveService() && | 277 return sync_context_->GetDriveService() && |
264 sync_context_->GetMetadataDatabase(); | 278 sync_context_->GetMetadataDatabase(); |
265 } | 279 } |
266 | 280 |
267 void ConflictResolver::UpdateFileMetadata( | 281 void ConflictResolver::UpdateFileMetadata( |
268 const std::string& file_id, | 282 const std::string& file_id, |
269 const SyncStatusCallback& callback) { | 283 scoped_ptr<SyncTaskToken> token) { |
270 drive_service()->GetFileResource( | 284 drive_service()->GetFileResource( |
271 file_id, | 285 file_id, |
272 base::Bind(&ConflictResolver::DidGetRemoteMetadata, | 286 base::Bind(&ConflictResolver::DidGetRemoteMetadata, |
273 weak_ptr_factory_.GetWeakPtr(), file_id, callback)); | 287 weak_ptr_factory_.GetWeakPtr(), file_id, |
| 288 base::Passed(&token))); |
274 } | 289 } |
275 | 290 |
276 void ConflictResolver::DidGetRemoteMetadata( | 291 void ConflictResolver::DidGetRemoteMetadata( |
277 const std::string& file_id, | 292 const std::string& file_id, |
278 const SyncStatusCallback& callback, | 293 scoped_ptr<SyncTaskToken> token, |
279 google_apis::GDataErrorCode error, | 294 google_apis::GDataErrorCode error, |
280 scoped_ptr<google_apis::FileResource> entry) { | 295 scoped_ptr<google_apis::FileResource> entry) { |
281 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); | 296 SyncStatusCode status = GDataErrorCodeToSyncStatusCode(error); |
282 if (status != SYNC_STATUS_OK && error != google_apis::HTTP_NOT_FOUND) { | 297 if (status != SYNC_STATUS_OK && error != google_apis::HTTP_NOT_FOUND) { |
283 callback.Run(status); | 298 SyncTaskManager::NotifyTaskDone(token.Pass(), status); |
284 return; | 299 return; |
285 } | 300 } |
286 | 301 |
287 if (error != google_apis::HTTP_NOT_FOUND) { | 302 if (error != google_apis::HTTP_NOT_FOUND) { |
288 metadata_database()->UpdateByDeletedRemoteFile(file_id, callback); | 303 metadata_database()->UpdateByDeletedRemoteFile( |
| 304 file_id, SyncTaskToken::WrapToCallback(token.Pass())); |
289 return; | 305 return; |
290 } | 306 } |
291 | 307 |
292 if (!entry) { | 308 if (!entry) { |
293 NOTREACHED(); | 309 NOTREACHED(); |
294 callback.Run(SYNC_STATUS_FAILED); | 310 SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_FAILED); |
295 return; | 311 return; |
296 } | 312 } |
297 | 313 |
298 metadata_database()->UpdateByFileResource(*entry, callback); | 314 metadata_database()->UpdateByFileResource( |
| 315 *entry, SyncTaskToken::WrapToCallback(token.Pass())); |
299 } | 316 } |
300 | 317 |
301 drive::DriveServiceInterface* ConflictResolver::drive_service() { | 318 drive::DriveServiceInterface* ConflictResolver::drive_service() { |
302 set_used_network(true); | 319 set_used_network(true); |
303 return sync_context_->GetDriveService(); | 320 return sync_context_->GetDriveService(); |
304 } | 321 } |
305 | 322 |
306 MetadataDatabase* ConflictResolver::metadata_database() { | 323 MetadataDatabase* ConflictResolver::metadata_database() { |
307 return sync_context_->GetMetadataDatabase(); | 324 return sync_context_->GetMetadataDatabase(); |
308 } | 325 } |
309 | 326 |
310 } // namespace drive_backend | 327 } // namespace drive_backend |
311 } // namespace sync_file_system | 328 } // namespace sync_file_system |
OLD | NEW |