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.
5 #include "chrome/browser/sync_file_system/drive_backend/metadata_database_index_on_disk.h"
7 #include "base/format_macros.h"
8 #include "base/logging.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_util.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.pb.h"
15 #include "chrome/browser/sync_file_system/logger.h"
16 #include "third_party/leveldatabase/src/include/leveldb/status.h"
18 // LevelDB database schema
19 // =======================
22 // - Entries are sorted by keys.
23 // - int64 value is serialized as a string by base::Int64ToString().
24 // - ServiceMetadata, FileMetadata, and FileTracker values are serialized
25 // as a string by SerializeToString() of protocol buffers.
28 // # Version of this schema
32 // # Metadata of the SyncFS service (compatible with version 3)
34 // value: <ServiceMetadata 'service_metadata'>
36 // # Metadata of remote files (compatible with version 3)
37 // key: "FILE: " + <string 'file_id'>
38 // value: <FileMetadata 'metadata'>
40 // # Trackers of remote file updates (compatible with version 3)
41 // key: "TRACKER: " + <int64 'tracker_id'>
42 // value: <FileTracker 'tracker'>
44 // # Index from App ID to the tracker ID
45 // key: "APP_ROOT: " + <string 'app_id'>
46 // value: <int64 'app_root_tracker_id'>
48 // # Index from file ID to the active tracker ID
49 // key: "ACTIVE_FILE: " + <string 'file_id'>
50 // value: <int64 'active_tracker_id'>
52 // # Index from file ID to a tracker ID
53 // key: "TRACKER_FILE: " + <string 'file_id'> + '\x00' + <int64 'tracker_id'>
56 // # Tracker IDs; a file metadata linked to multiple tracker IDs.
57 // key: "MULTI_FILE: " + <int64 'tracker_id'>
60 // # Index from the parent tracker ID and the title to the active tracker ID
61 // key: "ACTIVE_PATH: " + <int64 'parent_tracker_id'> +
62 // '\x00' + <string 'title'>
63 // value: <int64 'active_tracker_id'>
65 // # Index from the parent tracker ID and the title to a tracker ID
66 // key: "TRACKER_PATH: " + <int64 'parent_tracker_id'> +
67 // '\x00' + <string 'title'> + '\x00' + <int64 'tracker_id'>
70 // # Tracker IDs; a parent tracker ID and a title figure multiple tracker IDs
71 // key: "MULTI_PATH: " + <int64 'tracker_id'>
74 // # Dirty tracker IDs
75 // key: "DIRTY: " + <int64 'dirty_tracker_id'>
78 // # Demoted dirty tracker IDs
79 // key: "DEMOTED_DIRTY: " + <int64 'demoted_dirty_tracker_id'>
82 // # Timestamp when the last validation ran
84 // value: <time_t 'last_valid_time'>
86 namespace sync_file_system {
87 namespace drive_backend {
91 std::string GenerateAppRootIDByAppIDKey(const std::string& app_id) {
92 return kAppRootIDByAppIDKeyPrefix + app_id;
95 std::string GenerateActiveTrackerIDByFileIDKey(const std::string& file_id) {
96 return kActiveTrackerIDByFileIDKeyPrefix + file_id;
99 std::string GenerateTrackerIDByFileIDKeyPrefix(const std::string& file_id) {
100 std::ostringstream oss;
101 oss << kTrackerIDByFileIDKeyPrefix << file_id << '\0';
105 std::string GenerateMultiTrackerKey(const std::string& file_id) {
106 return kMultiTrackerByFileIDKeyPrefix + file_id;
109 std::string GenerateActiveTrackerIDByParentAndTitleKey(
110 int64 parent_id, const std::string& title) {
111 std::ostringstream oss;
112 oss << kActiveTrackerIDByParentAndTitleKeyPrefix << parent_id
117 std::string GenerateTrackerIDByParentAndTitleKeyPrefix(
118 int64 parent_id, const std::string& title) {
119 std::ostringstream oss;
120 oss << kTrackerIDByParentAndTitleKeyPrefix << parent_id << '\0'
125 std::string GenerateTrackerIDsByParentIDKeyPrefix(int64 parent_id) {
126 std::ostringstream oss;
127 oss << kTrackerIDByParentAndTitleKeyPrefix << parent_id << '\0';
131 std::string GenerateMultiBackingParentAndTitleKey(
132 int64 parent_id, const std::string& title) {
133 std::ostringstream oss;
134 oss << kMultiBackingParentAndTitleKeyPrefix << parent_id << '\0'
139 std::string GenerateDirtyIDKey(int64 tracker_id) {
140 return kDirtyIDKeyPrefix + base::Int64ToString(tracker_id);
143 std::string GenerateDemotedDirtyIDKey(int64 tracker_id) {
144 return kDemotedDirtyIDKeyPrefix + base::Int64ToString(tracker_id);
147 void RemoveUnreachableItems(LevelDBWrapper* db, int64 sync_root_tracker_id) {
150 typedef std::map<int64, std::set<int64> > ChildTrackersByParent;
151 ChildTrackersByParent trackers_by_parent;
153 // Set up links from parent tracker to child trackers.
154 std::set<int64> inactive_trackers;
155 scoped_ptr<LevelDBWrapper::Iterator> itr = db->NewIterator();
156 for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) {
157 if (!RemovePrefix(itr->key().ToString(), kFileTrackerKeyPrefix, NULL))
160 scoped_ptr<FileTracker> tracker(new FileTracker);
161 if (!tracker->ParseFromString(itr->value().ToString())) {
162 util::Log(logging::LOG_WARNING, FROM_HERE,
163 "Failed to parse a Tracker");
167 int64 parent_tracker_id = tracker->parent_tracker_id();
168 int64 tracker_id = tracker->tracker_id();
169 trackers_by_parent[parent_tracker_id].insert(tracker_id);
170 if (!tracker->active())
171 inactive_trackers.insert(tracker_id);
174 // Drop links from inactive trackers.
175 for (std::set<int64>::iterator iter = inactive_trackers.begin();
176 iter != inactive_trackers.end(); ++iter) {
177 trackers_by_parent.erase(*iter);
181 // Traverse tracker tree from sync-root.
182 std::set<int64> visited_trackers;
184 std::vector<int64> pending;
185 if (sync_root_tracker_id != kInvalidTrackerID)
186 pending.push_back(sync_root_tracker_id);
188 while (!pending.empty()) {
189 int64 tracker_id = pending.back();
190 DCHECK_NE(kInvalidTrackerID, tracker_id);
193 if (!visited_trackers.insert(tracker_id).second) {
199 LookUpMap(trackers_by_parent, tracker_id, std::set<int64>()),
204 // Delete all unreachable trackers, and list all |file_id| referred by
205 // remained trackers.
206 base::hash_set<std::string> referred_file_ids;
208 scoped_ptr<LevelDBWrapper::Iterator> itr = db->NewIterator();
209 for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) {
210 if (!RemovePrefix(itr->key().ToString(), kFileTrackerKeyPrefix, NULL))
213 scoped_ptr<FileTracker> tracker(new FileTracker);
214 if (!tracker->ParseFromString(itr->value().ToString())) {
215 util::Log(logging::LOG_WARNING, FROM_HERE,
216 "Failed to parse a Tracker");
220 if (ContainsKey(visited_trackers, tracker->tracker_id())) {
221 referred_file_ids.insert(tracker->file_id());
223 PutFileTrackerDeletionToDB(tracker->tracker_id(), db);
228 // Delete all unreferred metadata.
230 scoped_ptr<LevelDBWrapper::Iterator> itr = db->NewIterator();
231 for (itr->Seek(kFileMetadataKeyPrefix); itr->Valid(); itr->Next()) {
232 if (!RemovePrefix(itr->key().ToString(), kFileMetadataKeyPrefix, NULL))
235 scoped_ptr<FileMetadata> metadata(new FileMetadata);
236 if (!metadata->ParseFromString(itr->value().ToString())) {
237 util::Log(logging::LOG_WARNING, FROM_HERE,
238 "Failed to parse a Tracker");
242 if (!ContainsKey(referred_file_ids, metadata->file_id()))
243 PutFileMetadataDeletionToDB(metadata->file_id(), db);
251 scoped_ptr<MetadataDatabaseIndexOnDisk>
252 MetadataDatabaseIndexOnDisk::Create(LevelDBWrapper* db) {
255 scoped_ptr<ServiceMetadata> service_metadata = InitializeServiceMetadata(db);
256 if (!service_metadata)
257 return scoped_ptr<MetadataDatabaseIndexOnDisk>();
259 PutVersionToDB(kDatabaseOnDiskVersion, db);
260 RemoveUnreachableItems(db, service_metadata->sync_root_tracker_id());
261 scoped_ptr<MetadataDatabaseIndexOnDisk>
262 index(new MetadataDatabaseIndexOnDisk(db));
267 MetadataDatabaseIndexOnDisk::~MetadataDatabaseIndexOnDisk() {}
269 bool MetadataDatabaseIndexOnDisk::GetFileMetadata(
270 const std::string& file_id, FileMetadata* metadata) const {
271 const std::string key = kFileMetadataKeyPrefix + file_id;
273 leveldb::Status status = db_->Get(key, &value);
275 if (status.IsNotFound())
279 util::Log(logging::LOG_WARNING, FROM_HERE,
280 "LevelDB error (%s) in getting FileMetadata for ID: %s",
281 status.ToString().c_str(),
286 FileMetadata tmp_metadata;
287 if (!tmp_metadata.ParseFromString(value)) {
288 util::Log(logging::LOG_WARNING, FROM_HERE,
289 "Failed to parse a FileMetadata for ID: %s",
294 metadata->CopyFrom(tmp_metadata);
299 bool MetadataDatabaseIndexOnDisk::GetFileTracker(
300 int64 tracker_id, FileTracker* tracker) const {
301 const std::string key =
302 kFileTrackerKeyPrefix + base::Int64ToString(tracker_id);
304 leveldb::Status status = db_->Get(key, &value);
306 if (status.IsNotFound())
310 util::Log(logging::LOG_WARNING, FROM_HERE,
311 "LevelDB error (%s) in getting FileTracker for ID: %" PRId64,
312 status.ToString().c_str(),
317 FileTracker tmp_tracker;
318 if (!tmp_tracker.ParseFromString(value)) {
319 util::Log(logging::LOG_WARNING, FROM_HERE,
320 "Failed to parse a Tracker for ID: %" PRId64,
325 tracker->CopyFrom(tmp_tracker);
330 void MetadataDatabaseIndexOnDisk::StoreFileMetadata(
331 scoped_ptr<FileMetadata> metadata) {
333 PutFileMetadataToDB(*metadata, db_);
336 void MetadataDatabaseIndexOnDisk::StoreFileTracker(
337 scoped_ptr<FileTracker> tracker) {
340 int64 tracker_id = tracker->tracker_id();
341 FileTracker old_tracker;
342 if (!GetFileTracker(tracker_id, &old_tracker)) {
343 DVLOG(3) << "Adding new tracker: " << tracker->tracker_id()
344 << " " << GetTrackerTitle(*tracker);
345 AddToAppIDIndex(*tracker);
346 AddToFileIDIndexes(*tracker);
347 AddToPathIndexes(*tracker);
348 AddToDirtyTrackerIndexes(*tracker);
350 DVLOG(3) << "Updating tracker: " << tracker->tracker_id()
351 << " " << GetTrackerTitle(*tracker);
352 UpdateInAppIDIndex(old_tracker, *tracker);
353 UpdateInFileIDIndexes(old_tracker, *tracker);
354 UpdateInPathIndexes(old_tracker, *tracker);
355 UpdateInDirtyTrackerIndexes(old_tracker, *tracker);
359 PutFileTrackerToDB(*tracker, db_);
362 void MetadataDatabaseIndexOnDisk::RemoveFileMetadata(
363 const std::string& file_id) {
364 PutFileMetadataDeletionToDB(file_id, db_);
367 void MetadataDatabaseIndexOnDisk::RemoveFileTracker(int64 tracker_id) {
369 if (!GetFileTracker(tracker_id, &tracker)) {
374 DVLOG(1) << "Removing tracker: "
375 << tracker.tracker_id() << " " << GetTrackerTitle(tracker);
376 RemoveFromAppIDIndex(tracker);
377 RemoveFromFileIDIndexes(tracker);
378 RemoveFromPathIndexes(tracker);
379 RemoveFromDirtyTrackerIndexes(tracker);
381 PutFileTrackerDeletionToDB(tracker_id, db_);
384 TrackerIDSet MetadataDatabaseIndexOnDisk::GetFileTrackerIDsByFileID(
385 const std::string& file_id) const {
386 return GetTrackerIDSetByPrefix(
387 GenerateActiveTrackerIDByFileIDKey(file_id),
388 GenerateTrackerIDByFileIDKeyPrefix(file_id));
391 int64 MetadataDatabaseIndexOnDisk::GetAppRootTracker(
392 const std::string& app_id) const {
393 const std::string key = GenerateAppRootIDByAppIDKey(app_id);
395 leveldb::Status status = db_->Get(key, &value);
397 if (status.IsNotFound())
398 return kInvalidTrackerID;
401 util::Log(logging::LOG_WARNING, FROM_HERE,
402 "LevelDB error (%s) in getting AppRoot for AppID: %s",
403 status.ToString().c_str(),
405 return kInvalidTrackerID;
409 if (!base::StringToInt64(value, &root_id)) {
410 util::Log(logging::LOG_WARNING, FROM_HERE,
411 "Failed to parse a root ID (%s) for an App ID: %s",
414 return kInvalidTrackerID;
420 TrackerIDSet MetadataDatabaseIndexOnDisk::GetFileTrackerIDsByParentAndTitle(
421 int64 parent_tracker_id, const std::string& title) const {
422 return GetTrackerIDSetByPrefix(
423 GenerateActiveTrackerIDByParentAndTitleKey(parent_tracker_id, title),
424 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_tracker_id, title));
427 std::vector<int64> MetadataDatabaseIndexOnDisk::GetFileTrackerIDsByParent(
428 int64 parent_id) const {
429 std::vector<int64> result;
431 const std::string prefix = GenerateTrackerIDsByParentIDKeyPrefix(parent_id);
432 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
433 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
434 const std::string& key(itr->key().ToString());
435 std::string title_and_id;
436 if (!RemovePrefix(key, prefix, &title_and_id))
439 size_t pos = title_and_id.rfind('\0');
440 DCHECK(pos != std::string::npos);
443 if (!base::StringToInt64(title_and_id.substr(pos + 1), &tracker_id))
445 result.push_back(tracker_id);
450 std::string MetadataDatabaseIndexOnDisk::PickMultiTrackerFileID() const {
451 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
452 itr->Seek(kMultiTrackerByFileIDKeyPrefix);
454 return std::string();
457 if (!RemovePrefix(itr->key().ToString(),
458 kMultiTrackerByFileIDKeyPrefix, &file_id))
459 return std::string();
464 ParentIDAndTitle MetadataDatabaseIndexOnDisk::PickMultiBackingFilePath() const {
465 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
466 itr->Seek(kMultiBackingParentAndTitleKeyPrefix);
468 return ParentIDAndTitle();
471 if (!RemovePrefix(itr->key().ToString(),
472 kMultiBackingParentAndTitleKeyPrefix, &value))
473 return ParentIDAndTitle();
475 size_t pos = value.find('\0'); // '\0' is a separator.
476 if (pos == std::string::npos)
477 return ParentIDAndTitle();
480 return base::StringToInt64(value.substr(0, pos), &parent_id) ?
481 ParentIDAndTitle(parent_id, value.substr(pos + 1)) : ParentIDAndTitle();
484 int64 MetadataDatabaseIndexOnDisk::PickDirtyTracker() const {
485 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
486 itr->Seek(kDirtyIDKeyPrefix);
488 return kInvalidTrackerID;
491 if (!RemovePrefix(itr->key().ToString(), kDirtyIDKeyPrefix, &id_str))
492 return kInvalidTrackerID;
495 if (!base::StringToInt64(id_str, &tracker_id))
496 return kInvalidTrackerID;
501 void MetadataDatabaseIndexOnDisk::DemoteDirtyTracker(int64 tracker_id) {
502 const std::string key = GenerateDirtyIDKey(tracker_id);
505 leveldb::Status status = db_->Get(key, &value);
506 if (status.IsNotFound())
509 util::Log(logging::LOG_WARNING, FROM_HERE,
510 "LevelDB error (%s) in getting a dirty tracker for ID: %" PRId64,
511 status.ToString().c_str(),
517 db_->Put(GenerateDemotedDirtyIDKey(tracker_id), std::string());
518 --num_dirty_trackers_;
521 bool MetadataDatabaseIndexOnDisk::HasDemotedDirtyTracker() const {
522 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
523 itr->Seek(kDemotedDirtyIDKeyPrefix);
526 return StartsWithASCII(itr->key().ToString(), kDemotedDirtyIDKeyPrefix, true);
529 void MetadataDatabaseIndexOnDisk::PromoteDemotedDirtyTracker(int64 tracker_id) {
530 std::string demoted_key = GenerateDemotedDirtyIDKey(tracker_id);
533 if (db_->Get(demoted_key, &empty).ok()) {
534 db_->Delete(demoted_key);
535 db_->Put(GenerateDirtyIDKey(tracker_id), std::string());
536 ++num_dirty_trackers_;
540 bool MetadataDatabaseIndexOnDisk::PromoteDemotedDirtyTrackers() {
541 bool promoted = false;
542 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
543 for (itr->Seek(kDemotedDirtyIDKeyPrefix); itr->Valid(); itr->Next()) {
545 if (!RemovePrefix(itr->key().ToString(), kDemotedDirtyIDKeyPrefix, &id_str))
549 if (!base::StringToInt64(id_str, &tracker_id))
552 db_->Delete(itr->key().ToString());
553 db_->Put(GenerateDirtyIDKey(tracker_id), std::string());
554 ++num_dirty_trackers_;
560 size_t MetadataDatabaseIndexOnDisk::CountDirtyTracker() const {
561 return num_dirty_trackers_;
564 size_t MetadataDatabaseIndexOnDisk::CountFileMetadata() const {
565 // TODO(peria): Cache the number of FileMetadata in the DB.
567 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
568 for (itr->Seek(kFileMetadataKeyPrefix); itr->Valid(); itr->Next()) {
569 if (!StartsWithASCII(itr->key().ToString(), kFileMetadataKeyPrefix, true))
576 size_t MetadataDatabaseIndexOnDisk::CountFileTracker() const {
577 // TODO(peria): Cache the number of FileTracker in the DB.
579 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
580 for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) {
581 if (!StartsWithASCII(itr->key().ToString(), kFileTrackerKeyPrefix, true))
588 void MetadataDatabaseIndexOnDisk::SetSyncRootTrackerID(
589 int64 sync_root_id) const {
590 service_metadata_->set_sync_root_tracker_id(sync_root_id);
591 PutServiceMetadataToDB(*service_metadata_, db_);
594 void MetadataDatabaseIndexOnDisk::SetLargestChangeID(
595 int64 largest_change_id) const {
596 service_metadata_->set_largest_change_id(largest_change_id);
597 PutServiceMetadataToDB(*service_metadata_, db_);
600 void MetadataDatabaseIndexOnDisk::SetNextTrackerID(
601 int64 next_tracker_id) const {
602 service_metadata_->set_next_tracker_id(next_tracker_id);
603 PutServiceMetadataToDB(*service_metadata_, db_);
606 int64 MetadataDatabaseIndexOnDisk::GetSyncRootTrackerID() const {
607 if (!service_metadata_->has_sync_root_tracker_id())
608 return kInvalidTrackerID;
609 return service_metadata_->sync_root_tracker_id();
612 int64 MetadataDatabaseIndexOnDisk::GetLargestChangeID() const {
613 if (!service_metadata_->has_largest_change_id())
614 return kInvalidTrackerID;
615 return service_metadata_->largest_change_id();
618 int64 MetadataDatabaseIndexOnDisk::GetNextTrackerID() const {
619 if (!service_metadata_->has_next_tracker_id())
620 return kInvalidTrackerID;
621 return service_metadata_->next_tracker_id();
624 std::vector<std::string>
625 MetadataDatabaseIndexOnDisk::GetRegisteredAppIDs() const {
626 std::vector<std::string> result;
627 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
628 for (itr->Seek(kAppRootIDByAppIDKeyPrefix); itr->Valid(); itr->Next()) {
630 if (!RemovePrefix(itr->key().ToString(), kAppRootIDByAppIDKeyPrefix, &id))
632 result.push_back(id);
637 std::vector<int64> MetadataDatabaseIndexOnDisk::GetAllTrackerIDs() const {
638 std::vector<int64> tracker_ids;
639 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
640 for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) {
642 if (!RemovePrefix(itr->key().ToString(), kFileTrackerKeyPrefix, &id_str))
646 if (!base::StringToInt64(id_str, &tracker_id))
648 tracker_ids.push_back(tracker_id);
653 std::vector<std::string>
654 MetadataDatabaseIndexOnDisk::GetAllMetadataIDs() const {
655 std::vector<std::string> file_ids;
656 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
657 for (itr->Seek(kFileMetadataKeyPrefix); itr->Valid(); itr->Next()) {
659 if (!RemovePrefix(itr->key().ToString(), kFileMetadataKeyPrefix, &file_id))
661 file_ids.push_back(file_id);
666 int64 MetadataDatabaseIndexOnDisk::BuildTrackerIndexes() {
667 int64 num_puts_before = db_->num_puts();
669 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
670 for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) {
671 if (!RemovePrefix(itr->key().ToString(), kFileTrackerKeyPrefix, NULL))
675 if (!tracker.ParseFromString(itr->value().ToString())) {
676 util::Log(logging::LOG_WARNING, FROM_HERE,
677 "Failed to parse a Tracker");
681 AddToAppIDIndex(tracker);
682 AddToFileIDIndexes(tracker);
683 AddToPathIndexes(tracker);
684 AddToDirtyTrackerIndexes(tracker);
687 return db_->num_puts() - num_puts_before;
690 int64 MetadataDatabaseIndexOnDisk::DeleteTrackerIndexes() {
691 const char* kIndexPrefixes[] = {
692 kAppRootIDByAppIDKeyPrefix, kActiveTrackerIDByFileIDKeyPrefix,
693 kTrackerIDByFileIDKeyPrefix, kMultiTrackerByFileIDKeyPrefix,
694 kActiveTrackerIDByParentAndTitleKeyPrefix,
695 kTrackerIDByParentAndTitleKeyPrefix, kMultiBackingParentAndTitleKeyPrefix,
696 kDirtyIDKeyPrefix, kDemotedDirtyIDKeyPrefix
699 int64 num_deletes_before = db_->num_deletes();
700 for (size_t i = 0; i < arraysize(kIndexPrefixes); ++i)
701 DeleteKeyStartsWith(kIndexPrefixes[i]);
702 num_dirty_trackers_ = 0;
703 return db_->num_deletes() - num_deletes_before;
706 LevelDBWrapper* MetadataDatabaseIndexOnDisk::GetDBForTesting() {
710 MetadataDatabaseIndexOnDisk::MetadataDatabaseIndexOnDisk(LevelDBWrapper* db)
712 num_dirty_trackers_(0) {
713 // TODO(peria): Add UMA to measure the number of FileMetadata, FileTracker,
715 service_metadata_ = InitializeServiceMetadata(db_);
717 // Check if index is valid, if no validations run in 7 days.
718 const int64 kThresholdToValidateInDays = 7;
720 int64 last_check_time = 0;
722 if (db_->Get(kLastValidationTimeKey, &value).ok())
723 base::StringToInt64(value, &last_check_time);
724 base::TimeDelta since_last_check =
725 base::Time::Now() - base::Time::FromInternalValue(last_check_time);
726 int64 since_last_check_in_days = since_last_check.InDays();
727 if (since_last_check_in_days >= kThresholdToValidateInDays ||
728 since_last_check_in_days < 0) {
729 // TODO(peria): Add UMA to check if the number of deleted entries and the
730 // number of built entries are different or not.
731 DeleteTrackerIndexes();
732 BuildTrackerIndexes();
733 db_->Put(kLastValidationTimeKey,
734 base::Int64ToString(base::Time::Now().ToInternalValue()));
736 num_dirty_trackers_ = CountDirtyTrackerInternal();
740 void MetadataDatabaseIndexOnDisk::AddToAppIDIndex(const FileTracker& tracker) {
741 if (!IsAppRoot(tracker)) {
742 DVLOG(3) << " Tracker for " << tracker.file_id() << " is not an App root.";
746 DVLOG(1) << " Add to App root by App ID: " << tracker.app_id();
748 const std::string db_key = GenerateAppRootIDByAppIDKey(tracker.app_id());
749 DCHECK(tracker.active());
750 DCHECK(!DBHasKey(db_key));
751 db_->Put(db_key, base::Int64ToString(tracker.tracker_id()));
754 void MetadataDatabaseIndexOnDisk::UpdateInAppIDIndex(
755 const FileTracker& old_tracker,
756 const FileTracker& new_tracker) {
757 DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
759 if (IsAppRoot(old_tracker) && !IsAppRoot(new_tracker)) {
760 DCHECK(old_tracker.active());
761 DCHECK(!new_tracker.active());
762 const std::string key = GenerateAppRootIDByAppIDKey(old_tracker.app_id());
763 DCHECK(DBHasKey(key));
765 DVLOG(1) << " Remove from App root by App ID: " << old_tracker.app_id();
767 } else if (!IsAppRoot(old_tracker) && IsAppRoot(new_tracker)) {
768 DCHECK(!old_tracker.active());
769 DCHECK(new_tracker.active());
770 const std::string key = GenerateAppRootIDByAppIDKey(new_tracker.app_id());
771 DCHECK(!DBHasKey(key));
773 DVLOG(1) << " Add to App root by App ID: " << new_tracker.app_id();
774 db_->Put(key, base::Int64ToString(new_tracker.tracker_id()));
778 void MetadataDatabaseIndexOnDisk::RemoveFromAppIDIndex(
779 const FileTracker& tracker) {
780 if (!IsAppRoot(tracker)) {
781 DVLOG(3) << " Tracker for " << tracker.file_id() << " is not an App root.";
785 DCHECK(tracker.active());
786 const std::string key = GenerateAppRootIDByAppIDKey(tracker.app_id());
787 DCHECK(DBHasKey(key));
789 DVLOG(1) << " Remove from App root by App ID: " << tracker.app_id();
793 void MetadataDatabaseIndexOnDisk::AddToFileIDIndexes(
794 const FileTracker& new_tracker) {
795 const std::string& file_id = new_tracker.file_id();
797 DVLOG(1) << " Add to trackers by file ID: " << file_id;
798 const std::string prefix = GenerateTrackerIDByFileIDKeyPrefix(file_id);
799 AddToTrackerIDSetWithPrefix(
800 GenerateActiveTrackerIDByFileIDKey(file_id), prefix, new_tracker);
802 const std::string multi_tracker_key = GenerateMultiTrackerKey(file_id);
803 if (!DBHasKey(multi_tracker_key) &&
804 CountWithPrefix(prefix, new_tracker.tracker_id()) != NONE) {
805 DVLOG(1) << " Add to multi-tracker file IDs: " << file_id;
806 db_->Put(multi_tracker_key, std::string());
810 void MetadataDatabaseIndexOnDisk::UpdateInFileIDIndexes(
811 const FileTracker& old_tracker,
812 const FileTracker& new_tracker) {
813 DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
814 DCHECK_EQ(old_tracker.file_id(), new_tracker.file_id());
816 const std::string& file_id = new_tracker.file_id();
817 const std::string prefix = GenerateTrackerIDByFileIDKeyPrefix(file_id);
818 DCHECK(DBHasKey(prefix + base::Int64ToString(new_tracker.tracker_id())));
820 if (old_tracker.active() && !new_tracker.active()) {
821 DeactivateInTrackerIDSetWithPrefix(
822 GenerateActiveTrackerIDByFileIDKey(file_id), prefix,
823 new_tracker.tracker_id());
824 } else if (!old_tracker.active() && new_tracker.active()) {
825 ActivateInTrackerIDSetWithPrefix(
826 GenerateActiveTrackerIDByFileIDKey(file_id), prefix,
827 new_tracker.tracker_id());
831 void MetadataDatabaseIndexOnDisk::RemoveFromFileIDIndexes(
832 const FileTracker& tracker) {
833 const std::string& file_id = tracker.file_id();
834 const std::string prefix =
835 GenerateTrackerIDByFileIDKeyPrefix(file_id);
837 if (!EraseInTrackerIDSetWithPrefix(
838 GenerateActiveTrackerIDByFileIDKey(file_id),
839 prefix, tracker.tracker_id()))
842 DVLOG(1) << " Remove from trackers by file ID: " << tracker.tracker_id();
844 const std::string multi_key = GenerateMultiTrackerKey(file_id);
845 if (DBHasKey(multi_key) &&
846 CountWithPrefix(prefix, tracker.tracker_id()) != MULTIPLE) {
847 DVLOG(1) << " Remove from multi-tracker file IDs: " << file_id;
848 db_->Delete(multi_key);
852 void MetadataDatabaseIndexOnDisk::AddToPathIndexes(
853 const FileTracker& new_tracker) {
854 int64 parent_id = new_tracker.parent_tracker_id();
855 std::string title = GetTrackerTitle(new_tracker);
857 DVLOG(1) << " Add to trackers by parent and title: "
858 << parent_id << " " << title;
860 const std::string prefix =
861 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title);
862 if (!title.empty()) {
863 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
864 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
866 if (!RemovePrefix(itr->key().ToString(), prefix, &id_str))
870 if (!base::StringToInt64(id_str, &tracker_id))
872 if (tracker_id == new_tracker.tracker_id()) {
877 const std::string multi_key =
878 GenerateMultiBackingParentAndTitleKey(parent_id, title);
879 DVLOG_IF(1, !DBHasKey(multi_key))
880 << " Add to multi backing file paths: " << parent_id << " " << title;
881 db_->Put(multi_key, std::string());
886 AddToTrackerIDSetWithPrefix(
887 GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title),
888 prefix, new_tracker);
891 void MetadataDatabaseIndexOnDisk::UpdateInPathIndexes(
892 const FileTracker& old_tracker,
893 const FileTracker& new_tracker) {
894 DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
895 DCHECK_EQ(old_tracker.parent_tracker_id(), new_tracker.parent_tracker_id());
896 DCHECK(GetTrackerTitle(old_tracker) == GetTrackerTitle(new_tracker) ||
897 !old_tracker.has_synced_details());
899 int64 tracker_id = new_tracker.tracker_id();
900 int64 parent_id = new_tracker.parent_tracker_id();
901 const std::string old_title = GetTrackerTitle(old_tracker);
902 const std::string title = GetTrackerTitle(new_tracker);
904 if (old_title != title) {
905 const std::string old_prefix =
906 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, old_title);
907 EraseInTrackerIDSetWithPrefix(
908 GenerateActiveTrackerIDByParentAndTitleKey(parent_id, old_title),
909 old_prefix, tracker_id);
911 if (!old_title.empty() &&
912 CountWithPrefix(old_prefix, tracker_id) != MULTIPLE) {
913 const std::string old_multi_backing_key =
914 GenerateMultiBackingParentAndTitleKey(parent_id, old_title);
915 DVLOG_IF(1, DBHasKey(old_multi_backing_key))
916 << " Remove from multi backing file paths: "
917 << parent_id << " " << old_title;
918 db_->Delete(old_multi_backing_key);
921 DVLOG(1) << " Add to trackers by parent and title: "
922 << parent_id << " " << title;
924 const std::string prefix =
925 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title);
926 AddToTrackerIDSetWithPrefix(
927 GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title),
928 prefix, new_tracker);
930 if (!title.empty() && CountWithPrefix(prefix, tracker_id) != NONE) {
931 const std::string multi_backing_key =
932 GenerateMultiBackingParentAndTitleKey(parent_id, title);
933 DVLOG_IF(1, !DBHasKey(multi_backing_key))
934 << " Add to multi backing file_paths: "
935 << parent_id << " " << title;
936 db_->Put(multi_backing_key, std::string());
942 const std::string active_tracker_key =
943 GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title);
944 const std::string prefix =
945 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title);
946 if (old_tracker.active() && !new_tracker.active()) {
947 DeactivateInTrackerIDSetWithPrefix(
948 active_tracker_key, prefix, tracker_id);
949 } else if (!old_tracker.active() && new_tracker.active()) {
950 ActivateInTrackerIDSetWithPrefix(
951 active_tracker_key, prefix, tracker_id);
955 void MetadataDatabaseIndexOnDisk::RemoveFromPathIndexes(
956 const FileTracker& tracker) {
957 int64 tracker_id = tracker.tracker_id();
958 int64 parent_id = tracker.parent_tracker_id();
959 std::string title = GetTrackerTitle(tracker);
961 DVLOG(1) << " Remove from trackers by parent and title: "
962 << parent_id << " " << title;
964 const std::string active_tracker_key =
965 GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title);
966 const std::string key_prefix =
967 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title);
968 if (!EraseInTrackerIDSetWithPrefix(
969 active_tracker_key, key_prefix, tracker_id))
972 const std::string multi_key =
973 GenerateMultiBackingParentAndTitleKey(parent_id, title);
974 if (!title.empty() && DBHasKey(multi_key) &&
975 CountWithPrefix(key_prefix, tracker_id) != MULTIPLE) {
976 DVLOG(1) << " Remove from multi backing file paths: "
977 << parent_id << " " << title;
978 db_->Delete(multi_key);
982 void MetadataDatabaseIndexOnDisk::AddToDirtyTrackerIndexes(
983 const FileTracker& new_tracker) {
984 const std::string dirty_key = GenerateDirtyIDKey(new_tracker.tracker_id());
985 DCHECK(!DBHasKey(dirty_key));
986 DCHECK(!DBHasKey(GenerateDemotedDirtyIDKey(new_tracker.tracker_id())));
988 if (new_tracker.dirty()) {
989 DVLOG(1) << " Add to dirty tracker IDs: " << new_tracker.tracker_id();
990 db_->Put(dirty_key, std::string());
991 ++num_dirty_trackers_;
995 void MetadataDatabaseIndexOnDisk::UpdateInDirtyTrackerIndexes(
996 const FileTracker& old_tracker,
997 const FileTracker& new_tracker) {
998 DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
1000 int64 tracker_id = new_tracker.tracker_id();
1001 const std::string dirty_key = GenerateDirtyIDKey(tracker_id);
1002 const std::string demoted_key = GenerateDemotedDirtyIDKey(tracker_id);
1003 if (old_tracker.dirty() && !new_tracker.dirty()) {
1004 DCHECK(DBHasKey(dirty_key) || DBHasKey(demoted_key));
1006 DVLOG(1) << " Remove from dirty trackers IDs: " << tracker_id;
1008 if (DBHasKey(dirty_key))
1009 --num_dirty_trackers_;
1010 db_->Delete(dirty_key);
1011 db_->Delete(demoted_key);
1012 } else if (!old_tracker.dirty() && new_tracker.dirty()) {
1013 DCHECK(!DBHasKey(dirty_key));
1014 DCHECK(!DBHasKey(demoted_key));
1016 DVLOG(1) << " Add to dirty tracker IDs: " << tracker_id;
1018 db_->Put(dirty_key, std::string());
1019 ++num_dirty_trackers_;
1023 void MetadataDatabaseIndexOnDisk::RemoveFromDirtyTrackerIndexes(
1024 const FileTracker& tracker) {
1025 if (tracker.dirty()) {
1026 int64 tracker_id = tracker.tracker_id();
1027 const std::string dirty_key = GenerateDirtyIDKey(tracker_id);
1028 const std::string demoted_key = GenerateDemotedDirtyIDKey(tracker_id);
1029 DCHECK(DBHasKey(dirty_key) || DBHasKey(demoted_key));
1031 DVLOG(1) << " Remove from dirty tracker IDs: " << tracker_id;
1032 if (DBHasKey(dirty_key))
1033 --num_dirty_trackers_;
1034 db_->Delete(dirty_key);
1035 db_->Delete(demoted_key);
1039 TrackerIDSet MetadataDatabaseIndexOnDisk::GetTrackerIDSetByPrefix(
1040 const std::string& active_tracker_key,
1041 const std::string& ids_prefix) const {
1042 TrackerIDSet trackers;
1045 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
1046 for (itr->Seek(ids_prefix); itr->Valid(); itr->Next()) {
1047 const std::string& key(itr->key().ToString());
1049 if (!RemovePrefix(key, ids_prefix, &id_str))
1053 if (!base::StringToInt64(id_str, &tracker_id))
1055 trackers.InsertInactiveTracker(tracker_id);
1058 // Set an active tracker ID, if available.
1060 leveldb::Status status = db_->Get(active_tracker_key, &value);
1061 int64 active_tracker;
1062 if (status.ok() && base::StringToInt64(value, &active_tracker)) {
1063 DCHECK_NE(kInvalidTrackerID, active_tracker);
1064 trackers.Activate(active_tracker);
1070 void MetadataDatabaseIndexOnDisk::AddToTrackerIDSetWithPrefix(
1071 const std::string& active_tracker_key, const std::string& key_prefix,
1072 const FileTracker& tracker) {
1073 DCHECK(tracker.tracker_id());
1075 const std::string id_str = base::Int64ToString(tracker.tracker_id());
1076 db_->Put(key_prefix + id_str, std::string());
1077 if (tracker.active())
1078 db_->Put(active_tracker_key, id_str);
1081 bool MetadataDatabaseIndexOnDisk::EraseInTrackerIDSetWithPrefix(
1082 const std::string& active_tracker_key, const std::string& key_prefix,
1085 const std::string del_key = key_prefix + base::Int64ToString(tracker_id);
1086 leveldb::Status status = db_->Get(del_key, &value);
1087 if (status.IsNotFound())
1090 db_->Delete(del_key);
1092 status = db_->Get(active_tracker_key, &value);
1093 int64 active_tracker_id;
1094 if (status.ok() && base::StringToInt64(value, &active_tracker_id) &&
1095 active_tracker_id == tracker_id) {
1096 db_->Delete(active_tracker_key);
1102 void MetadataDatabaseIndexOnDisk::ActivateInTrackerIDSetWithPrefix(
1103 const std::string& active_tracker_key, const std::string& key_prefix,
1105 DCHECK(DBHasKey(key_prefix + base::Int64ToString(tracker_id)));
1108 leveldb::Status status = db_->Get(active_tracker_key, &value);
1109 int64 active_tracker_id = kInvalidTrackerID;
1110 if (status.IsNotFound() ||
1111 (status.ok() && base::StringToInt64(value, &active_tracker_id))) {
1112 DCHECK(active_tracker_id != tracker_id);
1113 db_->Put(active_tracker_key, base::Int64ToString(tracker_id));
1117 void MetadataDatabaseIndexOnDisk::DeactivateInTrackerIDSetWithPrefix(
1118 const std::string& active_tracker_key, const std::string& key_prefix,
1120 DCHECK(DBHasKey(key_prefix + base::Int64ToString(tracker_id)));
1123 leveldb::Status status = db_->Get(active_tracker_key, &value);
1124 int64 active_tracker_id;
1125 if (status.ok() && base::StringToInt64(value, &active_tracker_id)) {
1126 DCHECK(active_tracker_id == tracker_id);
1127 db_->Delete(active_tracker_key);
1131 bool MetadataDatabaseIndexOnDisk::DBHasKey(const std::string& key) {
1132 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
1134 return itr->Valid() && (itr->key() == key);
1137 size_t MetadataDatabaseIndexOnDisk::CountDirtyTrackerInternal() const {
1138 size_t num_dirty_trackers = 0;
1140 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
1141 for (itr->Seek(kDirtyIDKeyPrefix); itr->Valid(); itr->Next()) {
1142 if (!StartsWithASCII(itr->key().ToString(), kDirtyIDKeyPrefix, true))
1144 ++num_dirty_trackers;
1147 return num_dirty_trackers;
1150 MetadataDatabaseIndexOnDisk::NumEntries
1151 MetadataDatabaseIndexOnDisk::CountWithPrefix(
1152 const std::string& prefix, int64 ignored_id) {
1153 const std::string ignored = base::Int64ToString(ignored_id);
1156 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
1157 for (itr->Seek(prefix); itr->Valid() && count <= 1; itr->Next()) {
1159 if (!RemovePrefix(itr->key().ToString(), prefix, &value))
1161 if (value == ignored)
1169 return count == 0 ? NONE : SINGLE;
1172 void MetadataDatabaseIndexOnDisk::DeleteKeyStartsWith(
1173 const std::string& prefix) {
1174 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
1175 for (itr->Seek(prefix); itr->Valid();) {
1176 const std::string key = itr->key().ToString();
1177 if (!StartsWithASCII(key, prefix, true))
1183 } // namespace drive_backend
1184 } // namespace sync_file_system