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