Home | History | Annotate | Download | only in drive_backend
      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 "base/strings/string_number_conversions.h"
      9 #include "base/strings/string_util.h"
     10 #include "base/threading/thread_restrictions.h"
     11 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
     12 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
     13 #include "chrome/browser/sync_file_system/drive_backend/leveldb_wrapper.h"
     14 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
     15 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h"
     16 #include "chrome/browser/sync_file_system/logger.h"
     17 #include "third_party/leveldatabase/src/include/leveldb/db.h"
     18 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
     19 
     20 // LevelDB database schema
     21 // =======================
     22 //
     23 // NOTE
     24 // - Entries are sorted by keys.
     25 // - int64 value is serialized as a string by base::Int64ToString().
     26 // - ServiceMetadata, FileMetadata, and FileTracker values are serialized
     27 //   as a string by SerializeToString() of protocol buffers.
     28 //
     29 // Version 3
     30 //   # Version of this schema
     31 //   key: "VERSION"
     32 //   value: "3"
     33 //
     34 //   # Metadata of the SyncFS service
     35 //   key: "SERVICE"
     36 //   value: <ServiceMetadata 'service_metadata'>
     37 //
     38 //   # Metadata of remote files
     39 //   key: "FILE: " + <string 'file_id'>
     40 //   value: <FileMetadata 'metadata'>
     41 //
     42 //   # Trackers of local file updates
     43 //   key: "TRACKER: " + <int64 'tracker_id'>
     44 //   value: <FileTracker 'tracker'>
     45 
     46 namespace sync_file_system {
     47 namespace drive_backend {
     48 
     49 ParentIDAndTitle::ParentIDAndTitle() : parent_id(0) {}
     50 ParentIDAndTitle::ParentIDAndTitle(int64 parent_id,
     51                                    const std::string& title)
     52     : parent_id(parent_id), title(title) {}
     53 
     54 bool operator==(const ParentIDAndTitle& left, const ParentIDAndTitle& right) {
     55   return left.parent_id == right.parent_id && left.title == right.title;
     56 }
     57 
     58 bool operator<(const ParentIDAndTitle& left, const ParentIDAndTitle& right) {
     59   if (left.parent_id != right.parent_id)
     60     return left.parent_id < right.parent_id;
     61   return left.title < right.title;
     62 }
     63 
     64 DatabaseContents::DatabaseContents() {}
     65 
     66 DatabaseContents::~DatabaseContents() {}
     67 
     68 namespace {
     69 
     70 template <typename Container>
     71 typename Container::mapped_type FindItem(
     72     const Container& container,
     73     const typename Container::key_type& key) {
     74   typename Container::const_iterator found = container.find(key);
     75   if (found == container.end())
     76     return typename Container::mapped_type();
     77   return found->second;
     78 }
     79 
     80 void ReadDatabaseContents(LevelDBWrapper* db, DatabaseContents* contents) {
     81   DCHECK(db);
     82   DCHECK(contents);
     83 
     84   scoped_ptr<LevelDBWrapper::Iterator> itr(db->NewIterator());
     85   for (itr->SeekToFirst(); itr->Valid(); itr->Next()) {
     86     std::string key = itr->key().ToString();
     87     std::string value = itr->value().ToString();
     88 
     89     std::string file_id;
     90     if (RemovePrefix(key, kFileMetadataKeyPrefix, &file_id)) {
     91       scoped_ptr<FileMetadata> metadata(new FileMetadata);
     92       if (!metadata->ParseFromString(itr->value().ToString())) {
     93         util::Log(logging::LOG_WARNING, FROM_HERE,
     94                   "Failed to parse a FileMetadata");
     95         continue;
     96       }
     97 
     98       contents->file_metadata.push_back(metadata.release());
     99       continue;
    100     }
    101 
    102     std::string tracker_id_str;
    103     if (RemovePrefix(key, kFileTrackerKeyPrefix, &tracker_id_str)) {
    104       int64 tracker_id = 0;
    105       if (!base::StringToInt64(tracker_id_str, &tracker_id)) {
    106         util::Log(logging::LOG_WARNING, FROM_HERE,
    107                   "Failed to parse TrackerID");
    108         continue;
    109       }
    110 
    111       scoped_ptr<FileTracker> tracker(new FileTracker);
    112       if (!tracker->ParseFromString(itr->value().ToString())) {
    113         util::Log(logging::LOG_WARNING, FROM_HERE,
    114                   "Failed to parse a Tracker");
    115         continue;
    116       }
    117       contents->file_trackers.push_back(tracker.release());
    118       continue;
    119     }
    120   }
    121 }
    122 
    123 void RemoveUnreachableItems(DatabaseContents* contents,
    124                             int64 sync_root_tracker_id,
    125                             LevelDBWrapper* db) {
    126   typedef std::map<int64, std::set<int64> > ChildTrackersByParent;
    127   ChildTrackersByParent trackers_by_parent;
    128 
    129   // Set up links from parent tracker to child trackers.
    130   for (size_t i = 0; i < contents->file_trackers.size(); ++i) {
    131     const FileTracker& tracker = *contents->file_trackers[i];
    132     int64 parent_tracker_id = tracker.parent_tracker_id();
    133     int64 tracker_id = tracker.tracker_id();
    134 
    135     trackers_by_parent[parent_tracker_id].insert(tracker_id);
    136   }
    137 
    138   // Drop links from inactive trackers.
    139   for (size_t i = 0; i < contents->file_trackers.size(); ++i) {
    140     const FileTracker& tracker = *contents->file_trackers[i];
    141 
    142     if (!tracker.active())
    143       trackers_by_parent.erase(tracker.tracker_id());
    144   }
    145 
    146   std::vector<int64> pending;
    147   if (sync_root_tracker_id != kInvalidTrackerID)
    148     pending.push_back(sync_root_tracker_id);
    149 
    150   // Traverse tracker tree from sync-root.
    151   std::set<int64> visited_trackers;
    152   while (!pending.empty()) {
    153     int64 tracker_id = pending.back();
    154     DCHECK_NE(kInvalidTrackerID, tracker_id);
    155     pending.pop_back();
    156 
    157     if (!visited_trackers.insert(tracker_id).second) {
    158       NOTREACHED();
    159       continue;
    160     }
    161 
    162     AppendContents(
    163         LookUpMap(trackers_by_parent, tracker_id, std::set<int64>()),
    164         &pending);
    165   }
    166 
    167   // Delete all unreachable trackers.
    168   ScopedVector<FileTracker> reachable_trackers;
    169   for (size_t i = 0; i < contents->file_trackers.size(); ++i) {
    170     FileTracker* tracker = contents->file_trackers[i];
    171     if (ContainsKey(visited_trackers, tracker->tracker_id())) {
    172       reachable_trackers.push_back(tracker);
    173       contents->file_trackers[i] = NULL;
    174     } else {
    175       PutFileTrackerDeletionToDB(tracker->tracker_id(), db);
    176     }
    177   }
    178   contents->file_trackers = reachable_trackers.Pass();
    179 
    180   // List all |file_id| referred by a tracker.
    181   base::hash_set<std::string> referred_file_ids;
    182   for (size_t i = 0; i < contents->file_trackers.size(); ++i)
    183     referred_file_ids.insert(contents->file_trackers[i]->file_id());
    184 
    185   // Delete all unreferred metadata.
    186   ScopedVector<FileMetadata> referred_file_metadata;
    187   for (size_t i = 0; i < contents->file_metadata.size(); ++i) {
    188     FileMetadata* metadata = contents->file_metadata[i];
    189     if (ContainsKey(referred_file_ids, metadata->file_id())) {
    190       referred_file_metadata.push_back(metadata);
    191       contents->file_metadata[i] = NULL;
    192     } else {
    193       PutFileMetadataDeletionToDB(metadata->file_id(), db);
    194     }
    195   }
    196   contents->file_metadata = referred_file_metadata.Pass();
    197 }
    198 
    199 }  // namespace
    200 
    201 // static
    202 scoped_ptr<MetadataDatabaseIndex>
    203 MetadataDatabaseIndex::Create(LevelDBWrapper* db) {
    204   DCHECK(db);
    205 
    206   scoped_ptr<ServiceMetadata> service_metadata = InitializeServiceMetadata(db);
    207   if (!service_metadata)
    208     return scoped_ptr<MetadataDatabaseIndex>();
    209 
    210   DatabaseContents contents;
    211   PutVersionToDB(kCurrentDatabaseVersion, db);
    212   ReadDatabaseContents(db, &contents);
    213   RemoveUnreachableItems(&contents,
    214                          service_metadata->sync_root_tracker_id(),
    215                          db);
    216 
    217   scoped_ptr<MetadataDatabaseIndex> index(new MetadataDatabaseIndex(db));
    218   index->Initialize(service_metadata.Pass(), &contents);
    219   return index.Pass();
    220 }
    221 
    222 // static
    223 scoped_ptr<MetadataDatabaseIndex>
    224 MetadataDatabaseIndex::CreateForTesting(DatabaseContents* contents,
    225                                         LevelDBWrapper* db) {
    226   scoped_ptr<MetadataDatabaseIndex> index(new MetadataDatabaseIndex(db));
    227   index->Initialize(make_scoped_ptr(new ServiceMetadata), contents);
    228   return index.Pass();
    229 }
    230 
    231 void MetadataDatabaseIndex::Initialize(
    232     scoped_ptr<ServiceMetadata> service_metadata,
    233     DatabaseContents* contents) {
    234   service_metadata_ = service_metadata.Pass();
    235 
    236   for (size_t i = 0; i < contents->file_metadata.size(); ++i)
    237     StoreFileMetadata(make_scoped_ptr(contents->file_metadata[i]));
    238   contents->file_metadata.weak_clear();
    239 
    240   for (size_t i = 0; i < contents->file_trackers.size(); ++i)
    241     StoreFileTracker(make_scoped_ptr(contents->file_trackers[i]));
    242   contents->file_trackers.weak_clear();
    243 
    244   UMA_HISTOGRAM_COUNTS("SyncFileSystem.MetadataNumber", metadata_by_id_.size());
    245   UMA_HISTOGRAM_COUNTS("SyncFileSystem.TrackerNumber", tracker_by_id_.size());
    246   UMA_HISTOGRAM_COUNTS_100("SyncFileSystem.RegisteredAppNumber",
    247                            app_root_by_app_id_.size());
    248 }
    249 
    250 MetadataDatabaseIndex::MetadataDatabaseIndex(LevelDBWrapper* db) : db_(db) {}
    251 MetadataDatabaseIndex::~MetadataDatabaseIndex() {}
    252 
    253 bool MetadataDatabaseIndex::GetFileMetadata(
    254     const std::string& file_id, FileMetadata* metadata) const {
    255   FileMetadata* identified = metadata_by_id_.get(file_id);
    256   if (!identified)
    257     return false;
    258   if (metadata)
    259     metadata->CopyFrom(*identified);
    260   return true;
    261 }
    262 
    263 bool MetadataDatabaseIndex::GetFileTracker(
    264     int64 tracker_id, FileTracker* tracker) const {
    265   FileTracker* identified = tracker_by_id_.get(tracker_id);
    266   if (!identified)
    267     return false;
    268   if (tracker)
    269     tracker->CopyFrom(*identified);
    270   return true;
    271 }
    272 
    273 void MetadataDatabaseIndex::StoreFileMetadata(
    274     scoped_ptr<FileMetadata> metadata) {
    275   PutFileMetadataToDB(*metadata.get(), db_);
    276   if (!metadata) {
    277     NOTREACHED();
    278     return;
    279   }
    280 
    281   std::string file_id = metadata->file_id();
    282   metadata_by_id_.set(file_id, metadata.Pass());
    283 }
    284 
    285 void MetadataDatabaseIndex::StoreFileTracker(
    286     scoped_ptr<FileTracker> tracker) {
    287   PutFileTrackerToDB(*tracker.get(), db_);
    288   if (!tracker) {
    289     NOTREACHED();
    290     return;
    291   }
    292 
    293   int64 tracker_id = tracker->tracker_id();
    294   FileTracker* old_tracker = tracker_by_id_.get(tracker_id);
    295 
    296   if (!old_tracker) {
    297     DVLOG(3) << "Adding new tracker: " << tracker->tracker_id()
    298              << " " << GetTrackerTitle(*tracker);
    299 
    300     AddToAppIDIndex(*tracker);
    301     AddToPathIndexes(*tracker);
    302     AddToFileIDIndexes(*tracker);
    303     AddToDirtyTrackerIndexes(*tracker);
    304   } else {
    305     DVLOG(3) << "Updating tracker: " << tracker->tracker_id()
    306              << " " << GetTrackerTitle(*tracker);
    307 
    308     UpdateInAppIDIndex(*old_tracker, *tracker);
    309     UpdateInPathIndexes(*old_tracker, *tracker);
    310     UpdateInFileIDIndexes(*old_tracker, *tracker);
    311     UpdateInDirtyTrackerIndexes(*old_tracker, *tracker);
    312   }
    313 
    314   tracker_by_id_.set(tracker_id, tracker.Pass());
    315 }
    316 
    317 void MetadataDatabaseIndex::RemoveFileMetadata(const std::string& file_id) {
    318   PutFileMetadataDeletionToDB(file_id, db_);
    319   metadata_by_id_.erase(file_id);
    320 }
    321 
    322 void MetadataDatabaseIndex::RemoveFileTracker(int64 tracker_id) {
    323   PutFileTrackerDeletionToDB(tracker_id, db_);
    324 
    325   FileTracker* tracker = tracker_by_id_.get(tracker_id);
    326   if (!tracker) {
    327     NOTREACHED();
    328     return;
    329   }
    330 
    331   DVLOG(3) << "Removing tracker: "
    332            << tracker->tracker_id() << " " << GetTrackerTitle(*tracker);
    333 
    334   RemoveFromAppIDIndex(*tracker);
    335   RemoveFromPathIndexes(*tracker);
    336   RemoveFromFileIDIndexes(*tracker);
    337   RemoveFromDirtyTrackerIndexes(*tracker);
    338 
    339   tracker_by_id_.erase(tracker_id);
    340 }
    341 
    342 TrackerIDSet MetadataDatabaseIndex::GetFileTrackerIDsByFileID(
    343     const std::string& file_id) const {
    344   return FindItem(trackers_by_file_id_, file_id);
    345 }
    346 
    347 int64 MetadataDatabaseIndex::GetAppRootTracker(
    348     const std::string& app_id) const {
    349   return FindItem(app_root_by_app_id_, app_id);
    350 }
    351 
    352 TrackerIDSet MetadataDatabaseIndex::GetFileTrackerIDsByParentAndTitle(
    353     int64 parent_tracker_id,
    354     const std::string& title) const {
    355   TrackerIDsByParentAndTitle::const_iterator found =
    356       trackers_by_parent_and_title_.find(parent_tracker_id);
    357   if (found == trackers_by_parent_and_title_.end())
    358     return TrackerIDSet();
    359   return FindItem(found->second, title);
    360 }
    361 
    362 std::vector<int64> MetadataDatabaseIndex::GetFileTrackerIDsByParent(
    363     int64 parent_tracker_id) const {
    364   std::vector<int64> result;
    365   TrackerIDsByParentAndTitle::const_iterator found =
    366       trackers_by_parent_and_title_.find(parent_tracker_id);
    367   if (found == trackers_by_parent_and_title_.end())
    368     return result;
    369 
    370   for (TrackerIDsByTitle::const_iterator itr = found->second.begin();
    371        itr != found->second.end(); ++itr) {
    372     result.insert(result.end(), itr->second.begin(), itr->second.end());
    373   }
    374 
    375   return result;
    376 }
    377 
    378 std::string MetadataDatabaseIndex::PickMultiTrackerFileID() const {
    379   if (multi_tracker_file_ids_.empty())
    380     return std::string();
    381   return *multi_tracker_file_ids_.begin();
    382 }
    383 
    384 ParentIDAndTitle MetadataDatabaseIndex::PickMultiBackingFilePath() const {
    385   if (multi_backing_file_paths_.empty())
    386     return ParentIDAndTitle(kInvalidTrackerID, std::string());
    387   return *multi_backing_file_paths_.begin();
    388 }
    389 
    390 int64 MetadataDatabaseIndex::PickDirtyTracker() const {
    391   if (dirty_trackers_.empty())
    392     return kInvalidTrackerID;
    393   return *dirty_trackers_.begin();
    394 }
    395 
    396 void MetadataDatabaseIndex::DemoteDirtyTracker(int64 tracker_id) {
    397   if (dirty_trackers_.erase(tracker_id))
    398     demoted_dirty_trackers_.insert(tracker_id);
    399 }
    400 
    401 bool MetadataDatabaseIndex::HasDemotedDirtyTracker() const {
    402   return !demoted_dirty_trackers_.empty();
    403 }
    404 
    405 void MetadataDatabaseIndex::PromoteDemotedDirtyTracker(int64 tracker_id) {
    406   if (demoted_dirty_trackers_.erase(tracker_id) == 1)
    407     dirty_trackers_.insert(tracker_id);
    408 }
    409 
    410 bool MetadataDatabaseIndex::PromoteDemotedDirtyTrackers() {
    411   bool promoted = !demoted_dirty_trackers_.empty();
    412   dirty_trackers_.insert(demoted_dirty_trackers_.begin(),
    413                          demoted_dirty_trackers_.end());
    414   demoted_dirty_trackers_.clear();
    415   return promoted;
    416 }
    417 
    418 size_t MetadataDatabaseIndex::CountDirtyTracker() const {
    419   return dirty_trackers_.size();
    420 }
    421 
    422 size_t MetadataDatabaseIndex::CountFileMetadata() const {
    423   return metadata_by_id_.size();
    424 }
    425 
    426 size_t MetadataDatabaseIndex::CountFileTracker() const {
    427   return tracker_by_id_.size();
    428 }
    429 
    430 void MetadataDatabaseIndex::SetSyncRootTrackerID(
    431     int64 sync_root_id) const {
    432   service_metadata_->set_sync_root_tracker_id(sync_root_id);
    433   PutServiceMetadataToDB(*service_metadata_, db_);
    434 }
    435 
    436 void MetadataDatabaseIndex::SetLargestChangeID(
    437     int64 largest_change_id) const {
    438   service_metadata_->set_largest_change_id(largest_change_id);
    439   PutServiceMetadataToDB(*service_metadata_, db_);
    440 }
    441 
    442 void MetadataDatabaseIndex::SetNextTrackerID(
    443     int64 next_tracker_id) const {
    444   service_metadata_->set_next_tracker_id(next_tracker_id);
    445   PutServiceMetadataToDB(*service_metadata_, db_);
    446 }
    447 
    448 int64 MetadataDatabaseIndex::GetSyncRootTrackerID() const {
    449   if (!service_metadata_->has_sync_root_tracker_id())
    450     return kInvalidTrackerID;
    451   return service_metadata_->sync_root_tracker_id();
    452 }
    453 
    454 int64 MetadataDatabaseIndex::GetLargestChangeID() const {
    455   if (!service_metadata_->has_largest_change_id())
    456     return kInvalidTrackerID;
    457   return service_metadata_->largest_change_id();
    458 }
    459 
    460 int64 MetadataDatabaseIndex::GetNextTrackerID() const {
    461   if (!service_metadata_->has_next_tracker_id()) {
    462     NOTREACHED();
    463     return kInvalidTrackerID;
    464   }
    465   return service_metadata_->next_tracker_id();
    466 }
    467 
    468 std::vector<std::string> MetadataDatabaseIndex::GetRegisteredAppIDs() const {
    469   std::vector<std::string> result;
    470   result.reserve(app_root_by_app_id_.size());
    471   for (TrackerIDByAppID::const_iterator itr = app_root_by_app_id_.begin();
    472        itr != app_root_by_app_id_.end(); ++itr)
    473     result.push_back(itr->first);
    474   return result;
    475 }
    476 
    477 std::vector<int64> MetadataDatabaseIndex::GetAllTrackerIDs() const {
    478   std::vector<int64> result;
    479   for (TrackerByID::const_iterator itr = tracker_by_id_.begin();
    480        itr != tracker_by_id_.end(); ++itr) {
    481     result.push_back(itr->first);
    482   }
    483   return result;
    484 }
    485 
    486 std::vector<std::string> MetadataDatabaseIndex::GetAllMetadataIDs() const {
    487   std::vector<std::string> result;
    488   for (MetadataByID::const_iterator itr = metadata_by_id_.begin();
    489        itr != metadata_by_id_.end(); ++itr) {
    490     result.push_back(itr->first);
    491   }
    492   return result;
    493 }
    494 
    495 void MetadataDatabaseIndex::AddToAppIDIndex(
    496     const FileTracker& new_tracker) {
    497   if (!IsAppRoot(new_tracker))
    498     return;
    499 
    500   DVLOG(3) << "  Add to app_root_by_app_id_: " << new_tracker.app_id();
    501 
    502   DCHECK(new_tracker.active());
    503   DCHECK(!ContainsKey(app_root_by_app_id_, new_tracker.app_id()));
    504   app_root_by_app_id_[new_tracker.app_id()] = new_tracker.tracker_id();
    505 }
    506 
    507 void MetadataDatabaseIndex::UpdateInAppIDIndex(
    508     const FileTracker& old_tracker,
    509     const FileTracker& new_tracker) {
    510   DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
    511 
    512   if (IsAppRoot(old_tracker) && !IsAppRoot(new_tracker)) {
    513     DCHECK(old_tracker.active());
    514     DCHECK(!new_tracker.active());
    515     DCHECK(ContainsKey(app_root_by_app_id_, old_tracker.app_id()));
    516 
    517     DVLOG(3) << "  Remove from app_root_by_app_id_: " << old_tracker.app_id();
    518 
    519     app_root_by_app_id_.erase(old_tracker.app_id());
    520   } else if (!IsAppRoot(old_tracker) && IsAppRoot(new_tracker)) {
    521     DCHECK(!old_tracker.active());
    522     DCHECK(new_tracker.active());
    523     DCHECK(!ContainsKey(app_root_by_app_id_, new_tracker.app_id()));
    524 
    525     DVLOG(3) << "  Add to app_root_by_app_id_: " << new_tracker.app_id();
    526 
    527     app_root_by_app_id_[new_tracker.app_id()] = new_tracker.tracker_id();
    528   }
    529 }
    530 
    531 void MetadataDatabaseIndex::RemoveFromAppIDIndex(
    532     const FileTracker& tracker) {
    533   if (IsAppRoot(tracker)) {
    534     DCHECK(tracker.active());
    535     DCHECK(ContainsKey(app_root_by_app_id_, tracker.app_id()));
    536 
    537     DVLOG(3) << "  Remove from app_root_by_app_id_: " << tracker.app_id();
    538 
    539     app_root_by_app_id_.erase(tracker.app_id());
    540   }
    541 }
    542 
    543 void MetadataDatabaseIndex::AddToFileIDIndexes(
    544     const FileTracker& new_tracker) {
    545   DVLOG(3) << "  Add to trackers_by_file_id_: " << new_tracker.file_id();
    546 
    547   trackers_by_file_id_[new_tracker.file_id()].Insert(new_tracker);
    548 
    549   if (trackers_by_file_id_[new_tracker.file_id()].size() > 1) {
    550     DVLOG_IF(3, !ContainsKey(multi_tracker_file_ids_, new_tracker.file_id()))
    551         << "  Add to multi_tracker_file_ids_: " << new_tracker.file_id();
    552     multi_tracker_file_ids_.insert(new_tracker.file_id());
    553   }
    554 }
    555 
    556 void MetadataDatabaseIndex::UpdateInFileIDIndexes(
    557     const FileTracker& old_tracker,
    558     const FileTracker& new_tracker) {
    559   DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
    560   DCHECK_EQ(old_tracker.file_id(), new_tracker.file_id());
    561 
    562   std::string file_id = new_tracker.file_id();
    563   DCHECK(ContainsKey(trackers_by_file_id_, file_id));
    564 
    565   if (old_tracker.active() && !new_tracker.active())
    566     trackers_by_file_id_[file_id].Deactivate(new_tracker.tracker_id());
    567   else if (!old_tracker.active() && new_tracker.active())
    568     trackers_by_file_id_[file_id].Activate(new_tracker.tracker_id());
    569 }
    570 
    571 void MetadataDatabaseIndex::RemoveFromFileIDIndexes(
    572     const FileTracker& tracker) {
    573   TrackerIDsByFileID::iterator found =
    574       trackers_by_file_id_.find(tracker.file_id());
    575   if (found == trackers_by_file_id_.end()) {
    576     NOTREACHED();
    577     return;
    578   }
    579 
    580   DVLOG(3) << "  Remove from trackers_by_file_id_: "
    581            << tracker.tracker_id();
    582   found->second.Erase(tracker.tracker_id());
    583 
    584   if (trackers_by_file_id_[tracker.file_id()].size() <= 1) {
    585     DVLOG_IF(3, ContainsKey(multi_tracker_file_ids_, tracker.file_id()))
    586         << "  Remove from multi_tracker_file_ids_: " << tracker.file_id();
    587     multi_tracker_file_ids_.erase(tracker.file_id());
    588   }
    589 
    590   if (found->second.empty())
    591     trackers_by_file_id_.erase(found);
    592 }
    593 
    594 void MetadataDatabaseIndex::AddToPathIndexes(
    595     const FileTracker& new_tracker) {
    596   int64 parent = new_tracker.parent_tracker_id();
    597   std::string title = GetTrackerTitle(new_tracker);
    598 
    599   DVLOG(3) << "  Add to trackers_by_parent_and_title_: "
    600            << parent << " " << title;
    601 
    602   trackers_by_parent_and_title_[parent][title].Insert(new_tracker);
    603 
    604   if (trackers_by_parent_and_title_[parent][title].size() > 1 &&
    605       !title.empty()) {
    606     DVLOG_IF(3, !ContainsKey(multi_backing_file_paths_,
    607                              ParentIDAndTitle(parent, title)))
    608         << "  Add to multi_backing_file_paths_: " << parent << " " << title;
    609     multi_backing_file_paths_.insert(ParentIDAndTitle(parent, title));
    610   }
    611 }
    612 
    613 void MetadataDatabaseIndex::UpdateInPathIndexes(
    614     const FileTracker& old_tracker,
    615     const FileTracker& new_tracker) {
    616   DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
    617   DCHECK_EQ(old_tracker.parent_tracker_id(), new_tracker.parent_tracker_id());
    618   DCHECK(GetTrackerTitle(old_tracker) == GetTrackerTitle(new_tracker) ||
    619          !old_tracker.has_synced_details());
    620 
    621   int64 tracker_id = new_tracker.tracker_id();
    622   int64 parent = new_tracker.parent_tracker_id();
    623   std::string old_title = GetTrackerTitle(old_tracker);
    624   std::string title = GetTrackerTitle(new_tracker);
    625 
    626   TrackerIDsByTitle* trackers_by_title = &trackers_by_parent_and_title_[parent];
    627 
    628   if (old_title != title) {
    629     TrackerIDsByTitle::iterator found = trackers_by_title->find(old_title);
    630     if (found != trackers_by_title->end()) {
    631       DVLOG(3) << "  Remove from trackers_by_parent_and_title_: "
    632              << parent << " " << old_title;
    633 
    634       found->second.Erase(tracker_id);
    635       if (found->second.empty())
    636         trackers_by_title->erase(found);
    637     } else {
    638       NOTREACHED();
    639     }
    640 
    641     DVLOG(3) << "  Add to trackers_by_parent_and_title_: "
    642              << parent << " " << title;
    643 
    644     (*trackers_by_title)[title].Insert(new_tracker);
    645 
    646     if (trackers_by_parent_and_title_[parent][old_title].size() <= 1 &&
    647         !old_title.empty()) {
    648       DVLOG_IF(3, ContainsKey(multi_backing_file_paths_,
    649                               ParentIDAndTitle(parent, old_title)))
    650           << "  Remove from multi_backing_file_paths_: "
    651           << parent << " " << old_title;
    652       multi_backing_file_paths_.erase(ParentIDAndTitle(parent, old_title));
    653     }
    654 
    655     if (trackers_by_parent_and_title_[parent][title].size() > 1 &&
    656         !title.empty()) {
    657       DVLOG_IF(3, !ContainsKey(multi_backing_file_paths_,
    658                                ParentIDAndTitle(parent, title)))
    659           << "  Add to multi_backing_file_paths_: " << parent << " " << title;
    660       multi_backing_file_paths_.insert(ParentIDAndTitle(parent, title));
    661     }
    662 
    663     return;
    664   }
    665 
    666   if (old_tracker.active() && !new_tracker.active())
    667     trackers_by_parent_and_title_[parent][title].Deactivate(tracker_id);
    668   else if (!old_tracker.active() && new_tracker.active())
    669     trackers_by_parent_and_title_[parent][title].Activate(tracker_id);
    670 }
    671 
    672 void MetadataDatabaseIndex::RemoveFromPathIndexes(
    673     const FileTracker& tracker) {
    674   int64 tracker_id = tracker.tracker_id();
    675   int64 parent = tracker.parent_tracker_id();
    676   std::string title = GetTrackerTitle(tracker);
    677 
    678   DCHECK(ContainsKey(trackers_by_parent_and_title_, parent));
    679   DCHECK(ContainsKey(trackers_by_parent_and_title_[parent], title));
    680 
    681   DVLOG(3) << "  Remove from trackers_by_parent_and_title_: "
    682            << parent << " " << title;
    683 
    684   trackers_by_parent_and_title_[parent][title].Erase(tracker_id);
    685 
    686   if (trackers_by_parent_and_title_[parent][title].size() <= 1 &&
    687       !title.empty()) {
    688     DVLOG_IF(3, ContainsKey(multi_backing_file_paths_,
    689                             ParentIDAndTitle(parent, title)))
    690         << "  Remove from multi_backing_file_paths_: "
    691         << parent << " " << title;
    692     multi_backing_file_paths_.erase(ParentIDAndTitle(parent, title));
    693   }
    694 
    695   if (trackers_by_parent_and_title_[parent][title].empty()) {
    696     trackers_by_parent_and_title_[parent].erase(title);
    697     if (trackers_by_parent_and_title_[parent].empty())
    698       trackers_by_parent_and_title_.erase(parent);
    699   }
    700 }
    701 
    702 void MetadataDatabaseIndex::AddToDirtyTrackerIndexes(
    703     const FileTracker& new_tracker) {
    704   DCHECK(!ContainsKey(dirty_trackers_, new_tracker.tracker_id()));
    705   DCHECK(!ContainsKey(demoted_dirty_trackers_, new_tracker.tracker_id()));
    706 
    707   if (new_tracker.dirty()) {
    708     DVLOG(3) << "  Add to dirty_trackers_: " << new_tracker.tracker_id();
    709     dirty_trackers_.insert(new_tracker.tracker_id());
    710   }
    711 }
    712 
    713 void MetadataDatabaseIndex::UpdateInDirtyTrackerIndexes(
    714     const FileTracker& old_tracker,
    715     const FileTracker& new_tracker) {
    716   DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
    717 
    718   int64 tracker_id = new_tracker.tracker_id();
    719   if (old_tracker.dirty() && !new_tracker.dirty()) {
    720     DCHECK(ContainsKey(dirty_trackers_, tracker_id) ||
    721            ContainsKey(demoted_dirty_trackers_, tracker_id));
    722 
    723     DVLOG(3) << "  Remove from dirty_trackers_: " << tracker_id;
    724 
    725     dirty_trackers_.erase(tracker_id);
    726     demoted_dirty_trackers_.erase(tracker_id);
    727   } else if (!old_tracker.dirty() && new_tracker.dirty()) {
    728     DCHECK(!ContainsKey(dirty_trackers_, tracker_id));
    729     DCHECK(!ContainsKey(demoted_dirty_trackers_, tracker_id));
    730 
    731     DVLOG(3) << "  Add to dirty_trackers_: " << tracker_id;
    732 
    733     dirty_trackers_.insert(tracker_id);
    734   }
    735 }
    736 
    737 void MetadataDatabaseIndex::RemoveFromDirtyTrackerIndexes(
    738     const FileTracker& tracker) {
    739   if (tracker.dirty()) {
    740     int64 tracker_id = tracker.tracker_id();
    741     DCHECK(ContainsKey(dirty_trackers_, tracker_id) ||
    742            ContainsKey(demoted_dirty_trackers_, tracker_id));
    743 
    744     DVLOG(3) << "  Remove from dirty_trackers_: " << tracker_id;
    745     dirty_trackers_.erase(tracker_id);
    746 
    747     demoted_dirty_trackers_.erase(tracker_id);
    748   }
    749 }
    750 
    751 }  // namespace drive_backend
    752 }  // namespace sync_file_system
    753