metadata_database_index.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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
46namespace sync_file_system {
47namespace drive_backend {
48
49ParentIDAndTitle::ParentIDAndTitle() : parent_id(0) {}
50ParentIDAndTitle::ParentIDAndTitle(int64 parent_id,
51                                   const std::string& title)
52    : parent_id(parent_id), title(title) {}
53
54bool operator==(const ParentIDAndTitle& left, const ParentIDAndTitle& right) {
55  return left.parent_id == right.parent_id && left.title == right.title;
56}
57
58bool 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
64DatabaseContents::DatabaseContents() {}
65
66DatabaseContents::~DatabaseContents() {}
67
68namespace {
69
70template <typename Container>
71typename 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
80void 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
123void 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
202scoped_ptr<MetadataDatabaseIndex>
203MetadataDatabaseIndex::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
223scoped_ptr<MetadataDatabaseIndex>
224MetadataDatabaseIndex::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
231void 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
250MetadataDatabaseIndex::MetadataDatabaseIndex(LevelDBWrapper* db) : db_(db) {}
251MetadataDatabaseIndex::~MetadataDatabaseIndex() {}
252
253bool 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
263bool 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
273void 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
285void 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
317void MetadataDatabaseIndex::RemoveFileMetadata(const std::string& file_id) {
318  PutFileMetadataDeletionToDB(file_id, db_);
319  metadata_by_id_.erase(file_id);
320}
321
322void 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
342TrackerIDSet MetadataDatabaseIndex::GetFileTrackerIDsByFileID(
343    const std::string& file_id) const {
344  return FindItem(trackers_by_file_id_, file_id);
345}
346
347int64 MetadataDatabaseIndex::GetAppRootTracker(
348    const std::string& app_id) const {
349  return FindItem(app_root_by_app_id_, app_id);
350}
351
352TrackerIDSet 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
362std::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
378std::string MetadataDatabaseIndex::PickMultiTrackerFileID() const {
379  if (multi_tracker_file_ids_.empty())
380    return std::string();
381  return *multi_tracker_file_ids_.begin();
382}
383
384ParentIDAndTitle 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
390int64 MetadataDatabaseIndex::PickDirtyTracker() const {
391  if (dirty_trackers_.empty())
392    return kInvalidTrackerID;
393  return *dirty_trackers_.begin();
394}
395
396void MetadataDatabaseIndex::DemoteDirtyTracker(int64 tracker_id) {
397  if (dirty_trackers_.erase(tracker_id))
398    demoted_dirty_trackers_.insert(tracker_id);
399}
400
401bool MetadataDatabaseIndex::HasDemotedDirtyTracker() const {
402  return !demoted_dirty_trackers_.empty();
403}
404
405void MetadataDatabaseIndex::PromoteDemotedDirtyTracker(int64 tracker_id) {
406  if (demoted_dirty_trackers_.erase(tracker_id) == 1)
407    dirty_trackers_.insert(tracker_id);
408}
409
410bool 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
418size_t MetadataDatabaseIndex::CountDirtyTracker() const {
419  return dirty_trackers_.size();
420}
421
422size_t MetadataDatabaseIndex::CountFileMetadata() const {
423  return metadata_by_id_.size();
424}
425
426size_t MetadataDatabaseIndex::CountFileTracker() const {
427  return tracker_by_id_.size();
428}
429
430void 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
436void 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
442void 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
448int64 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
454int64 MetadataDatabaseIndex::GetLargestChangeID() const {
455  if (!service_metadata_->has_largest_change_id())
456    return kInvalidTrackerID;
457  return service_metadata_->largest_change_id();
458}
459
460int64 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
468std::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
477std::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
486std::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
495void 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
507void 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
531void 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
543void 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
556void 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
571void 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
594void 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
613void 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
672void 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
702void 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
713void 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
737void 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