metadata_database_index.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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  DatabaseContents contents;
208
209  PutVersionToDB(kCurrentDatabaseVersion, db);
210  ReadDatabaseContents(db, &contents);
211  RemoveUnreachableItems(&contents,
212                         service_metadata->sync_root_tracker_id(),
213                         db);
214
215  scoped_ptr<MetadataDatabaseIndex> index(new MetadataDatabaseIndex(db));
216  index->Initialize(service_metadata.Pass(), &contents);
217  return index.Pass();
218}
219
220// static
221scoped_ptr<MetadataDatabaseIndex>
222MetadataDatabaseIndex::CreateForTesting(DatabaseContents* contents,
223                                        LevelDBWrapper* db) {
224  scoped_ptr<MetadataDatabaseIndex> index(new MetadataDatabaseIndex(db));
225  index->Initialize(make_scoped_ptr(new ServiceMetadata), contents);
226  return index.Pass();
227}
228
229void MetadataDatabaseIndex::Initialize(
230    scoped_ptr<ServiceMetadata> service_metadata,
231    DatabaseContents* contents) {
232  service_metadata_ = service_metadata.Pass();
233
234  for (size_t i = 0; i < contents->file_metadata.size(); ++i)
235    StoreFileMetadata(make_scoped_ptr(contents->file_metadata[i]));
236  contents->file_metadata.weak_clear();
237
238  for (size_t i = 0; i < contents->file_trackers.size(); ++i)
239    StoreFileTracker(make_scoped_ptr(contents->file_trackers[i]));
240  contents->file_trackers.weak_clear();
241
242  UMA_HISTOGRAM_COUNTS("SyncFileSystem.MetadataNumber", metadata_by_id_.size());
243  UMA_HISTOGRAM_COUNTS("SyncFileSystem.TrackerNumber", tracker_by_id_.size());
244  UMA_HISTOGRAM_COUNTS_100("SyncFileSystem.RegisteredAppNumber",
245                           app_root_by_app_id_.size());
246}
247
248MetadataDatabaseIndex::MetadataDatabaseIndex(LevelDBWrapper* db) : db_(db) {}
249MetadataDatabaseIndex::~MetadataDatabaseIndex() {}
250
251bool MetadataDatabaseIndex::GetFileMetadata(
252    const std::string& file_id, FileMetadata* metadata) const {
253  FileMetadata* identified = metadata_by_id_.get(file_id);
254  if (!identified)
255    return false;
256  if (metadata)
257    metadata->CopyFrom(*identified);
258  return true;
259}
260
261bool MetadataDatabaseIndex::GetFileTracker(
262    int64 tracker_id, FileTracker* tracker) const {
263  FileTracker* identified = tracker_by_id_.get(tracker_id);
264  if (!identified)
265    return false;
266  if (tracker)
267    tracker->CopyFrom(*identified);
268  return true;
269}
270
271void MetadataDatabaseIndex::StoreFileMetadata(
272    scoped_ptr<FileMetadata> metadata) {
273  PutFileMetadataToDB(*metadata.get(), db_);
274  if (!metadata) {
275    NOTREACHED();
276    return;
277  }
278
279  std::string file_id = metadata->file_id();
280  metadata_by_id_.set(file_id, metadata.Pass());
281}
282
283void MetadataDatabaseIndex::StoreFileTracker(
284    scoped_ptr<FileTracker> tracker) {
285  PutFileTrackerToDB(*tracker.get(), db_);
286  if (!tracker) {
287    NOTREACHED();
288    return;
289  }
290
291  int64 tracker_id = tracker->tracker_id();
292  FileTracker* old_tracker = tracker_by_id_.get(tracker_id);
293
294  if (!old_tracker) {
295    DVLOG(3) << "Adding new tracker: " << tracker->tracker_id()
296             << " " << GetTrackerTitle(*tracker);
297
298    AddToAppIDIndex(*tracker);
299    AddToPathIndexes(*tracker);
300    AddToFileIDIndexes(*tracker);
301    AddToDirtyTrackerIndexes(*tracker);
302  } else {
303    DVLOG(3) << "Updating tracker: " << tracker->tracker_id()
304             << " " << GetTrackerTitle(*tracker);
305
306    UpdateInAppIDIndex(*old_tracker, *tracker);
307    UpdateInPathIndexes(*old_tracker, *tracker);
308    UpdateInFileIDIndexes(*old_tracker, *tracker);
309    UpdateInDirtyTrackerIndexes(*old_tracker, *tracker);
310  }
311
312  tracker_by_id_.set(tracker_id, tracker.Pass());
313}
314
315void MetadataDatabaseIndex::RemoveFileMetadata(const std::string& file_id) {
316  PutFileMetadataDeletionToDB(file_id, db_);
317  metadata_by_id_.erase(file_id);
318}
319
320void MetadataDatabaseIndex::RemoveFileTracker(int64 tracker_id) {
321  PutFileTrackerDeletionToDB(tracker_id, db_);
322
323  FileTracker* tracker = tracker_by_id_.get(tracker_id);
324  if (!tracker) {
325    NOTREACHED();
326    return;
327  }
328
329  DVLOG(3) << "Removing tracker: "
330           << tracker->tracker_id() << " " << GetTrackerTitle(*tracker);
331
332  RemoveFromAppIDIndex(*tracker);
333  RemoveFromPathIndexes(*tracker);
334  RemoveFromFileIDIndexes(*tracker);
335  RemoveFromDirtyTrackerIndexes(*tracker);
336
337  tracker_by_id_.erase(tracker_id);
338}
339
340TrackerIDSet MetadataDatabaseIndex::GetFileTrackerIDsByFileID(
341    const std::string& file_id) const {
342  return FindItem(trackers_by_file_id_, file_id);
343}
344
345int64 MetadataDatabaseIndex::GetAppRootTracker(
346    const std::string& app_id) const {
347  return FindItem(app_root_by_app_id_, app_id);
348}
349
350TrackerIDSet MetadataDatabaseIndex::GetFileTrackerIDsByParentAndTitle(
351    int64 parent_tracker_id,
352    const std::string& title) const {
353  TrackerIDsByParentAndTitle::const_iterator found =
354      trackers_by_parent_and_title_.find(parent_tracker_id);
355  if (found == trackers_by_parent_and_title_.end())
356    return TrackerIDSet();
357  return FindItem(found->second, title);
358}
359
360std::vector<int64> MetadataDatabaseIndex::GetFileTrackerIDsByParent(
361    int64 parent_tracker_id) const {
362  std::vector<int64> result;
363  TrackerIDsByParentAndTitle::const_iterator found =
364      trackers_by_parent_and_title_.find(parent_tracker_id);
365  if (found == trackers_by_parent_and_title_.end())
366    return result;
367
368  for (TrackerIDsByTitle::const_iterator itr = found->second.begin();
369       itr != found->second.end(); ++itr) {
370    result.insert(result.end(), itr->second.begin(), itr->second.end());
371  }
372
373  return result;
374}
375
376std::string MetadataDatabaseIndex::PickMultiTrackerFileID() const {
377  if (multi_tracker_file_ids_.empty())
378    return std::string();
379  return *multi_tracker_file_ids_.begin();
380}
381
382ParentIDAndTitle MetadataDatabaseIndex::PickMultiBackingFilePath() const {
383  if (multi_backing_file_paths_.empty())
384    return ParentIDAndTitle(kInvalidTrackerID, std::string());
385  return *multi_backing_file_paths_.begin();
386}
387
388int64 MetadataDatabaseIndex::PickDirtyTracker() const {
389  if (dirty_trackers_.empty())
390    return kInvalidTrackerID;
391  return *dirty_trackers_.begin();
392}
393
394void MetadataDatabaseIndex::DemoteDirtyTracker(int64 tracker_id) {
395  if (dirty_trackers_.erase(tracker_id))
396    demoted_dirty_trackers_.insert(tracker_id);
397}
398
399bool MetadataDatabaseIndex::HasDemotedDirtyTracker() const {
400  return !demoted_dirty_trackers_.empty();
401}
402
403void MetadataDatabaseIndex::PromoteDemotedDirtyTracker(int64 tracker_id) {
404  if (demoted_dirty_trackers_.erase(tracker_id) == 1)
405    dirty_trackers_.insert(tracker_id);
406}
407
408bool MetadataDatabaseIndex::PromoteDemotedDirtyTrackers() {
409  bool promoted = !demoted_dirty_trackers_.empty();
410  dirty_trackers_.insert(demoted_dirty_trackers_.begin(),
411                         demoted_dirty_trackers_.end());
412  demoted_dirty_trackers_.clear();
413  return promoted;
414}
415
416size_t MetadataDatabaseIndex::CountDirtyTracker() const {
417  return dirty_trackers_.size() + demoted_dirty_trackers_.size();
418}
419
420size_t MetadataDatabaseIndex::CountFileMetadata() const {
421  return metadata_by_id_.size();
422}
423
424size_t MetadataDatabaseIndex::CountFileTracker() const {
425  return tracker_by_id_.size();
426}
427
428void MetadataDatabaseIndex::SetSyncRootTrackerID(
429    int64 sync_root_id) const {
430  service_metadata_->set_sync_root_tracker_id(sync_root_id);
431  PutServiceMetadataToDB(*service_metadata_, db_);
432}
433
434void MetadataDatabaseIndex::SetLargestChangeID(
435    int64 largest_change_id) const {
436  service_metadata_->set_largest_change_id(largest_change_id);
437  PutServiceMetadataToDB(*service_metadata_, db_);
438}
439
440void MetadataDatabaseIndex::SetNextTrackerID(
441    int64 next_tracker_id) const {
442  service_metadata_->set_next_tracker_id(next_tracker_id);
443  PutServiceMetadataToDB(*service_metadata_, db_);
444}
445
446int64 MetadataDatabaseIndex::GetSyncRootTrackerID() const {
447  if (!service_metadata_->has_sync_root_tracker_id())
448    return kInvalidTrackerID;
449  return service_metadata_->sync_root_tracker_id();
450}
451
452int64 MetadataDatabaseIndex::GetLargestChangeID() const {
453  if (!service_metadata_->has_largest_change_id())
454    return kInvalidTrackerID;
455  return service_metadata_->largest_change_id();
456}
457
458int64 MetadataDatabaseIndex::GetNextTrackerID() const {
459  if (!service_metadata_->has_next_tracker_id()) {
460    NOTREACHED();
461    return kInvalidTrackerID;
462  }
463  return service_metadata_->next_tracker_id();
464}
465
466std::vector<std::string> MetadataDatabaseIndex::GetRegisteredAppIDs() const {
467  std::vector<std::string> result;
468  result.reserve(app_root_by_app_id_.size());
469  for (TrackerIDByAppID::const_iterator itr = app_root_by_app_id_.begin();
470       itr != app_root_by_app_id_.end(); ++itr)
471    result.push_back(itr->first);
472  return result;
473}
474
475std::vector<int64> MetadataDatabaseIndex::GetAllTrackerIDs() const {
476  std::vector<int64> result;
477  for (TrackerByID::const_iterator itr = tracker_by_id_.begin();
478       itr != tracker_by_id_.end(); ++itr) {
479    result.push_back(itr->first);
480  }
481  return result;
482}
483
484std::vector<std::string> MetadataDatabaseIndex::GetAllMetadataIDs() const {
485  std::vector<std::string> result;
486  for (MetadataByID::const_iterator itr = metadata_by_id_.begin();
487       itr != metadata_by_id_.end(); ++itr) {
488    result.push_back(itr->first);
489  }
490  return result;
491}
492
493void MetadataDatabaseIndex::AddToAppIDIndex(
494    const FileTracker& new_tracker) {
495  if (!IsAppRoot(new_tracker))
496    return;
497
498  DVLOG(3) << "  Add to app_root_by_app_id_: " << new_tracker.app_id();
499
500  DCHECK(new_tracker.active());
501  DCHECK(!ContainsKey(app_root_by_app_id_, new_tracker.app_id()));
502  app_root_by_app_id_[new_tracker.app_id()] = new_tracker.tracker_id();
503}
504
505void MetadataDatabaseIndex::UpdateInAppIDIndex(
506    const FileTracker& old_tracker,
507    const FileTracker& new_tracker) {
508  DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
509
510  if (IsAppRoot(old_tracker) && !IsAppRoot(new_tracker)) {
511    DCHECK(old_tracker.active());
512    DCHECK(!new_tracker.active());
513    DCHECK(ContainsKey(app_root_by_app_id_, old_tracker.app_id()));
514
515    DVLOG(3) << "  Remove from app_root_by_app_id_: " << old_tracker.app_id();
516
517    app_root_by_app_id_.erase(old_tracker.app_id());
518  } else if (!IsAppRoot(old_tracker) && IsAppRoot(new_tracker)) {
519    DCHECK(!old_tracker.active());
520    DCHECK(new_tracker.active());
521    DCHECK(!ContainsKey(app_root_by_app_id_, new_tracker.app_id()));
522
523    DVLOG(3) << "  Add to app_root_by_app_id_: " << new_tracker.app_id();
524
525    app_root_by_app_id_[new_tracker.app_id()] = new_tracker.tracker_id();
526  }
527}
528
529void MetadataDatabaseIndex::RemoveFromAppIDIndex(
530    const FileTracker& tracker) {
531  if (IsAppRoot(tracker)) {
532    DCHECK(tracker.active());
533    DCHECK(ContainsKey(app_root_by_app_id_, tracker.app_id()));
534
535    DVLOG(3) << "  Remove from app_root_by_app_id_: " << tracker.app_id();
536
537    app_root_by_app_id_.erase(tracker.app_id());
538  }
539}
540
541void MetadataDatabaseIndex::AddToFileIDIndexes(
542    const FileTracker& new_tracker) {
543  DVLOG(3) << "  Add to trackers_by_file_id_: " << new_tracker.file_id();
544
545  trackers_by_file_id_[new_tracker.file_id()].Insert(new_tracker);
546
547  if (trackers_by_file_id_[new_tracker.file_id()].size() > 1) {
548    DVLOG_IF(3, !ContainsKey(multi_tracker_file_ids_, new_tracker.file_id()))
549        << "  Add to multi_tracker_file_ids_: " << new_tracker.file_id();
550    multi_tracker_file_ids_.insert(new_tracker.file_id());
551  }
552}
553
554void MetadataDatabaseIndex::UpdateInFileIDIndexes(
555    const FileTracker& old_tracker,
556    const FileTracker& new_tracker) {
557  DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
558  DCHECK_EQ(old_tracker.file_id(), new_tracker.file_id());
559
560  std::string file_id = new_tracker.file_id();
561  DCHECK(ContainsKey(trackers_by_file_id_, file_id));
562
563  if (old_tracker.active() && !new_tracker.active())
564    trackers_by_file_id_[file_id].Deactivate(new_tracker.tracker_id());
565  else if (!old_tracker.active() && new_tracker.active())
566    trackers_by_file_id_[file_id].Activate(new_tracker.tracker_id());
567}
568
569void MetadataDatabaseIndex::RemoveFromFileIDIndexes(
570    const FileTracker& tracker) {
571  TrackerIDsByFileID::iterator found =
572      trackers_by_file_id_.find(tracker.file_id());
573  if (found == trackers_by_file_id_.end()) {
574    NOTREACHED();
575    return;
576  }
577
578  DVLOG(3) << "  Remove from trackers_by_file_id_: "
579           << tracker.tracker_id();
580  found->second.Erase(tracker.tracker_id());
581
582  if (trackers_by_file_id_[tracker.file_id()].size() <= 1) {
583    DVLOG_IF(3, ContainsKey(multi_tracker_file_ids_, tracker.file_id()))
584        << "  Remove from multi_tracker_file_ids_: " << tracker.file_id();
585    multi_tracker_file_ids_.erase(tracker.file_id());
586  }
587
588  if (found->second.empty())
589    trackers_by_file_id_.erase(found);
590}
591
592void MetadataDatabaseIndex::AddToPathIndexes(
593    const FileTracker& new_tracker) {
594  int64 parent = new_tracker.parent_tracker_id();
595  std::string title = GetTrackerTitle(new_tracker);
596
597  DVLOG(3) << "  Add to trackers_by_parent_and_title_: "
598           << parent << " " << title;
599
600  trackers_by_parent_and_title_[parent][title].Insert(new_tracker);
601
602  if (trackers_by_parent_and_title_[parent][title].size() > 1 &&
603      !title.empty()) {
604    DVLOG_IF(3, !ContainsKey(multi_backing_file_paths_,
605                             ParentIDAndTitle(parent, title)))
606        << "  Add to multi_backing_file_paths_: " << parent << " " << title;
607    multi_backing_file_paths_.insert(ParentIDAndTitle(parent, title));
608  }
609}
610
611void MetadataDatabaseIndex::UpdateInPathIndexes(
612    const FileTracker& old_tracker,
613    const FileTracker& new_tracker) {
614  DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
615  DCHECK_EQ(old_tracker.parent_tracker_id(), new_tracker.parent_tracker_id());
616  DCHECK(GetTrackerTitle(old_tracker) == GetTrackerTitle(new_tracker) ||
617         !old_tracker.has_synced_details());
618
619  int64 tracker_id = new_tracker.tracker_id();
620  int64 parent = new_tracker.parent_tracker_id();
621  std::string old_title = GetTrackerTitle(old_tracker);
622  std::string title = GetTrackerTitle(new_tracker);
623
624  TrackerIDsByTitle* trackers_by_title = &trackers_by_parent_and_title_[parent];
625
626  if (old_title != title) {
627    TrackerIDsByTitle::iterator found = trackers_by_title->find(old_title);
628    if (found != trackers_by_title->end()) {
629      DVLOG(3) << "  Remove from trackers_by_parent_and_title_: "
630             << parent << " " << old_title;
631
632      found->second.Erase(tracker_id);
633      if (found->second.empty())
634        trackers_by_title->erase(found);
635    } else {
636      NOTREACHED();
637    }
638
639    DVLOG(3) << "  Add to trackers_by_parent_and_title_: "
640             << parent << " " << title;
641
642    (*trackers_by_title)[title].Insert(new_tracker);
643
644    if (trackers_by_parent_and_title_[parent][old_title].size() <= 1 &&
645        !old_title.empty()) {
646      DVLOG_IF(3, ContainsKey(multi_backing_file_paths_,
647                              ParentIDAndTitle(parent, old_title)))
648          << "  Remove from multi_backing_file_paths_: "
649          << parent << " " << old_title;
650      multi_backing_file_paths_.erase(ParentIDAndTitle(parent, old_title));
651    }
652
653    if (trackers_by_parent_and_title_[parent][title].size() > 1 &&
654        !title.empty()) {
655      DVLOG_IF(3, !ContainsKey(multi_backing_file_paths_,
656                               ParentIDAndTitle(parent, title)))
657          << "  Add to multi_backing_file_paths_: " << parent << " " << title;
658      multi_backing_file_paths_.insert(ParentIDAndTitle(parent, title));
659    }
660
661    return;
662  }
663
664  if (old_tracker.active() && !new_tracker.active())
665    trackers_by_parent_and_title_[parent][title].Deactivate(tracker_id);
666  else if (!old_tracker.active() && new_tracker.active())
667    trackers_by_parent_and_title_[parent][title].Activate(tracker_id);
668}
669
670void MetadataDatabaseIndex::RemoveFromPathIndexes(
671    const FileTracker& tracker) {
672  int64 tracker_id = tracker.tracker_id();
673  int64 parent = tracker.parent_tracker_id();
674  std::string title = GetTrackerTitle(tracker);
675
676  DCHECK(ContainsKey(trackers_by_parent_and_title_, parent));
677  DCHECK(ContainsKey(trackers_by_parent_and_title_[parent], title));
678
679  DVLOG(3) << "  Remove from trackers_by_parent_and_title_: "
680           << parent << " " << title;
681
682  trackers_by_parent_and_title_[parent][title].Erase(tracker_id);
683
684  if (trackers_by_parent_and_title_[parent][title].size() <= 1 &&
685      !title.empty()) {
686    DVLOG_IF(3, ContainsKey(multi_backing_file_paths_,
687                            ParentIDAndTitle(parent, title)))
688        << "  Remove from multi_backing_file_paths_: "
689        << parent << " " << title;
690    multi_backing_file_paths_.erase(ParentIDAndTitle(parent, title));
691  }
692
693  if (trackers_by_parent_and_title_[parent][title].empty()) {
694    trackers_by_parent_and_title_[parent].erase(title);
695    if (trackers_by_parent_and_title_[parent].empty())
696      trackers_by_parent_and_title_.erase(parent);
697  }
698}
699
700void MetadataDatabaseIndex::AddToDirtyTrackerIndexes(
701    const FileTracker& new_tracker) {
702  DCHECK(!ContainsKey(dirty_trackers_, new_tracker.tracker_id()));
703  DCHECK(!ContainsKey(demoted_dirty_trackers_, new_tracker.tracker_id()));
704
705  if (new_tracker.dirty()) {
706    DVLOG(3) << "  Add to dirty_trackers_: " << new_tracker.tracker_id();
707    dirty_trackers_.insert(new_tracker.tracker_id());
708  }
709}
710
711void MetadataDatabaseIndex::UpdateInDirtyTrackerIndexes(
712    const FileTracker& old_tracker,
713    const FileTracker& new_tracker) {
714  DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
715
716  int64 tracker_id = new_tracker.tracker_id();
717  if (old_tracker.dirty() && !new_tracker.dirty()) {
718    DCHECK(ContainsKey(dirty_trackers_, tracker_id) ||
719           ContainsKey(demoted_dirty_trackers_, tracker_id));
720
721    DVLOG(3) << "  Remove from dirty_trackers_: " << tracker_id;
722
723    dirty_trackers_.erase(tracker_id);
724    demoted_dirty_trackers_.erase(tracker_id);
725  } else if (!old_tracker.dirty() && new_tracker.dirty()) {
726    DCHECK(!ContainsKey(dirty_trackers_, tracker_id));
727    DCHECK(!ContainsKey(demoted_dirty_trackers_, tracker_id));
728
729    DVLOG(3) << "  Add to dirty_trackers_: " << tracker_id;
730
731    dirty_trackers_.insert(tracker_id);
732  }
733}
734
735void MetadataDatabaseIndex::RemoveFromDirtyTrackerIndexes(
736    const FileTracker& tracker) {
737  if (tracker.dirty()) {
738    int64 tracker_id = tracker.tracker_id();
739    DCHECK(ContainsKey(dirty_trackers_, tracker_id) ||
740           ContainsKey(demoted_dirty_trackers_, tracker_id));
741
742    DVLOG(3) << "  Remove from dirty_trackers_: " << tracker_id;
743    dirty_trackers_.erase(tracker_id);
744
745    demoted_dirty_trackers_.erase(tracker_id);
746  }
747}
748
749}  // namespace drive_backend
750}  // namespace sync_file_system
751