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, nullptr))
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, nullptr))
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, nullptr))
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)
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);
358 PutFileTrackerToDB(*tracker, db_);
361 void MetadataDatabaseIndexOnDisk::RemoveFileMetadata(
362 const std::string& file_id) {
363 PutFileMetadataDeletionToDB(file_id, db_);
366 void MetadataDatabaseIndexOnDisk::RemoveFileTracker(int64 tracker_id) {
368 if (!GetFileTracker(tracker_id, &tracker)) {
373 DVLOG(1) << "Removing tracker: "
374 << tracker.tracker_id() << " " << GetTrackerTitle(tracker);
375 RemoveFromAppIDIndex(tracker);
376 RemoveFromFileIDIndexes(tracker);
377 RemoveFromPathIndexes(tracker);
378 RemoveFromDirtyTrackerIndexes(tracker);
380 PutFileTrackerDeletionToDB(tracker_id, db_);
383 TrackerIDSet MetadataDatabaseIndexOnDisk::GetFileTrackerIDsByFileID(
384 const std::string& file_id) const {
385 return GetTrackerIDSetByPrefix(
386 GenerateActiveTrackerIDByFileIDKey(file_id),
387 GenerateTrackerIDByFileIDKeyPrefix(file_id));
390 int64 MetadataDatabaseIndexOnDisk::GetAppRootTracker(
391 const std::string& app_id) const {
392 const std::string key = GenerateAppRootIDByAppIDKey(app_id);
394 leveldb::Status status = db_->Get(key, &value);
396 if (status.IsNotFound())
397 return kInvalidTrackerID;
400 util::Log(logging::LOG_WARNING, FROM_HERE,
401 "LevelDB error (%s) in getting AppRoot for AppID: %s",
402 status.ToString().c_str(),
404 return kInvalidTrackerID;
408 if (!base::StringToInt64(value, &root_id)) {
409 util::Log(logging::LOG_WARNING, FROM_HERE,
410 "Failed to parse a root ID (%s) for an App ID: %s",
413 return kInvalidTrackerID;
419 TrackerIDSet MetadataDatabaseIndexOnDisk::GetFileTrackerIDsByParentAndTitle(
420 int64 parent_tracker_id, const std::string& title) const {
421 return GetTrackerIDSetByPrefix(
422 GenerateActiveTrackerIDByParentAndTitleKey(parent_tracker_id, title),
423 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_tracker_id, title));
426 std::vector<int64> MetadataDatabaseIndexOnDisk::GetFileTrackerIDsByParent(
427 int64 parent_id) const {
428 std::vector<int64> result;
430 const std::string prefix = GenerateTrackerIDsByParentIDKeyPrefix(parent_id);
431 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
432 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
433 const std::string& key(itr->key().ToString());
434 std::string title_and_id;
435 if (!RemovePrefix(key, prefix, &title_and_id))
438 size_t pos = title_and_id.rfind('\0');
439 DCHECK(pos != std::string::npos);
442 if (!base::StringToInt64(title_and_id.substr(pos + 1), &tracker_id))
444 result.push_back(tracker_id);
449 std::string MetadataDatabaseIndexOnDisk::PickMultiTrackerFileID() const {
450 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
451 itr->Seek(kMultiTrackerByFileIDKeyPrefix);
453 return std::string();
456 if (!RemovePrefix(itr->key().ToString(),
457 kMultiTrackerByFileIDKeyPrefix, &file_id))
458 return std::string();
463 ParentIDAndTitle MetadataDatabaseIndexOnDisk::PickMultiBackingFilePath() const {
464 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
465 itr->Seek(kMultiBackingParentAndTitleKeyPrefix);
467 return ParentIDAndTitle();
470 if (!RemovePrefix(itr->key().ToString(),
471 kMultiBackingParentAndTitleKeyPrefix, &value))
472 return ParentIDAndTitle();
474 size_t pos = value.find('\0'); // '\0' is a separator.
475 if (pos == std::string::npos)
476 return ParentIDAndTitle();
479 return base::StringToInt64(value.substr(0, pos), &parent_id) ?
480 ParentIDAndTitle(parent_id, value.substr(pos + 1)) : ParentIDAndTitle();
483 int64 MetadataDatabaseIndexOnDisk::PickDirtyTracker() const {
484 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
485 itr->Seek(kDirtyIDKeyPrefix);
487 return kInvalidTrackerID;
490 if (!RemovePrefix(itr->key().ToString(), kDirtyIDKeyPrefix, &id_str))
491 return kInvalidTrackerID;
494 if (!base::StringToInt64(id_str, &tracker_id))
495 return kInvalidTrackerID;
500 void MetadataDatabaseIndexOnDisk::DemoteDirtyTracker(int64 tracker_id) {
501 const std::string key = GenerateDirtyIDKey(tracker_id);
504 leveldb::Status status = db_->Get(key, &value);
505 if (status.IsNotFound())
508 util::Log(logging::LOG_WARNING, FROM_HERE,
509 "LevelDB error (%s) in getting a dirty tracker for ID: %" PRId64,
510 status.ToString().c_str(),
516 db_->Put(GenerateDemotedDirtyIDKey(tracker_id), std::string());
517 --num_dirty_trackers_;
520 bool MetadataDatabaseIndexOnDisk::HasDemotedDirtyTracker() const {
521 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
522 itr->Seek(kDemotedDirtyIDKeyPrefix);
525 return StartsWithASCII(itr->key().ToString(), kDemotedDirtyIDKeyPrefix, true);
528 bool MetadataDatabaseIndexOnDisk::IsDemotedDirtyTracker(
529 int64 tracker_id) const {
530 return DBHasKey(GenerateDemotedDirtyIDKey(tracker_id));
533 void MetadataDatabaseIndexOnDisk::PromoteDemotedDirtyTracker(int64 tracker_id) {
534 std::string demoted_key = GenerateDemotedDirtyIDKey(tracker_id);
537 if (db_->Get(demoted_key, &empty).ok()) {
538 db_->Delete(demoted_key);
539 db_->Put(GenerateDirtyIDKey(tracker_id), std::string());
540 ++num_dirty_trackers_;
544 bool MetadataDatabaseIndexOnDisk::PromoteDemotedDirtyTrackers() {
545 bool promoted = false;
546 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
547 for (itr->Seek(kDemotedDirtyIDKeyPrefix); itr->Valid(); itr->Next()) {
549 if (!RemovePrefix(itr->key().ToString(), kDemotedDirtyIDKeyPrefix, &id_str))
553 if (!base::StringToInt64(id_str, &tracker_id))
556 db_->Delete(itr->key().ToString());
557 db_->Put(GenerateDirtyIDKey(tracker_id), std::string());
558 ++num_dirty_trackers_;
564 size_t MetadataDatabaseIndexOnDisk::CountDirtyTracker() const {
565 return num_dirty_trackers_;
568 size_t MetadataDatabaseIndexOnDisk::CountFileMetadata() const {
569 // TODO(peria): Cache the number of FileMetadata in the DB.
571 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
572 for (itr->Seek(kFileMetadataKeyPrefix); itr->Valid(); itr->Next()) {
573 if (!StartsWithASCII(itr->key().ToString(), kFileMetadataKeyPrefix, true))
580 size_t MetadataDatabaseIndexOnDisk::CountFileTracker() const {
581 // TODO(peria): Cache the number of FileTracker in the DB.
583 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
584 for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) {
585 if (!StartsWithASCII(itr->key().ToString(), kFileTrackerKeyPrefix, true))
592 void MetadataDatabaseIndexOnDisk::SetSyncRootTrackerID(
593 int64 sync_root_id) const {
594 service_metadata_->set_sync_root_tracker_id(sync_root_id);
595 PutServiceMetadataToDB(*service_metadata_, db_);
598 void MetadataDatabaseIndexOnDisk::SetLargestChangeID(
599 int64 largest_change_id) const {
600 service_metadata_->set_largest_change_id(largest_change_id);
601 PutServiceMetadataToDB(*service_metadata_, db_);
604 void MetadataDatabaseIndexOnDisk::SetNextTrackerID(
605 int64 next_tracker_id) const {
606 service_metadata_->set_next_tracker_id(next_tracker_id);
607 PutServiceMetadataToDB(*service_metadata_, db_);
610 int64 MetadataDatabaseIndexOnDisk::GetSyncRootTrackerID() const {
611 if (!service_metadata_->has_sync_root_tracker_id())
612 return kInvalidTrackerID;
613 return service_metadata_->sync_root_tracker_id();
616 int64 MetadataDatabaseIndexOnDisk::GetLargestChangeID() const {
617 if (!service_metadata_->has_largest_change_id())
618 return kInvalidTrackerID;
619 return service_metadata_->largest_change_id();
622 int64 MetadataDatabaseIndexOnDisk::GetNextTrackerID() const {
623 if (!service_metadata_->has_next_tracker_id())
624 return kInvalidTrackerID;
625 return service_metadata_->next_tracker_id();
628 std::vector<std::string>
629 MetadataDatabaseIndexOnDisk::GetRegisteredAppIDs() const {
630 std::vector<std::string> result;
631 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
632 for (itr->Seek(kAppRootIDByAppIDKeyPrefix); itr->Valid(); itr->Next()) {
634 if (!RemovePrefix(itr->key().ToString(), kAppRootIDByAppIDKeyPrefix, &id))
636 result.push_back(id);
641 std::vector<int64> MetadataDatabaseIndexOnDisk::GetAllTrackerIDs() const {
642 std::vector<int64> tracker_ids;
643 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
644 for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) {
646 if (!RemovePrefix(itr->key().ToString(), kFileTrackerKeyPrefix, &id_str))
650 if (!base::StringToInt64(id_str, &tracker_id))
652 tracker_ids.push_back(tracker_id);
657 std::vector<std::string>
658 MetadataDatabaseIndexOnDisk::GetAllMetadataIDs() const {
659 std::vector<std::string> file_ids;
660 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
661 for (itr->Seek(kFileMetadataKeyPrefix); itr->Valid(); itr->Next()) {
663 if (!RemovePrefix(itr->key().ToString(), kFileMetadataKeyPrefix, &file_id))
665 file_ids.push_back(file_id);
670 int64 MetadataDatabaseIndexOnDisk::BuildTrackerIndexes() {
671 int64 num_puts_before = db_->num_puts();
673 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
674 for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) {
675 if (!RemovePrefix(itr->key().ToString(), kFileTrackerKeyPrefix, nullptr))
679 if (!tracker.ParseFromString(itr->value().ToString())) {
680 util::Log(logging::LOG_WARNING, FROM_HERE,
681 "Failed to parse a Tracker");
685 AddToAppIDIndex(tracker);
686 AddToFileIDIndexes(tracker);
687 AddToPathIndexes(tracker);
688 AddToDirtyTrackerIndexes(tracker);
691 return db_->num_puts() - num_puts_before;
694 int64 MetadataDatabaseIndexOnDisk::DeleteTrackerIndexes() {
695 const char* kIndexPrefixes[] = {
696 kAppRootIDByAppIDKeyPrefix, kActiveTrackerIDByFileIDKeyPrefix,
697 kTrackerIDByFileIDKeyPrefix, kMultiTrackerByFileIDKeyPrefix,
698 kActiveTrackerIDByParentAndTitleKeyPrefix,
699 kTrackerIDByParentAndTitleKeyPrefix, kMultiBackingParentAndTitleKeyPrefix,
700 kDirtyIDKeyPrefix, kDemotedDirtyIDKeyPrefix
703 int64 num_deletes_before = db_->num_deletes();
704 for (size_t i = 0; i < arraysize(kIndexPrefixes); ++i)
705 DeleteKeyStartsWith(kIndexPrefixes[i]);
706 num_dirty_trackers_ = 0;
707 return db_->num_deletes() - num_deletes_before;
710 LevelDBWrapper* MetadataDatabaseIndexOnDisk::GetDBForTesting() {
714 MetadataDatabaseIndexOnDisk::MetadataDatabaseIndexOnDisk(LevelDBWrapper* db)
716 num_dirty_trackers_(0) {
717 // TODO(peria): Add UMA to measure the number of FileMetadata, FileTracker,
719 service_metadata_ = InitializeServiceMetadata(db_);
721 // Check if index is valid, if no validations run in 7 days.
722 const int64 kThresholdToValidateInDays = 7;
724 int64 last_check_time = 0;
726 if (db_->Get(kLastValidationTimeKey, &value).ok())
727 base::StringToInt64(value, &last_check_time);
728 base::TimeDelta since_last_check =
729 base::Time::Now() - base::Time::FromInternalValue(last_check_time);
730 int64 since_last_check_in_days = since_last_check.InDays();
731 if (since_last_check_in_days >= kThresholdToValidateInDays ||
732 since_last_check_in_days < 0) {
733 // TODO(peria): Add UMA to check if the number of deleted entries and the
734 // number of built entries are different or not.
735 DeleteTrackerIndexes();
736 BuildTrackerIndexes();
737 db_->Put(kLastValidationTimeKey,
738 base::Int64ToString(base::Time::Now().ToInternalValue()));
740 num_dirty_trackers_ = CountDirtyTrackerInternal();
744 void MetadataDatabaseIndexOnDisk::AddToAppIDIndex(const FileTracker& tracker) {
745 if (!IsAppRoot(tracker)) {
746 DVLOG(3) << " Tracker for " << tracker.file_id() << " is not an App root.";
750 DVLOG(1) << " Add to App root by App ID: " << tracker.app_id();
752 const std::string db_key = GenerateAppRootIDByAppIDKey(tracker.app_id());
753 DCHECK(tracker.active());
754 DCHECK(!DBHasKey(db_key));
755 db_->Put(db_key, base::Int64ToString(tracker.tracker_id()));
758 void MetadataDatabaseIndexOnDisk::UpdateInAppIDIndex(
759 const FileTracker& old_tracker,
760 const FileTracker& new_tracker) {
761 DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
763 if (IsAppRoot(old_tracker) && !IsAppRoot(new_tracker)) {
764 DCHECK(old_tracker.active());
765 DCHECK(!new_tracker.active());
766 const std::string key = GenerateAppRootIDByAppIDKey(old_tracker.app_id());
767 DCHECK(DBHasKey(key));
769 DVLOG(1) << " Remove from App root by App ID: " << old_tracker.app_id();
771 } else if (!IsAppRoot(old_tracker) && IsAppRoot(new_tracker)) {
772 DCHECK(!old_tracker.active());
773 DCHECK(new_tracker.active());
774 const std::string key = GenerateAppRootIDByAppIDKey(new_tracker.app_id());
775 DCHECK(!DBHasKey(key));
777 DVLOG(1) << " Add to App root by App ID: " << new_tracker.app_id();
778 db_->Put(key, base::Int64ToString(new_tracker.tracker_id()));
782 void MetadataDatabaseIndexOnDisk::RemoveFromAppIDIndex(
783 const FileTracker& tracker) {
784 if (!IsAppRoot(tracker)) {
785 DVLOG(3) << " Tracker for " << tracker.file_id() << " is not an App root.";
789 DCHECK(tracker.active());
790 const std::string key = GenerateAppRootIDByAppIDKey(tracker.app_id());
791 DCHECK(DBHasKey(key));
793 DVLOG(1) << " Remove from App root by App ID: " << tracker.app_id();
797 void MetadataDatabaseIndexOnDisk::AddToFileIDIndexes(
798 const FileTracker& new_tracker) {
799 const std::string& file_id = new_tracker.file_id();
801 DVLOG(1) << " Add to trackers by file ID: " << file_id;
802 const std::string prefix = GenerateTrackerIDByFileIDKeyPrefix(file_id);
803 AddToTrackerIDSetWithPrefix(
804 GenerateActiveTrackerIDByFileIDKey(file_id), prefix, new_tracker);
806 const std::string multi_tracker_key = GenerateMultiTrackerKey(file_id);
807 if (!DBHasKey(multi_tracker_key) &&
808 CountWithPrefix(prefix, new_tracker.tracker_id()) != NONE) {
809 DVLOG(1) << " Add to multi-tracker file IDs: " << file_id;
810 db_->Put(multi_tracker_key, std::string());
814 void MetadataDatabaseIndexOnDisk::UpdateInFileIDIndexes(
815 const FileTracker& old_tracker,
816 const FileTracker& new_tracker) {
817 DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
818 DCHECK_EQ(old_tracker.file_id(), new_tracker.file_id());
820 const std::string& file_id = new_tracker.file_id();
821 const std::string prefix = GenerateTrackerIDByFileIDKeyPrefix(file_id);
822 DCHECK(DBHasKey(prefix + base::Int64ToString(new_tracker.tracker_id())));
824 if (old_tracker.active() && !new_tracker.active()) {
825 DeactivateInTrackerIDSetWithPrefix(
826 GenerateActiveTrackerIDByFileIDKey(file_id), prefix,
827 new_tracker.tracker_id());
828 } else if (!old_tracker.active() && new_tracker.active()) {
829 ActivateInTrackerIDSetWithPrefix(
830 GenerateActiveTrackerIDByFileIDKey(file_id), prefix,
831 new_tracker.tracker_id());
835 void MetadataDatabaseIndexOnDisk::RemoveFromFileIDIndexes(
836 const FileTracker& tracker) {
837 const std::string& file_id = tracker.file_id();
838 const std::string prefix =
839 GenerateTrackerIDByFileIDKeyPrefix(file_id);
841 if (!EraseInTrackerIDSetWithPrefix(
842 GenerateActiveTrackerIDByFileIDKey(file_id),
843 prefix, tracker.tracker_id()))
846 DVLOG(1) << " Remove from trackers by file ID: " << tracker.tracker_id();
848 const std::string multi_key = GenerateMultiTrackerKey(file_id);
849 if (DBHasKey(multi_key) &&
850 CountWithPrefix(prefix, tracker.tracker_id()) != MULTIPLE) {
851 DVLOG(1) << " Remove from multi-tracker file IDs: " << file_id;
852 db_->Delete(multi_key);
856 void MetadataDatabaseIndexOnDisk::AddToPathIndexes(
857 const FileTracker& new_tracker) {
858 int64 parent_id = new_tracker.parent_tracker_id();
859 std::string title = GetTrackerTitle(new_tracker);
861 DVLOG(1) << " Add to trackers by parent and title: "
862 << parent_id << " " << title;
864 const std::string prefix =
865 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title);
866 if (!title.empty()) {
867 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
868 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
870 if (!RemovePrefix(itr->key().ToString(), prefix, &id_str))
874 if (!base::StringToInt64(id_str, &tracker_id))
876 if (tracker_id == new_tracker.tracker_id()) {
881 const std::string multi_key =
882 GenerateMultiBackingParentAndTitleKey(parent_id, title);
883 DVLOG_IF(1, !DBHasKey(multi_key))
884 << " Add to multi backing file paths: " << parent_id << " " << title;
885 db_->Put(multi_key, std::string());
890 AddToTrackerIDSetWithPrefix(
891 GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title),
892 prefix, new_tracker);
895 void MetadataDatabaseIndexOnDisk::UpdateInPathIndexes(
896 const FileTracker& old_tracker,
897 const FileTracker& new_tracker) {
898 DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
899 DCHECK_EQ(old_tracker.parent_tracker_id(), new_tracker.parent_tracker_id());
900 DCHECK(GetTrackerTitle(old_tracker) == GetTrackerTitle(new_tracker) ||
901 !old_tracker.has_synced_details());
903 int64 tracker_id = new_tracker.tracker_id();
904 int64 parent_id = new_tracker.parent_tracker_id();
905 const std::string old_title = GetTrackerTitle(old_tracker);
906 const std::string title = GetTrackerTitle(new_tracker);
908 if (old_title != title) {
909 const std::string old_prefix =
910 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, old_title);
911 EraseInTrackerIDSetWithPrefix(
912 GenerateActiveTrackerIDByParentAndTitleKey(parent_id, old_title),
913 old_prefix, tracker_id);
915 if (!old_title.empty() &&
916 CountWithPrefix(old_prefix, tracker_id) != MULTIPLE) {
917 const std::string old_multi_backing_key =
918 GenerateMultiBackingParentAndTitleKey(parent_id, old_title);
919 DVLOG_IF(1, DBHasKey(old_multi_backing_key))
920 << " Remove from multi backing file paths: "
921 << parent_id << " " << old_title;
922 db_->Delete(old_multi_backing_key);
925 DVLOG(1) << " Add to trackers by parent and title: "
926 << parent_id << " " << title;
928 const std::string prefix =
929 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title);
930 AddToTrackerIDSetWithPrefix(
931 GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title),
932 prefix, new_tracker);
934 if (!title.empty() && CountWithPrefix(prefix, tracker_id) != NONE) {
935 const std::string multi_backing_key =
936 GenerateMultiBackingParentAndTitleKey(parent_id, title);
937 DVLOG_IF(1, !DBHasKey(multi_backing_key))
938 << " Add to multi backing file_paths: "
939 << parent_id << " " << title;
940 db_->Put(multi_backing_key, std::string());
946 const std::string active_tracker_key =
947 GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title);
948 const std::string prefix =
949 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title);
950 if (old_tracker.active() && !new_tracker.active()) {
951 DeactivateInTrackerIDSetWithPrefix(
952 active_tracker_key, prefix, tracker_id);
953 } else if (!old_tracker.active() && new_tracker.active()) {
954 ActivateInTrackerIDSetWithPrefix(
955 active_tracker_key, prefix, tracker_id);
959 void MetadataDatabaseIndexOnDisk::RemoveFromPathIndexes(
960 const FileTracker& tracker) {
961 int64 tracker_id = tracker.tracker_id();
962 int64 parent_id = tracker.parent_tracker_id();
963 std::string title = GetTrackerTitle(tracker);
965 DVLOG(1) << " Remove from trackers by parent and title: "
966 << parent_id << " " << title;
968 const std::string active_tracker_key =
969 GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title);
970 const std::string key_prefix =
971 GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title);
972 if (!EraseInTrackerIDSetWithPrefix(
973 active_tracker_key, key_prefix, tracker_id))
976 const std::string multi_key =
977 GenerateMultiBackingParentAndTitleKey(parent_id, title);
978 if (!title.empty() && DBHasKey(multi_key) &&
979 CountWithPrefix(key_prefix, tracker_id) != MULTIPLE) {
980 DVLOG(1) << " Remove from multi backing file paths: "
981 << parent_id << " " << title;
982 db_->Delete(multi_key);
986 void MetadataDatabaseIndexOnDisk::AddToDirtyTrackerIndexes(
987 const FileTracker& new_tracker) {
988 const std::string dirty_key = GenerateDirtyIDKey(new_tracker.tracker_id());
989 DCHECK(!DBHasKey(dirty_key));
990 DCHECK(!DBHasKey(GenerateDemotedDirtyIDKey(new_tracker.tracker_id())));
992 if (new_tracker.dirty()) {
993 DVLOG(1) << " Add to dirty tracker IDs: " << new_tracker.tracker_id();
994 db_->Put(dirty_key, std::string());
995 ++num_dirty_trackers_;
999 void MetadataDatabaseIndexOnDisk::UpdateInDirtyTrackerIndexes(
1000 const FileTracker& old_tracker,
1001 const FileTracker& new_tracker) {
1002 DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
1004 int64 tracker_id = new_tracker.tracker_id();
1005 const std::string dirty_key = GenerateDirtyIDKey(tracker_id);
1006 const std::string demoted_key = GenerateDemotedDirtyIDKey(tracker_id);
1007 if (old_tracker.dirty() && !new_tracker.dirty()) {
1008 DCHECK(DBHasKey(dirty_key) || DBHasKey(demoted_key));
1010 DVLOG(1) << " Remove from dirty trackers IDs: " << tracker_id;
1012 if (DBHasKey(dirty_key))
1013 --num_dirty_trackers_;
1014 db_->Delete(dirty_key);
1015 db_->Delete(demoted_key);
1016 } else if (!old_tracker.dirty() && new_tracker.dirty()) {
1017 DCHECK(!DBHasKey(dirty_key));
1018 DCHECK(!DBHasKey(demoted_key));
1020 DVLOG(1) << " Add to dirty tracker IDs: " << tracker_id;
1022 db_->Put(dirty_key, std::string());
1023 ++num_dirty_trackers_;
1027 void MetadataDatabaseIndexOnDisk::RemoveFromDirtyTrackerIndexes(
1028 const FileTracker& tracker) {
1029 if (tracker.dirty()) {
1030 int64 tracker_id = tracker.tracker_id();
1031 const std::string dirty_key = GenerateDirtyIDKey(tracker_id);
1032 const std::string demoted_key = GenerateDemotedDirtyIDKey(tracker_id);
1033 DCHECK(DBHasKey(dirty_key) || DBHasKey(demoted_key));
1035 DVLOG(1) << " Remove from dirty tracker IDs: " << tracker_id;
1036 if (DBHasKey(dirty_key))
1037 --num_dirty_trackers_;
1038 db_->Delete(dirty_key);
1039 db_->Delete(demoted_key);
1043 TrackerIDSet MetadataDatabaseIndexOnDisk::GetTrackerIDSetByPrefix(
1044 const std::string& active_tracker_key,
1045 const std::string& ids_prefix) const {
1046 TrackerIDSet trackers;
1049 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
1050 for (itr->Seek(ids_prefix); itr->Valid(); itr->Next()) {
1051 const std::string& key(itr->key().ToString());
1053 if (!RemovePrefix(key, ids_prefix, &id_str))
1057 if (!base::StringToInt64(id_str, &tracker_id))
1059 trackers.InsertInactiveTracker(tracker_id);
1062 // Set an active tracker ID, if available.
1064 leveldb::Status status = db_->Get(active_tracker_key, &value);
1065 int64 active_tracker;
1066 if (status.ok() && base::StringToInt64(value, &active_tracker)) {
1067 DCHECK_NE(kInvalidTrackerID, active_tracker);
1068 trackers.Activate(active_tracker);
1074 void MetadataDatabaseIndexOnDisk::AddToTrackerIDSetWithPrefix(
1075 const std::string& active_tracker_key, const std::string& key_prefix,
1076 const FileTracker& tracker) {
1077 DCHECK(tracker.tracker_id());
1079 const std::string id_str = base::Int64ToString(tracker.tracker_id());
1080 db_->Put(key_prefix + id_str, std::string());
1081 if (tracker.active())
1082 db_->Put(active_tracker_key, id_str);
1085 bool MetadataDatabaseIndexOnDisk::EraseInTrackerIDSetWithPrefix(
1086 const std::string& active_tracker_key, const std::string& key_prefix,
1089 const std::string del_key = key_prefix + base::Int64ToString(tracker_id);
1090 leveldb::Status status = db_->Get(del_key, &value);
1091 if (status.IsNotFound())
1094 db_->Delete(del_key);
1096 status = db_->Get(active_tracker_key, &value);
1097 int64 active_tracker_id;
1098 if (status.ok() && base::StringToInt64(value, &active_tracker_id) &&
1099 active_tracker_id == tracker_id) {
1100 db_->Delete(active_tracker_key);
1106 void MetadataDatabaseIndexOnDisk::ActivateInTrackerIDSetWithPrefix(
1107 const std::string& active_tracker_key, const std::string& key_prefix,
1109 DCHECK(DBHasKey(key_prefix + base::Int64ToString(tracker_id)));
1112 leveldb::Status status = db_->Get(active_tracker_key, &value);
1113 int64 active_tracker_id = kInvalidTrackerID;
1114 if (status.IsNotFound() ||
1115 (status.ok() && base::StringToInt64(value, &active_tracker_id))) {
1116 DCHECK(active_tracker_id != tracker_id);
1117 db_->Put(active_tracker_key, base::Int64ToString(tracker_id));
1121 void MetadataDatabaseIndexOnDisk::DeactivateInTrackerIDSetWithPrefix(
1122 const std::string& active_tracker_key, const std::string& key_prefix,
1124 DCHECK(DBHasKey(key_prefix + base::Int64ToString(tracker_id)));
1127 leveldb::Status status = db_->Get(active_tracker_key, &value);
1128 int64 active_tracker_id;
1129 if (status.ok() && base::StringToInt64(value, &active_tracker_id)) {
1130 DCHECK(active_tracker_id == tracker_id);
1131 db_->Delete(active_tracker_key);
1135 bool MetadataDatabaseIndexOnDisk::DBHasKey(const std::string& key) const {
1136 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
1138 return itr->Valid() && (itr->key() == key);
1141 size_t MetadataDatabaseIndexOnDisk::CountDirtyTrackerInternal() const {
1142 size_t num_dirty_trackers = 0;
1144 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
1145 for (itr->Seek(kDirtyIDKeyPrefix); itr->Valid(); itr->Next()) {
1146 if (!StartsWithASCII(itr->key().ToString(), kDirtyIDKeyPrefix, true))
1148 ++num_dirty_trackers;
1151 return num_dirty_trackers;
1154 MetadataDatabaseIndexOnDisk::NumEntries
1155 MetadataDatabaseIndexOnDisk::CountWithPrefix(
1156 const std::string& prefix, int64 ignored_id) {
1157 const std::string ignored = base::Int64ToString(ignored_id);
1160 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
1161 for (itr->Seek(prefix); itr->Valid() && count <= 1; itr->Next()) {
1163 if (!RemovePrefix(itr->key().ToString(), prefix, &value))
1165 if (value == ignored)
1173 return count == 0 ? NONE : SINGLE;
1176 void MetadataDatabaseIndexOnDisk::DeleteKeyStartsWith(
1177 const std::string& prefix) {
1178 scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
1179 for (itr->Seek(prefix); itr->Valid();) {
1180 const std::string key = itr->key().ToString();
1181 if (!StartsWithASCII(key, prefix, true))
1187 } // namespace drive_backend
1188 } // namespace sync_file_system