1 // Copyright 2014 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_file_system/drive_backend/metadata_database_index.h" 6 7 #include "base/metrics/histogram.h" 8 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h" 9 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h" 10 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h" 11 12 namespace sync_file_system { 13 namespace drive_backend { 14 15 ParentIDAndTitle::ParentIDAndTitle() : parent_id(0) {} 16 ParentIDAndTitle::ParentIDAndTitle(int64 parent_id, 17 const std::string& title) 18 : parent_id(parent_id), title(title) {} 19 20 bool operator==(const ParentIDAndTitle& left, const ParentIDAndTitle& right) { 21 return left.parent_id == right.parent_id && left.title == right.title; 22 } 23 24 bool operator<(const ParentIDAndTitle& left, const ParentIDAndTitle& right) { 25 if (left.parent_id != right.parent_id) 26 return left.parent_id < right.parent_id; 27 return left.title < right.title; 28 } 29 30 namespace { 31 32 template <typename Container> 33 typename Container::mapped_type FindItem( 34 const Container& container, 35 const typename Container::key_type& key) { 36 typename Container::const_iterator found = container.find(key); 37 if (found == container.end()) 38 return typename Container::mapped_type(); 39 return found->second; 40 } 41 42 bool IsAppRoot(const FileTracker& tracker) { 43 return tracker.tracker_kind() == TRACKER_KIND_APP_ROOT || 44 tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT; 45 } 46 47 std::string GetTrackerTitle(const FileTracker& tracker) { 48 if (tracker.has_synced_details()) 49 return tracker.synced_details().title(); 50 return std::string(); 51 } 52 53 } // namespace 54 55 MetadataDatabaseIndex::MetadataDatabaseIndex(DatabaseContents* content) { 56 for (size_t i = 0; i < content->file_metadata.size(); ++i) 57 StoreFileMetadata(make_scoped_ptr(content->file_metadata[i])); 58 content->file_metadata.weak_clear(); 59 60 for (size_t i = 0; i < content->file_trackers.size(); ++i) 61 StoreFileTracker(make_scoped_ptr(content->file_trackers[i])); 62 content->file_trackers.weak_clear(); 63 64 UMA_HISTOGRAM_COUNTS("SyncFileSystem.MetadataNumber", metadata_by_id_.size()); 65 UMA_HISTOGRAM_COUNTS("SyncFileSystem.TrackerNumber", tracker_by_id_.size()); 66 UMA_HISTOGRAM_COUNTS_100("SyncFileSystem.RegisteredAppNumber", 67 app_root_by_app_id_.size()); 68 } 69 70 MetadataDatabaseIndex::~MetadataDatabaseIndex() {} 71 72 const FileTracker* MetadataDatabaseIndex::GetFileTracker( 73 int64 tracker_id) const { 74 return tracker_by_id_.get(tracker_id); 75 } 76 77 const FileMetadata* MetadataDatabaseIndex::GetFileMetadata( 78 const std::string& file_id) const { 79 return metadata_by_id_.get(file_id); 80 } 81 82 void MetadataDatabaseIndex::StoreFileMetadata( 83 scoped_ptr<FileMetadata> metadata) { 84 if (!metadata) { 85 NOTREACHED(); 86 return; 87 } 88 89 std::string file_id = metadata->file_id(); 90 metadata_by_id_.set(file_id, metadata.Pass()); 91 } 92 93 void MetadataDatabaseIndex::StoreFileTracker(scoped_ptr<FileTracker> tracker) { 94 if (!tracker) { 95 NOTREACHED(); 96 return; 97 } 98 99 int64 tracker_id = tracker->tracker_id(); 100 FileTracker* old_tracker = tracker_by_id_.get(tracker_id); 101 102 if (!old_tracker) { 103 DVLOG(3) << "Adding new tracker: " << tracker->tracker_id() 104 << " " << GetTrackerTitle(*tracker); 105 106 AddToAppIDIndex(*tracker); 107 AddToPathIndexes(*tracker); 108 AddToFileIDIndexes(*tracker); 109 AddToDirtyTrackerIndexes(*tracker); 110 } else { 111 DVLOG(3) << "Updating tracker: " << tracker->tracker_id() 112 << " " << GetTrackerTitle(*tracker); 113 114 UpdateInAppIDIndex(*old_tracker, *tracker); 115 UpdateInPathIndexes(*old_tracker, *tracker); 116 UpdateInFileIDIndexes(*old_tracker, *tracker); 117 UpdateInDirtyTrackerIndexes(*old_tracker, *tracker); 118 } 119 120 tracker_by_id_.set(tracker_id, tracker.Pass()); 121 } 122 123 void MetadataDatabaseIndex::RemoveFileMetadata(const std::string& file_id) { 124 metadata_by_id_.erase(file_id); 125 } 126 127 void MetadataDatabaseIndex::RemoveFileTracker(int64 tracker_id) { 128 FileTracker* tracker = tracker_by_id_.get(tracker_id); 129 if (!tracker) { 130 NOTREACHED(); 131 return; 132 } 133 134 DVLOG(3) << "Removing tracker: " 135 << tracker->tracker_id() << " " << GetTrackerTitle(*tracker); 136 137 RemoveFromAppIDIndex(*tracker); 138 RemoveFromPathIndexes(*tracker); 139 RemoveFromFileIDIndexes(*tracker); 140 RemoveFromDirtyTrackerIndexes(*tracker); 141 142 tracker_by_id_.erase(tracker_id); 143 } 144 145 TrackerIDSet MetadataDatabaseIndex::GetFileTrackerIDsByFileID( 146 const std::string& file_id) const { 147 return FindItem(trackers_by_file_id_, file_id); 148 } 149 150 int64 MetadataDatabaseIndex::GetAppRootTracker( 151 const std::string& app_id) const { 152 return FindItem(app_root_by_app_id_, app_id); 153 } 154 155 TrackerIDSet MetadataDatabaseIndex::GetFileTrackerIDsByParentAndTitle( 156 int64 parent_tracker_id, 157 const std::string& title) const { 158 TrackerIDsByParentAndTitle::const_iterator found = 159 trackers_by_parent_and_title_.find(parent_tracker_id); 160 if (found == trackers_by_parent_and_title_.end()) 161 return TrackerIDSet(); 162 return FindItem(found->second, title); 163 } 164 165 std::vector<int64> MetadataDatabaseIndex::GetFileTrackerIDsByParent( 166 int64 parent_tracker_id) const { 167 std::vector<int64> result; 168 TrackerIDsByParentAndTitle::const_iterator found = 169 trackers_by_parent_and_title_.find(parent_tracker_id); 170 if (found == trackers_by_parent_and_title_.end()) 171 return result; 172 173 for (TrackerIDsByTitle::const_iterator itr = found->second.begin(); 174 itr != found->second.end(); ++itr) { 175 result.insert(result.end(), itr->second.begin(), itr->second.end()); 176 } 177 178 return result; 179 } 180 181 std::string MetadataDatabaseIndex::PickMultiTrackerFileID() const { 182 if (multi_tracker_file_ids_.empty()) 183 return std::string(); 184 return *multi_tracker_file_ids_.begin(); 185 } 186 187 ParentIDAndTitle MetadataDatabaseIndex::PickMultiBackingFilePath() const { 188 if (multi_backing_file_paths_.empty()) 189 return ParentIDAndTitle(kInvalidTrackerID, std::string()); 190 return *multi_backing_file_paths_.begin(); 191 } 192 193 int64 MetadataDatabaseIndex::PickDirtyTracker() const { 194 if (dirty_trackers_.empty()) 195 return kInvalidTrackerID; 196 return *dirty_trackers_.begin(); 197 } 198 199 void MetadataDatabaseIndex::DemoteDirtyTracker(int64 tracker_id) { 200 if (dirty_trackers_.erase(tracker_id)) 201 demoted_dirty_trackers_.insert(tracker_id); 202 } 203 204 bool MetadataDatabaseIndex::HasDemotedDirtyTracker() const { 205 return !demoted_dirty_trackers_.empty(); 206 } 207 208 void MetadataDatabaseIndex::PromoteDemotedDirtyTrackers() { 209 dirty_trackers_.insert(demoted_dirty_trackers_.begin(), 210 demoted_dirty_trackers_.end()); 211 demoted_dirty_trackers_.clear(); 212 } 213 214 size_t MetadataDatabaseIndex::CountDirtyTracker() const { 215 return dirty_trackers_.size() + demoted_dirty_trackers_.size(); 216 } 217 218 size_t MetadataDatabaseIndex::CountFileMetadata() const { 219 return metadata_by_id_.size(); 220 } 221 222 size_t MetadataDatabaseIndex::CountFileTracker() const { 223 return tracker_by_id_.size(); 224 } 225 226 std::vector<std::string> MetadataDatabaseIndex::GetRegisteredAppIDs() const { 227 std::vector<std::string> result; 228 result.reserve(app_root_by_app_id_.size()); 229 for (TrackerIDByAppID::const_iterator itr = app_root_by_app_id_.begin(); 230 itr != app_root_by_app_id_.end(); ++itr) 231 result.push_back(itr->first); 232 return result; 233 } 234 235 std::vector<int64> MetadataDatabaseIndex::GetAllTrackerIDs() const { 236 std::vector<int64> result; 237 for (TrackerByID::const_iterator itr = tracker_by_id_.begin(); 238 itr != tracker_by_id_.end(); ++itr) { 239 result.push_back(itr->first); 240 } 241 return result; 242 } 243 244 std::vector<std::string> MetadataDatabaseIndex::GetAllMetadataIDs() const { 245 std::vector<std::string> result; 246 for (MetadataByID::const_iterator itr = metadata_by_id_.begin(); 247 itr != metadata_by_id_.end(); ++itr) { 248 result.push_back(itr->first); 249 } 250 return result; 251 } 252 253 void MetadataDatabaseIndex::AddToAppIDIndex( 254 const FileTracker& new_tracker) { 255 if (!IsAppRoot(new_tracker)) 256 return; 257 258 DVLOG(3) << " Add to app_root_by_app_id_: " << new_tracker.app_id(); 259 260 DCHECK(new_tracker.active()); 261 DCHECK(!ContainsKey(app_root_by_app_id_, new_tracker.app_id())); 262 app_root_by_app_id_[new_tracker.app_id()] = new_tracker.tracker_id(); 263 } 264 265 void MetadataDatabaseIndex::UpdateInAppIDIndex( 266 const FileTracker& old_tracker, 267 const FileTracker& new_tracker) { 268 DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id()); 269 270 if (IsAppRoot(old_tracker) && !IsAppRoot(new_tracker)) { 271 DCHECK(old_tracker.active()); 272 DCHECK(!new_tracker.active()); 273 DCHECK(ContainsKey(app_root_by_app_id_, old_tracker.app_id())); 274 275 DVLOG(3) << " Remove from app_root_by_app_id_: " << old_tracker.app_id(); 276 277 app_root_by_app_id_.erase(old_tracker.app_id()); 278 } else if (!IsAppRoot(old_tracker) && IsAppRoot(new_tracker)) { 279 DCHECK(!old_tracker.active()); 280 DCHECK(new_tracker.active()); 281 DCHECK(!ContainsKey(app_root_by_app_id_, new_tracker.app_id())); 282 283 DVLOG(3) << " Add to app_root_by_app_id_: " << new_tracker.app_id(); 284 285 app_root_by_app_id_[new_tracker.app_id()] = new_tracker.tracker_id(); 286 } 287 } 288 289 void MetadataDatabaseIndex::RemoveFromAppIDIndex( 290 const FileTracker& tracker) { 291 if (IsAppRoot(tracker)) { 292 DCHECK(tracker.active()); 293 DCHECK(ContainsKey(app_root_by_app_id_, tracker.app_id())); 294 295 DVLOG(3) << " Remove from app_root_by_app_id_: " << tracker.app_id(); 296 297 app_root_by_app_id_.erase(tracker.app_id()); 298 } 299 } 300 301 void MetadataDatabaseIndex::AddToFileIDIndexes( 302 const FileTracker& new_tracker) { 303 DVLOG(3) << " Add to trackers_by_file_id_: " << new_tracker.file_id(); 304 305 trackers_by_file_id_[new_tracker.file_id()].Insert(new_tracker); 306 307 if (trackers_by_file_id_[new_tracker.file_id()].size() > 1) { 308 DVLOG_IF(3, !ContainsKey(multi_tracker_file_ids_, new_tracker.file_id())) 309 << " Add to multi_tracker_file_ids_: " << new_tracker.file_id(); 310 multi_tracker_file_ids_.insert(new_tracker.file_id()); 311 } 312 } 313 314 void MetadataDatabaseIndex::UpdateInFileIDIndexes( 315 const FileTracker& old_tracker, 316 const FileTracker& new_tracker) { 317 DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id()); 318 DCHECK_EQ(old_tracker.file_id(), new_tracker.file_id()); 319 320 std::string file_id = new_tracker.file_id(); 321 DCHECK(ContainsKey(trackers_by_file_id_, file_id)); 322 323 if (old_tracker.active() && !new_tracker.active()) 324 trackers_by_file_id_[file_id].Deactivate(new_tracker.tracker_id()); 325 else if (!old_tracker.active() && new_tracker.active()) 326 trackers_by_file_id_[file_id].Activate(new_tracker.tracker_id()); 327 } 328 329 void MetadataDatabaseIndex::RemoveFromFileIDIndexes( 330 const FileTracker& tracker) { 331 TrackerIDsByFileID::iterator found = 332 trackers_by_file_id_.find(tracker.file_id()); 333 if (found == trackers_by_file_id_.end()) { 334 NOTREACHED(); 335 return; 336 } 337 338 DVLOG(3) << " Remove from trackers_by_file_id_: " 339 << tracker.tracker_id(); 340 found->second.Erase(tracker.tracker_id()); 341 342 if (trackers_by_file_id_[tracker.file_id()].size() <= 1) { 343 DVLOG_IF(3, ContainsKey(multi_tracker_file_ids_, tracker.file_id())) 344 << " Remove from multi_tracker_file_ids_: " << tracker.file_id(); 345 multi_tracker_file_ids_.erase(tracker.file_id()); 346 } 347 348 if (found->second.empty()) 349 trackers_by_file_id_.erase(found); 350 } 351 352 void MetadataDatabaseIndex::AddToPathIndexes( 353 const FileTracker& new_tracker) { 354 int64 parent = new_tracker.parent_tracker_id(); 355 std::string title = GetTrackerTitle(new_tracker); 356 357 DVLOG(3) << " Add to trackers_by_parent_and_title_: " 358 << parent << " " << title; 359 360 trackers_by_parent_and_title_[parent][title].Insert(new_tracker); 361 362 if (trackers_by_parent_and_title_[parent][title].size() > 1 && 363 !title.empty()) { 364 DVLOG_IF(3, !ContainsKey(multi_backing_file_paths_, 365 ParentIDAndTitle(parent, title))) 366 << " Add to multi_backing_file_paths_: " << parent << " " << title; 367 multi_backing_file_paths_.insert(ParentIDAndTitle(parent, title)); 368 } 369 } 370 371 void MetadataDatabaseIndex::UpdateInPathIndexes( 372 const FileTracker& old_tracker, 373 const FileTracker& new_tracker) { 374 DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id()); 375 DCHECK_EQ(old_tracker.parent_tracker_id(), new_tracker.parent_tracker_id()); 376 DCHECK(GetTrackerTitle(old_tracker) == GetTrackerTitle(new_tracker) || 377 !old_tracker.has_synced_details()); 378 379 int64 tracker_id = new_tracker.tracker_id(); 380 int64 parent = new_tracker.parent_tracker_id(); 381 std::string old_title = GetTrackerTitle(old_tracker); 382 std::string title = GetTrackerTitle(new_tracker); 383 384 TrackerIDsByTitle* trackers_by_title = &trackers_by_parent_and_title_[parent]; 385 386 if (old_title != title) { 387 TrackerIDsByTitle::iterator found = trackers_by_title->find(old_title); 388 if (found != trackers_by_title->end()) { 389 DVLOG(3) << " Remove from trackers_by_parent_and_title_: " 390 << parent << " " << old_title; 391 392 found->second.Erase(tracker_id); 393 if (found->second.empty()) 394 trackers_by_title->erase(found); 395 } else { 396 NOTREACHED(); 397 } 398 399 DVLOG(3) << " Add to trackers_by_parent_and_title_: " 400 << parent << " " << title; 401 402 (*trackers_by_title)[title].Insert(new_tracker); 403 404 if (trackers_by_parent_and_title_[parent][old_title].size() <= 1 && 405 !old_title.empty()) { 406 DVLOG_IF(3, ContainsKey(multi_backing_file_paths_, 407 ParentIDAndTitle(parent, old_title))) 408 << " Remove from multi_backing_file_paths_: " 409 << parent << " " << old_title; 410 multi_backing_file_paths_.erase(ParentIDAndTitle(parent, old_title)); 411 } 412 413 if (trackers_by_parent_and_title_[parent][title].size() > 1 && 414 !title.empty()) { 415 DVLOG_IF(3, !ContainsKey(multi_backing_file_paths_, 416 ParentIDAndTitle(parent, title))) 417 << " Add to multi_backing_file_paths_: " << parent << " " << title; 418 multi_backing_file_paths_.insert(ParentIDAndTitle(parent, title)); 419 } 420 421 return; 422 } 423 424 if (old_tracker.active() && !new_tracker.active()) 425 trackers_by_parent_and_title_[parent][title].Deactivate(tracker_id); 426 else if (!old_tracker.active() && new_tracker.active()) 427 trackers_by_parent_and_title_[parent][title].Activate(tracker_id); 428 } 429 430 void MetadataDatabaseIndex::RemoveFromPathIndexes( 431 const FileTracker& tracker) { 432 int64 tracker_id = tracker.tracker_id(); 433 int64 parent = tracker.parent_tracker_id(); 434 std::string title = GetTrackerTitle(tracker); 435 436 DCHECK(ContainsKey(trackers_by_parent_and_title_, parent)); 437 DCHECK(ContainsKey(trackers_by_parent_and_title_[parent], title)); 438 439 DVLOG(3) << " Remove from trackers_by_parent_and_title_: " 440 << parent << " " << title; 441 442 trackers_by_parent_and_title_[parent][title].Erase(tracker_id); 443 444 if (trackers_by_parent_and_title_[parent][title].size() <= 1 && 445 !title.empty()) { 446 DVLOG_IF(3, ContainsKey(multi_backing_file_paths_, 447 ParentIDAndTitle(parent, title))) 448 << " Remove from multi_backing_file_paths_: " 449 << parent << " " << title; 450 multi_backing_file_paths_.erase(ParentIDAndTitle(parent, title)); 451 } 452 453 if (trackers_by_parent_and_title_[parent][title].empty()) { 454 trackers_by_parent_and_title_[parent].erase(title); 455 if (trackers_by_parent_and_title_[parent].empty()) 456 trackers_by_parent_and_title_.erase(parent); 457 } 458 } 459 460 void MetadataDatabaseIndex::AddToDirtyTrackerIndexes( 461 const FileTracker& new_tracker) { 462 DCHECK(!ContainsKey(dirty_trackers_, new_tracker.tracker_id())); 463 DCHECK(!ContainsKey(demoted_dirty_trackers_, new_tracker.tracker_id())); 464 465 if (new_tracker.dirty()) { 466 DVLOG(3) << " Add to dirty_trackers_: " << new_tracker.tracker_id(); 467 dirty_trackers_.insert(new_tracker.tracker_id()); 468 } 469 } 470 471 void MetadataDatabaseIndex::UpdateInDirtyTrackerIndexes( 472 const FileTracker& old_tracker, 473 const FileTracker& new_tracker) { 474 DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id()); 475 476 int64 tracker_id = new_tracker.tracker_id(); 477 if (old_tracker.dirty() && !new_tracker.dirty()) { 478 DCHECK(ContainsKey(dirty_trackers_, tracker_id) || 479 ContainsKey(demoted_dirty_trackers_, tracker_id)); 480 481 DVLOG(3) << " Remove from dirty_trackers_: " << tracker_id; 482 483 dirty_trackers_.erase(tracker_id); 484 demoted_dirty_trackers_.erase(tracker_id); 485 } else if (!old_tracker.dirty() && new_tracker.dirty()) { 486 DCHECK(!ContainsKey(dirty_trackers_, tracker_id)); 487 DCHECK(!ContainsKey(demoted_dirty_trackers_, tracker_id)); 488 489 DVLOG(3) << " Add to dirty_trackers_: " << tracker_id; 490 491 dirty_trackers_.insert(tracker_id); 492 } 493 } 494 495 void MetadataDatabaseIndex::RemoveFromDirtyTrackerIndexes( 496 const FileTracker& tracker) { 497 if (tracker.dirty()) { 498 int64 tracker_id = tracker.tracker_id(); 499 DCHECK(ContainsKey(dirty_trackers_, tracker_id) || 500 ContainsKey(demoted_dirty_trackers_, tracker_id)); 501 502 DVLOG(3) << " Remove from dirty_trackers_: " << tracker_id; 503 dirty_trackers_.erase(tracker_id); 504 505 demoted_dirty_trackers_.erase(tracker_id); 506 } 507 } 508 509 } // namespace drive_backend 510 } // namespace sync_file_system 511