metadata_database_index.cc revision 6d86b77056ed63eb6871182f42a9fd5f07550f90
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
12namespace sync_file_system {
13namespace drive_backend {
14
15ParentIDAndTitle::ParentIDAndTitle() : parent_id(0) {}
16ParentIDAndTitle::ParentIDAndTitle(int64 parent_id,
17                                   const std::string& title)
18    : parent_id(parent_id), title(title) {}
19
20bool operator==(const ParentIDAndTitle& left, const ParentIDAndTitle& right) {
21  return left.parent_id == right.parent_id && left.title == right.title;
22}
23
24bool 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
30namespace {
31
32template <typename Container>
33typename 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
42bool IsAppRoot(const FileTracker& tracker) {
43  return tracker.tracker_kind() == TRACKER_KIND_APP_ROOT ||
44      tracker.tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT;
45}
46
47std::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
55MetadataDatabaseIndex::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
70MetadataDatabaseIndex::~MetadataDatabaseIndex() {}
71
72const FileTracker* MetadataDatabaseIndex::GetFileTracker(
73    int64 tracker_id) const {
74  return tracker_by_id_.get(tracker_id);
75}
76
77const FileMetadata* MetadataDatabaseIndex::GetFileMetadata(
78    const std::string& file_id) const {
79  return metadata_by_id_.get(file_id);
80}
81
82void 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
93void 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
123void MetadataDatabaseIndex::RemoveFileMetadata(const std::string& file_id) {
124  metadata_by_id_.erase(file_id);
125}
126
127void 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
145TrackerIDSet MetadataDatabaseIndex::GetFileTrackerIDsByFileID(
146    const std::string& file_id) const {
147  return FindItem(trackers_by_file_id_, file_id);
148}
149
150int64 MetadataDatabaseIndex::GetAppRootTracker(
151    const std::string& app_id) const {
152  return FindItem(app_root_by_app_id_, app_id);
153}
154
155TrackerIDSet 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
165std::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
181std::string MetadataDatabaseIndex::PickMultiTrackerFileID() const {
182  if (multi_tracker_file_ids_.empty())
183    return std::string();
184  return *multi_tracker_file_ids_.begin();
185}
186
187ParentIDAndTitle 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
193int64 MetadataDatabaseIndex::PickDirtyTracker() const {
194  if (dirty_trackers_.empty())
195    return kInvalidTrackerID;
196  return *dirty_trackers_.begin();
197}
198
199void MetadataDatabaseIndex::DemoteDirtyTracker(int64 tracker_id) {
200  if (dirty_trackers_.erase(tracker_id))
201    demoted_dirty_trackers_.insert(tracker_id);
202}
203
204bool MetadataDatabaseIndex::HasDemotedDirtyTracker() const {
205  return !demoted_dirty_trackers_.empty();
206}
207
208void MetadataDatabaseIndex::PromoteDemotedDirtyTrackers() {
209  dirty_trackers_.insert(demoted_dirty_trackers_.begin(),
210                         demoted_dirty_trackers_.end());
211  demoted_dirty_trackers_.clear();
212}
213
214size_t MetadataDatabaseIndex::CountDirtyTracker() const {
215  return dirty_trackers_.size() + demoted_dirty_trackers_.size();
216}
217
218size_t MetadataDatabaseIndex::CountFileMetadata() const {
219  return metadata_by_id_.size();
220}
221
222size_t MetadataDatabaseIndex::CountFileTracker() const {
223  return tracker_by_id_.size();
224}
225
226std::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
235std::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
244std::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
253void 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
265void 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
289void 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
301void 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
314void 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
329void 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
352void 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
371void 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
430void 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
460void 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
471void 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
495void 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