blob: d092a607aef53395049d47e2ff2f7cf855860635 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/sync_file_system/drive_backend/metadata_database_index_on_disk.h"
#include <unordered_set>
#include "base/format_macros.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
#include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
#include "chrome/browser/sync_file_system/drive_backend/leveldb_wrapper.h"
#include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h"
#include "chrome/browser/sync_file_system/logger.h"
#include "third_party/leveldatabase/src/include/leveldb/status.h"
// LevelDB database schema
// =======================
//
// NOTE
// - Entries are sorted by keys.
// - int64_t value is serialized as a string by base::Int64ToString().
// - ServiceMetadata, FileMetadata, and FileTracker values are serialized
// as a string by SerializeToString() of protocol buffers.
//
// Version 4:
// # Version of this schema
// key: "VERSION"
// value: "4"
//
// # Metadata of the SyncFS service (compatible with version 3)
// key: "SERVICE"
// value: <ServiceMetadata 'service_metadata'>
//
// # Metadata of remote files (compatible with version 3)
// key: "FILE: " + <string 'file_id'>
// value: <FileMetadata 'metadata'>
//
// # Trackers of remote file updates (compatible with version 3)
// key: "TRACKER: " + <int64_t 'tracker_id'>
// value: <FileTracker 'tracker'>
//
// # Index from App ID to the tracker ID
// key: "APP_ROOT: " + <string 'app_id'>
// value: <int64_t 'app_root_tracker_id'>
//
// # Index from file ID to the active tracker ID
// key: "ACTIVE_FILE: " + <string 'file_id'>
// value: <int64_t 'active_tracker_id'>
//
// # Index from file ID to a tracker ID
// key: "TRACKER_FILE: " + <string 'file_id'> + '\x00' +
// <int64_t'tracker_id'>
// value: <empty>
//
// # Tracker IDs; a file metadata linked to multiple tracker IDs.
// key: "MULTI_FILE: " + <int64_t 'tracker_id'>
// value: <empty>
//
// # Index from the parent tracker ID and the title to the active tracker ID
// key: "ACTIVE_PATH: " + <int64_t 'parent_tracker_id'> +
// '\x00' + <string 'title'>
// value: <int64_t 'active_tracker_id'>
//
// # Index from the parent tracker ID and the title to a tracker ID
// key: "TRACKER_PATH: " + <int64_t 'parent_tracker_id'> +
// '\x00' + <string 'title'> + '\x00' + <int64_t 'tracker_id'>
// value: <empty>
//
// # Tracker IDs; a parent tracker ID and a title figure multiple tracker IDs
// key: "MULTI_PATH: " + <int64_t 'tracker_id'>
// value: <empty>
//
// # Dirty tracker IDs
// key: "DIRTY: " + <int64_t 'dirty_tracker_id'>
// value: <empty>
//
// # Demoted dirty tracker IDs
// key: "DEMOTED_DIRTY: " + <int64_t 'demoted_dirty_tracker_id'>
// value: <empty>
//
// # Timestamp when the last validation ran
// key: "LAST_VALID"
// value: <time_t 'last_valid_time'>
namespace sync_file_system {
namespace drive_backend {
namespace {
std::string GenerateAppRootIDByAppIDKey(const std::string& app_id) {
return kAppRootIDByAppIDKeyPrefix + app_id;
}
std::string GenerateActiveTrackerIDByFileIDKey(const std::string& file_id) {
return kActiveTrackerIDByFileIDKeyPrefix + file_id;
}
std::string GenerateTrackerIDByFileIDKeyPrefix(const std::string& file_id) {
std::ostringstream oss;
oss << kTrackerIDByFileIDKeyPrefix << file_id << '\0';
return oss.str();
}
std::string GenerateMultiTrackerKey(const std::string& file_id) {
return kMultiTrackerByFileIDKeyPrefix + file_id;
}
std::string GenerateActiveTrackerIDByParentAndTitleKey(
int64_t parent_id,
const std::string& title) {
std::ostringstream oss;
oss << kActiveTrackerIDByParentAndTitleKeyPrefix << parent_id
<< '\0' << title;
return oss.str();
}
std::string GenerateTrackerIDByParentAndTitleKeyPrefix(
int64_t parent_id,
const std::string& title) {
std::ostringstream oss;
oss << kTrackerIDByParentAndTitleKeyPrefix << parent_id << '\0'
<< title << '\0';
return oss.str();
}
std::string GenerateTrackerIDsByParentIDKeyPrefix(int64_t parent_id) {
std::ostringstream oss;
oss << kTrackerIDByParentAndTitleKeyPrefix << parent_id << '\0';
return oss.str();
}
std::string GenerateMultiBackingParentAndTitleKey(int64_t parent_id,
const std::string& title) {
std::ostringstream oss;
oss << kMultiBackingParentAndTitleKeyPrefix << parent_id << '\0'
<< title;
return oss.str();
}
std::string GenerateDirtyIDKey(int64_t tracker_id) {
return kDirtyIDKeyPrefix + base::Int64ToString(tracker_id);
}
std::string GenerateDemotedDirtyIDKey(int64_t tracker_id) {
return kDemotedDirtyIDKeyPrefix + base::Int64ToString(tracker_id);
}
void RemoveUnreachableItemsFromDB(LevelDBWrapper* db,
int64_t sync_root_tracker_id) {
DCHECK(db);
typedef std::map<int64_t, std::set<int64_t>> ChildTrackersByParent;
ChildTrackersByParent trackers_by_parent;
{
// Set up links from parent tracker to child trackers.
std::set<int64_t> inactive_trackers;
std::unique_ptr<LevelDBWrapper::Iterator> itr = db->NewIterator();
for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) {
if (!RemovePrefix(itr->key().ToString(), kFileTrackerKeyPrefix, nullptr))
break;
std::unique_ptr<FileTracker> tracker(new FileTracker);
if (!tracker->ParseFromString(itr->value().ToString())) {
util::Log(logging::LOG_WARNING, FROM_HERE,
"Failed to parse a Tracker");
continue;
}
int64_t parent_tracker_id = tracker->parent_tracker_id();
int64_t tracker_id = tracker->tracker_id();
trackers_by_parent[parent_tracker_id].insert(tracker_id);
if (!tracker->active())
inactive_trackers.insert(tracker_id);
}
// Drop links from inactive trackers.
for (auto iter = inactive_trackers.begin(); iter != inactive_trackers.end();
++iter) {
trackers_by_parent.erase(*iter);
}
}
// Traverse tracker tree from sync-root.
std::set<int64_t> visited_trackers;
{
std::vector<int64_t> pending;
if (sync_root_tracker_id != kInvalidTrackerID)
pending.push_back(sync_root_tracker_id);
while (!pending.empty()) {
int64_t tracker_id = pending.back();
DCHECK_NE(kInvalidTrackerID, tracker_id);
pending.pop_back();
if (!visited_trackers.insert(tracker_id).second) {
NOTREACHED();
continue;
}
AppendContents(
LookUpMap(trackers_by_parent, tracker_id, std::set<int64_t>()),
&pending);
}
}
// Delete all unreachable trackers, and list all |file_id| referred by
// remained trackers.
std::unordered_set<std::string> referred_file_ids;
{
std::unique_ptr<LevelDBWrapper::Iterator> itr = db->NewIterator();
for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) {
if (!RemovePrefix(itr->key().ToString(), kFileTrackerKeyPrefix, nullptr))
break;
std::unique_ptr<FileTracker> tracker(new FileTracker);
if (!tracker->ParseFromString(itr->value().ToString())) {
util::Log(logging::LOG_WARNING, FROM_HERE,
"Failed to parse a Tracker");
continue;
}
if (base::ContainsKey(visited_trackers, tracker->tracker_id())) {
referred_file_ids.insert(tracker->file_id());
} else {
PutFileTrackerDeletionToDB(tracker->tracker_id(), db);
}
}
}
// Delete all unreferred metadata.
{
std::unique_ptr<LevelDBWrapper::Iterator> itr = db->NewIterator();
for (itr->Seek(kFileMetadataKeyPrefix); itr->Valid(); itr->Next()) {
if (!RemovePrefix(itr->key().ToString(), kFileMetadataKeyPrefix, nullptr))
break;
std::unique_ptr<FileMetadata> metadata(new FileMetadata);
if (!metadata->ParseFromString(itr->value().ToString())) {
util::Log(logging::LOG_WARNING, FROM_HERE,
"Failed to parse a Tracker");
continue;
}
if (!base::ContainsKey(referred_file_ids, metadata->file_id()))
PutFileMetadataDeletionToDB(metadata->file_id(), db);
}
}
}
} // namespace
// static
std::unique_ptr<MetadataDatabaseIndexOnDisk>
MetadataDatabaseIndexOnDisk::Create(LevelDBWrapper* db) {
DCHECK(db);
std::unique_ptr<ServiceMetadata> service_metadata =
InitializeServiceMetadata(db);
if (!service_metadata)
return nullptr;
PutVersionToDB(kDatabaseOnDiskVersion, db);
RemoveUnreachableItemsFromDB(db, service_metadata->sync_root_tracker_id());
std::unique_ptr<MetadataDatabaseIndexOnDisk> index(
new MetadataDatabaseIndexOnDisk(db));
return index;
}
MetadataDatabaseIndexOnDisk::~MetadataDatabaseIndexOnDisk() {}
void MetadataDatabaseIndexOnDisk::RemoveUnreachableItems() {
RemoveUnreachableItemsFromDB(
db_, service_metadata_->sync_root_tracker_id());
DeleteTrackerIndexes();
BuildTrackerIndexes();
}
bool MetadataDatabaseIndexOnDisk::GetFileMetadata(
const std::string& file_id, FileMetadata* metadata) const {
const std::string key = kFileMetadataKeyPrefix + file_id;
std::string value;
leveldb::Status status = db_->Get(key, &value);
if (status.IsNotFound())
return false;
if (!status.ok()) {
util::Log(logging::LOG_WARNING, FROM_HERE,
"LevelDB error (%s) in getting FileMetadata for ID: %s",
status.ToString().c_str(),
file_id.c_str());
return false;
}
FileMetadata tmp_metadata;
if (!tmp_metadata.ParseFromString(value)) {
util::Log(logging::LOG_WARNING, FROM_HERE,
"Failed to parse a FileMetadata for ID: %s",
file_id.c_str());
return false;
}
if (metadata)
metadata->CopyFrom(tmp_metadata);
return true;
}
bool MetadataDatabaseIndexOnDisk::GetFileTracker(int64_t tracker_id,
FileTracker* tracker) const {
const std::string key =
kFileTrackerKeyPrefix + base::Int64ToString(tracker_id);
std::string value;
leveldb::Status status = db_->Get(key, &value);
if (status.IsNotFound())
return false;
if (!status.ok()) {
util::Log(logging::LOG_WARNING, FROM_HERE,
"LevelDB error (%s) in getting FileTracker for ID: %" PRId64,
status.ToString().c_str(),
tracker_id);
return false;
}
FileTracker tmp_tracker;
if (!tmp_tracker.ParseFromString(value)) {
util::Log(logging::LOG_WARNING, FROM_HERE,
"Failed to parse a Tracker for ID: %" PRId64,
tracker_id);
return false;
}
if (tracker)
tracker->CopyFrom(tmp_tracker);
return true;
}
void MetadataDatabaseIndexOnDisk::StoreFileMetadata(
std::unique_ptr<FileMetadata> metadata) {
DCHECK(metadata);
PutFileMetadataToDB(*metadata, db_);
}
void MetadataDatabaseIndexOnDisk::StoreFileTracker(
std::unique_ptr<FileTracker> tracker) {
DCHECK(tracker);
int64_t tracker_id = tracker->tracker_id();
FileTracker old_tracker;
if (!GetFileTracker(tracker_id, &old_tracker)) {
DVLOG(3) << "Adding new tracker: " << tracker->tracker_id()
<< " " << GetTrackerTitle(*tracker);
AddToAppIDIndex(*tracker);
AddToFileIDIndexes(*tracker);
AddToPathIndexes(*tracker);
AddToDirtyTrackerIndexes(*tracker);
} else {
DVLOG(3) << "Updating tracker: " << tracker->tracker_id()
<< " " << GetTrackerTitle(*tracker);
UpdateInAppIDIndex(old_tracker, *tracker);
UpdateInFileIDIndexes(old_tracker, *tracker);
UpdateInPathIndexes(old_tracker, *tracker);
UpdateInDirtyTrackerIndexes(old_tracker, *tracker);
}
PutFileTrackerToDB(*tracker, db_);
}
void MetadataDatabaseIndexOnDisk::RemoveFileMetadata(
const std::string& file_id) {
PutFileMetadataDeletionToDB(file_id, db_);
}
void MetadataDatabaseIndexOnDisk::RemoveFileTracker(int64_t tracker_id) {
FileTracker tracker;
if (!GetFileTracker(tracker_id, &tracker)) {
NOTREACHED();
return;
}
DVLOG(1) << "Removing tracker: "
<< tracker.tracker_id() << " " << GetTrackerTitle(tracker);
RemoveFromAppIDIndex(tracker);
RemoveFromFileIDIndexes(tracker);
RemoveFromPathIndexes(tracker);
RemoveFromDirtyTrackerIndexes(tracker);
PutFileTrackerDeletionToDB(tracker_id, db_);
}
TrackerIDSet MetadataDatabaseIndexOnDisk::GetFileTrackerIDsByFileID(
const std::string& file_id) const {
return GetTrackerIDSetByPrefix(
GenerateActiveTrackerIDByFileIDKey(file_id),
GenerateTrackerIDByFileIDKeyPrefix(file_id));
}
int64_t MetadataDatabaseIndexOnDisk::GetAppRootTracker(
const std::string& app_id) const {
const std::string key = GenerateAppRootIDByAppIDKey(app_id);
std::string value;
leveldb::Status status = db_->Get(key, &value);
if (status.IsNotFound())
return kInvalidTrackerID;
if (!status.ok()) {
util::Log(logging::LOG_WARNING, FROM_HERE,
"LevelDB error (%s) in getting AppRoot for AppID: %s",
status.ToString().c_str(),
app_id.c_str());
return kInvalidTrackerID;
}
int64_t root_id;
if (!base::StringToInt64(value, &root_id)) {
util::Log(logging::LOG_WARNING, FROM_HERE,
"Failed to parse a root ID (%s) for an App ID: %s",
value.c_str(),
app_id.c_str());
return kInvalidTrackerID;
}
return root_id;
}
TrackerIDSet MetadataDatabaseIndexOnDisk::GetFileTrackerIDsByParentAndTitle(
int64_t parent_tracker_id,
const std::string& title) const {
return GetTrackerIDSetByPrefix(
GenerateActiveTrackerIDByParentAndTitleKey(parent_tracker_id, title),
GenerateTrackerIDByParentAndTitleKeyPrefix(parent_tracker_id, title));
}
std::vector<int64_t> MetadataDatabaseIndexOnDisk::GetFileTrackerIDsByParent(
int64_t parent_id) const {
std::vector<int64_t> result;
const std::string prefix = GenerateTrackerIDsByParentIDKeyPrefix(parent_id);
std::unique_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
const std::string& key(itr->key().ToString());
std::string title_and_id;
if (!RemovePrefix(key, prefix, &title_and_id))
break;
size_t pos = title_and_id.rfind('\0');
DCHECK(pos != std::string::npos);
int64_t tracker_id;
if (!base::StringToInt64(title_and_id.substr(pos + 1), &tracker_id))
continue;
result.push_back(tracker_id);
}
return result;
}
std::string MetadataDatabaseIndexOnDisk::PickMultiTrackerFileID() const {
std::unique_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
itr->Seek(kMultiTrackerByFileIDKeyPrefix);
if (!itr->Valid())
return std::string();
std::string file_id;
if (!RemovePrefix(itr->key().ToString(),
kMultiTrackerByFileIDKeyPrefix, &file_id))
return std::string();
return file_id;
}
ParentIDAndTitle MetadataDatabaseIndexOnDisk::PickMultiBackingFilePath() const {
std::unique_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
itr->Seek(kMultiBackingParentAndTitleKeyPrefix);
if (!itr->Valid())
return ParentIDAndTitle();
std::string value;
if (!RemovePrefix(itr->key().ToString(),
kMultiBackingParentAndTitleKeyPrefix, &value))
return ParentIDAndTitle();
size_t pos = value.find('\0'); // '\0' is a separator.
if (pos == std::string::npos)
return ParentIDAndTitle();
int64_t parent_id;
return base::StringToInt64(value.substr(0, pos), &parent_id) ?
ParentIDAndTitle(parent_id, value.substr(pos + 1)) : ParentIDAndTitle();
}
int64_t MetadataDatabaseIndexOnDisk::PickDirtyTracker() const {
std::unique_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
itr->Seek(kDirtyIDKeyPrefix);
if (!itr->Valid())
return kInvalidTrackerID;
std::string id_str;
if (!RemovePrefix(itr->key().ToString(), kDirtyIDKeyPrefix, &id_str))
return kInvalidTrackerID;
int64_t tracker_id;
if (!base::StringToInt64(id_str, &tracker_id))
return kInvalidTrackerID;
return tracker_id;
}
void MetadataDatabaseIndexOnDisk::DemoteDirtyTracker(int64_t tracker_id) {
const std::string key = GenerateDirtyIDKey(tracker_id);
std::string value;
leveldb::Status status = db_->Get(key, &value);
if (status.IsNotFound())
return;
if (!status.ok()) {
util::Log(logging::LOG_WARNING, FROM_HERE,
"LevelDB error (%s) in getting a dirty tracker for ID: %" PRId64,
status.ToString().c_str(),
tracker_id);
return;
}
db_->Delete(key);
db_->Put(GenerateDemotedDirtyIDKey(tracker_id), std::string());
--num_dirty_trackers_;
}
bool MetadataDatabaseIndexOnDisk::HasDemotedDirtyTracker() const {
std::unique_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
itr->Seek(kDemotedDirtyIDKeyPrefix);
if (!itr->Valid())
return false;
return base::StartsWith(itr->key().ToString(), kDemotedDirtyIDKeyPrefix,
base::CompareCase::SENSITIVE);
}
bool MetadataDatabaseIndexOnDisk::IsDemotedDirtyTracker(
int64_t tracker_id) const {
return DBHasKey(GenerateDemotedDirtyIDKey(tracker_id));
}
void MetadataDatabaseIndexOnDisk::PromoteDemotedDirtyTracker(
int64_t tracker_id) {
std::string demoted_key = GenerateDemotedDirtyIDKey(tracker_id);
std::string empty;
if (db_->Get(demoted_key, &empty).ok()) {
db_->Delete(demoted_key);
db_->Put(GenerateDirtyIDKey(tracker_id), std::string());
++num_dirty_trackers_;
}
}
bool MetadataDatabaseIndexOnDisk::PromoteDemotedDirtyTrackers() {
bool promoted = false;
std::unique_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
for (itr->Seek(kDemotedDirtyIDKeyPrefix); itr->Valid(); itr->Next()) {
std::string id_str;
if (!RemovePrefix(itr->key().ToString(), kDemotedDirtyIDKeyPrefix, &id_str))
break;
int64_t tracker_id;
if (!base::StringToInt64(id_str, &tracker_id))
continue;
db_->Delete(itr->key().ToString());
db_->Put(GenerateDirtyIDKey(tracker_id), std::string());
++num_dirty_trackers_;
promoted = true;
}
return promoted;
}
size_t MetadataDatabaseIndexOnDisk::CountDirtyTracker() const {
return num_dirty_trackers_;
}
size_t MetadataDatabaseIndexOnDisk::CountFileMetadata() const {
// TODO(peria): Cache the number of FileMetadata in the DB.
size_t count = 0;
std::unique_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
for (itr->Seek(kFileMetadataKeyPrefix); itr->Valid(); itr->Next()) {
if (!base::StartsWith(itr->key().ToString(), kFileMetadataKeyPrefix,
base::CompareCase::SENSITIVE))
break;
++count;
}
return count;
}
size_t MetadataDatabaseIndexOnDisk::CountFileTracker() const {
// TODO(peria): Cache the number of FileTracker in the DB.
size_t count = 0;
std::unique_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) {
if (!base::StartsWith(itr->key().ToString(), kFileTrackerKeyPrefix,
base::CompareCase::SENSITIVE))
break;
++count;
}
return count;
}
void MetadataDatabaseIndexOnDisk::SetSyncRootRevalidated() const {
service_metadata_->set_sync_root_revalidated(true);
PutServiceMetadataToDB(*service_metadata_, db_);
}
void MetadataDatabaseIndexOnDisk::SetSyncRootTrackerID(
int64_t sync_root_id) const {
service_metadata_->set_sync_root_tracker_id(sync_root_id);
PutServiceMetadataToDB(*service_metadata_, db_);
}
void MetadataDatabaseIndexOnDisk::SetLargestChangeID(
int64_t largest_change_id) const {
service_metadata_->set_largest_change_id(largest_change_id);
PutServiceMetadataToDB(*service_metadata_, db_);
}
void MetadataDatabaseIndexOnDisk::SetNextTrackerID(
int64_t next_tracker_id) const {
service_metadata_->set_next_tracker_id(next_tracker_id);
PutServiceMetadataToDB(*service_metadata_, db_);
}
bool MetadataDatabaseIndexOnDisk::IsSyncRootRevalidated() const {
return service_metadata_->has_sync_root_revalidated() &&
service_metadata_->sync_root_revalidated();
}
int64_t MetadataDatabaseIndexOnDisk::GetSyncRootTrackerID() const {
if (!service_metadata_->has_sync_root_tracker_id())
return kInvalidTrackerID;
return service_metadata_->sync_root_tracker_id();
}
int64_t MetadataDatabaseIndexOnDisk::GetLargestChangeID() const {
if (!service_metadata_->has_largest_change_id())
return kInvalidTrackerID;
return service_metadata_->largest_change_id();
}
int64_t MetadataDatabaseIndexOnDisk::GetNextTrackerID() const {
if (!service_metadata_->has_next_tracker_id())
return kInvalidTrackerID;
return service_metadata_->next_tracker_id();
}
std::vector<std::string>
MetadataDatabaseIndexOnDisk::GetRegisteredAppIDs() const {
std::vector<std::string> result;
std::unique_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
for (itr->Seek(kAppRootIDByAppIDKeyPrefix); itr->Valid(); itr->Next()) {
std::string id;
if (!RemovePrefix(itr->key().ToString(), kAppRootIDByAppIDKeyPrefix, &id))
break;
result.push_back(id);
}
return result;
}
std::vector<int64_t> MetadataDatabaseIndexOnDisk::GetAllTrackerIDs() const {
std::vector<int64_t> tracker_ids;
std::unique_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) {
std::string id_str;
if (!RemovePrefix(itr->key().ToString(), kFileTrackerKeyPrefix, &id_str))
break;
int64_t tracker_id;
if (!base::StringToInt64(id_str, &tracker_id))
continue;
tracker_ids.push_back(tracker_id);
}
return tracker_ids;
}
std::vector<std::string>
MetadataDatabaseIndexOnDisk::GetAllMetadataIDs() const {
std::vector<std::string> file_ids;
std::unique_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
for (itr->Seek(kFileMetadataKeyPrefix); itr->Valid(); itr->Next()) {
std::string file_id;
if (!RemovePrefix(itr->key().ToString(), kFileMetadataKeyPrefix, &file_id))
break;
file_ids.push_back(file_id);
}
return file_ids;
}
int64_t MetadataDatabaseIndexOnDisk::BuildTrackerIndexes() {
int64_t num_puts_before = db_->num_puts();
std::unique_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) {
if (!RemovePrefix(itr->key().ToString(), kFileTrackerKeyPrefix, nullptr))
break;
FileTracker tracker;
if (!tracker.ParseFromString(itr->value().ToString())) {
util::Log(logging::LOG_WARNING, FROM_HERE,
"Failed to parse a Tracker");
continue;
}
AddToAppIDIndex(tracker);
AddToFileIDIndexes(tracker);
AddToPathIndexes(tracker);
AddToDirtyTrackerIndexes(tracker);
}
return db_->num_puts() - num_puts_before;
}
int64_t MetadataDatabaseIndexOnDisk::DeleteTrackerIndexes() {
const char* kIndexPrefixes[] = {
kAppRootIDByAppIDKeyPrefix, kActiveTrackerIDByFileIDKeyPrefix,
kTrackerIDByFileIDKeyPrefix, kMultiTrackerByFileIDKeyPrefix,
kActiveTrackerIDByParentAndTitleKeyPrefix,
kTrackerIDByParentAndTitleKeyPrefix, kMultiBackingParentAndTitleKeyPrefix,
kDirtyIDKeyPrefix, kDemotedDirtyIDKeyPrefix
};
int64_t num_deletes_before = db_->num_deletes();
for (size_t i = 0; i < base::size(kIndexPrefixes); ++i)
DeleteKeyStartsWith(kIndexPrefixes[i]);
num_dirty_trackers_ = 0;
return db_->num_deletes() - num_deletes_before;
}
LevelDBWrapper* MetadataDatabaseIndexOnDisk::GetDBForTesting() {
return db_;
}
MetadataDatabaseIndexOnDisk::MetadataDatabaseIndexOnDisk(LevelDBWrapper* db)
: db_(db),
num_dirty_trackers_(0) {
// TODO(peria): Add UMA to measure the number of FileMetadata, FileTracker,
// and AppRootId.
service_metadata_ = InitializeServiceMetadata(db_);
// Check if index is valid, if no validations run in 7 days.
const int64_t kThresholdToValidateInDays = 7;
int64_t last_check_time = 0;
std::string value;
if (db_->Get(kLastValidationTimeKey, &value).ok())
base::StringToInt64(value, &last_check_time);
base::TimeDelta since_last_check =
base::Time::Now() - base::Time::FromInternalValue(last_check_time);
int64_t since_last_check_in_days = since_last_check.InDays();
if (since_last_check_in_days >= kThresholdToValidateInDays ||
since_last_check_in_days < 0) {
// TODO(peria): Add UMA to check if the number of deleted entries and the
// number of built entries are different or not.
DeleteTrackerIndexes();
BuildTrackerIndexes();
db_->Put(kLastValidationTimeKey,
base::Int64ToString(base::Time::Now().ToInternalValue()));
} else {
num_dirty_trackers_ = CountDirtyTrackerInternal();
}
}
void MetadataDatabaseIndexOnDisk::AddToAppIDIndex(const FileTracker& tracker) {
if (!IsAppRoot(tracker)) {
DVLOG(3) << " Tracker for " << tracker.file_id() << " is not an App root.";
return;
}
DVLOG(1) << " Add to App root by App ID: " << tracker.app_id();
const std::string db_key = GenerateAppRootIDByAppIDKey(tracker.app_id());
DCHECK(tracker.active());
DCHECK(!DBHasKey(db_key));
db_->Put(db_key, base::Int64ToString(tracker.tracker_id()));
}
void MetadataDatabaseIndexOnDisk::UpdateInAppIDIndex(
const FileTracker& old_tracker,
const FileTracker& new_tracker) {
DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
if (IsAppRoot(old_tracker) && !IsAppRoot(new_tracker)) {
DCHECK(old_tracker.active());
DCHECK(!new_tracker.active());
const std::string key = GenerateAppRootIDByAppIDKey(old_tracker.app_id());
DCHECK(DBHasKey(key));
DVLOG(1) << " Remove from App root by App ID: " << old_tracker.app_id();
db_->Delete(key);
} else if (!IsAppRoot(old_tracker) && IsAppRoot(new_tracker)) {
DCHECK(!old_tracker.active());
DCHECK(new_tracker.active());
const std::string key = GenerateAppRootIDByAppIDKey(new_tracker.app_id());
DCHECK(!DBHasKey(key));
DVLOG(1) << " Add to App root by App ID: " << new_tracker.app_id();
db_->Put(key, base::Int64ToString(new_tracker.tracker_id()));
}
}
void MetadataDatabaseIndexOnDisk::RemoveFromAppIDIndex(
const FileTracker& tracker) {
if (!IsAppRoot(tracker)) {
DVLOG(3) << " Tracker for " << tracker.file_id() << " is not an App root.";
return;
}
DCHECK(tracker.active());
const std::string key = GenerateAppRootIDByAppIDKey(tracker.app_id());
DCHECK(DBHasKey(key));
DVLOG(1) << " Remove from App root by App ID: " << tracker.app_id();
db_->Delete(key);
}
void MetadataDatabaseIndexOnDisk::AddToFileIDIndexes(
const FileTracker& new_tracker) {
const std::string& file_id = new_tracker.file_id();
DVLOG(1) << " Add to trackers by file ID: " << file_id;
const std::string prefix = GenerateTrackerIDByFileIDKeyPrefix(file_id);
AddToTrackerIDSetWithPrefix(
GenerateActiveTrackerIDByFileIDKey(file_id), prefix, new_tracker);
const std::string multi_tracker_key = GenerateMultiTrackerKey(file_id);
if (!DBHasKey(multi_tracker_key) &&
CountWithPrefix(prefix, new_tracker.tracker_id()) != NONE) {
DVLOG(1) << " Add to multi-tracker file IDs: " << file_id;
db_->Put(multi_tracker_key, std::string());
}
}
void MetadataDatabaseIndexOnDisk::UpdateInFileIDIndexes(
const FileTracker& old_tracker,
const FileTracker& new_tracker) {
DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
DCHECK_EQ(old_tracker.file_id(), new_tracker.file_id());
const std::string& file_id = new_tracker.file_id();
const std::string prefix = GenerateTrackerIDByFileIDKeyPrefix(file_id);
DCHECK(DBHasKey(prefix + base::Int64ToString(new_tracker.tracker_id())));
if (old_tracker.active() && !new_tracker.active()) {
DeactivateInTrackerIDSetWithPrefix(
GenerateActiveTrackerIDByFileIDKey(file_id), prefix,
new_tracker.tracker_id());
} else if (!old_tracker.active() && new_tracker.active()) {
ActivateInTrackerIDSetWithPrefix(
GenerateActiveTrackerIDByFileIDKey(file_id), prefix,
new_tracker.tracker_id());
}
}
void MetadataDatabaseIndexOnDisk::RemoveFromFileIDIndexes(
const FileTracker& tracker) {
const std::string& file_id = tracker.file_id();
const std::string prefix =
GenerateTrackerIDByFileIDKeyPrefix(file_id);
if (!EraseInTrackerIDSetWithPrefix(
GenerateActiveTrackerIDByFileIDKey(file_id),
prefix, tracker.tracker_id()))
return;
DVLOG(1) << " Remove from trackers by file ID: " << tracker.tracker_id();
const std::string multi_key = GenerateMultiTrackerKey(file_id);
if (DBHasKey(multi_key) &&
CountWithPrefix(prefix, tracker.tracker_id()) != MULTIPLE) {
DVLOG(1) << " Remove from multi-tracker file IDs: " << file_id;
db_->Delete(multi_key);
}
}
void MetadataDatabaseIndexOnDisk::AddToPathIndexes(
const FileTracker& new_tracker) {
int64_t parent_id = new_tracker.parent_tracker_id();
std::string title = GetTrackerTitle(new_tracker);
DVLOG(1) << " Add to trackers by parent and title: "
<< parent_id << " " << title;
const std::string prefix =
GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title);
if (!title.empty()) {
std::unique_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
std::string id_str;
if (!RemovePrefix(itr->key().ToString(), prefix, &id_str))
break;
int64_t tracker_id;
if (!base::StringToInt64(id_str, &tracker_id))
continue;
if (tracker_id == new_tracker.tracker_id()) {
NOTREACHED();
continue;
}
const std::string multi_key =
GenerateMultiBackingParentAndTitleKey(parent_id, title);
DVLOG_IF(1, !DBHasKey(multi_key))
<< " Add to multi backing file paths: " << parent_id << " " << title;
db_->Put(multi_key, std::string());
break;
}
}
AddToTrackerIDSetWithPrefix(
GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title),
prefix, new_tracker);
}
void MetadataDatabaseIndexOnDisk::UpdateInPathIndexes(
const FileTracker& old_tracker,
const FileTracker& new_tracker) {
DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
DCHECK_EQ(old_tracker.parent_tracker_id(), new_tracker.parent_tracker_id());
DCHECK(GetTrackerTitle(old_tracker) == GetTrackerTitle(new_tracker) ||
!old_tracker.has_synced_details());
int64_t tracker_id = new_tracker.tracker_id();
int64_t parent_id = new_tracker.parent_tracker_id();
const std::string old_title = GetTrackerTitle(old_tracker);
const std::string title = GetTrackerTitle(new_tracker);
if (old_title != title) {
const std::string old_prefix =
GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, old_title);
EraseInTrackerIDSetWithPrefix(
GenerateActiveTrackerIDByParentAndTitleKey(parent_id, old_title),
old_prefix, tracker_id);
if (!old_title.empty() &&
CountWithPrefix(old_prefix, tracker_id) != MULTIPLE) {
const std::string old_multi_backing_key =
GenerateMultiBackingParentAndTitleKey(parent_id, old_title);
DVLOG_IF(1, DBHasKey(old_multi_backing_key))
<< " Remove from multi backing file paths: "
<< parent_id << " " << old_title;
db_->Delete(old_multi_backing_key);
}
DVLOG(1) << " Add to trackers by parent and title: "
<< parent_id << " " << title;
const std::string prefix =
GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title);
AddToTrackerIDSetWithPrefix(
GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title),
prefix, new_tracker);
if (!title.empty() && CountWithPrefix(prefix, tracker_id) != NONE) {
const std::string multi_backing_key =
GenerateMultiBackingParentAndTitleKey(parent_id, title);
DVLOG_IF(1, !DBHasKey(multi_backing_key))
<< " Add to multi backing file_paths: "
<< parent_id << " " << title;
db_->Put(multi_backing_key, std::string());
}
return;
}
const std::string active_tracker_key =
GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title);
const std::string prefix =
GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title);
if (old_tracker.active() && !new_tracker.active()) {
DeactivateInTrackerIDSetWithPrefix(
active_tracker_key, prefix, tracker_id);
} else if (!old_tracker.active() && new_tracker.active()) {
ActivateInTrackerIDSetWithPrefix(
active_tracker_key, prefix, tracker_id);
}
}
void MetadataDatabaseIndexOnDisk::RemoveFromPathIndexes(
const FileTracker& tracker) {
int64_t tracker_id = tracker.tracker_id();
int64_t parent_id = tracker.parent_tracker_id();
std::string title = GetTrackerTitle(tracker);
DVLOG(1) << " Remove from trackers by parent and title: "
<< parent_id << " " << title;
const std::string active_tracker_key =
GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title);
const std::string key_prefix =
GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title);
if (!EraseInTrackerIDSetWithPrefix(
active_tracker_key, key_prefix, tracker_id))
return;
const std::string multi_key =
GenerateMultiBackingParentAndTitleKey(parent_id, title);
if (!title.empty() && DBHasKey(multi_key) &&
CountWithPrefix(key_prefix, tracker_id) != MULTIPLE) {
DVLOG(1) << " Remove from multi backing file paths: "
<< parent_id << " " << title;
db_->Delete(multi_key);
}
}
void MetadataDatabaseIndexOnDisk::AddToDirtyTrackerIndexes(
const FileTracker& new_tracker) {
const std::string dirty_key = GenerateDirtyIDKey(new_tracker.tracker_id());
DCHECK(!DBHasKey(dirty_key));
DCHECK(!DBHasKey(GenerateDemotedDirtyIDKey(new_tracker.tracker_id())));
if (new_tracker.dirty()) {
DVLOG(1) << " Add to dirty tracker IDs: " << new_tracker.tracker_id();
db_->Put(dirty_key, std::string());
++num_dirty_trackers_;
}
}
void MetadataDatabaseIndexOnDisk::UpdateInDirtyTrackerIndexes(
const FileTracker& old_tracker,
const FileTracker& new_tracker) {
DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
int64_t tracker_id = new_tracker.tracker_id();
const std::string dirty_key = GenerateDirtyIDKey(tracker_id);
const std::string demoted_key = GenerateDemotedDirtyIDKey(tracker_id);
if (old_tracker.dirty() && !new_tracker.dirty()) {
DCHECK(DBHasKey(dirty_key) || DBHasKey(demoted_key));
DVLOG(1) << " Remove from dirty trackers IDs: " << tracker_id;
if (DBHasKey(dirty_key))
--num_dirty_trackers_;
db_->Delete(dirty_key);
db_->Delete(demoted_key);
} else if (!old_tracker.dirty() && new_tracker.dirty()) {
DCHECK(!DBHasKey(dirty_key));
DCHECK(!DBHasKey(demoted_key));
DVLOG(1) << " Add to dirty tracker IDs: " << tracker_id;
db_->Put(dirty_key, std::string());
++num_dirty_trackers_;
}
}
void MetadataDatabaseIndexOnDisk::RemoveFromDirtyTrackerIndexes(
const FileTracker& tracker) {
if (tracker.dirty()) {
int64_t tracker_id = tracker.tracker_id();
const std::string dirty_key = GenerateDirtyIDKey(tracker_id);
const std::string demoted_key = GenerateDemotedDirtyIDKey(tracker_id);
DCHECK(DBHasKey(dirty_key) || DBHasKey(demoted_key));
DVLOG(1) << " Remove from dirty tracker IDs: " << tracker_id;
if (DBHasKey(dirty_key))
--num_dirty_trackers_;
db_->Delete(dirty_key);
db_->Delete(demoted_key);
}
}
TrackerIDSet MetadataDatabaseIndexOnDisk::GetTrackerIDSetByPrefix(
const std::string& active_tracker_key,
const std::string& ids_prefix) const {
TrackerIDSet trackers;
// Seek IDs.
std::unique_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
for (itr->Seek(ids_prefix); itr->Valid(); itr->Next()) {
const std::string& key(itr->key().ToString());
std::string id_str;
if (!RemovePrefix(key, ids_prefix, &id_str))
break;
int64_t tracker_id;
if (!base::StringToInt64(id_str, &tracker_id))
continue;
trackers.InsertInactiveTracker(tracker_id);
}
// Set an active tracker ID, if available.
std::string value;
leveldb::Status status = db_->Get(active_tracker_key, &value);
int64_t active_tracker;
if (status.ok() && base::StringToInt64(value, &active_tracker)) {
DCHECK_NE(kInvalidTrackerID, active_tracker);
trackers.Activate(active_tracker);
}
return trackers;
}
void MetadataDatabaseIndexOnDisk::AddToTrackerIDSetWithPrefix(
const std::string& active_tracker_key, const std::string& key_prefix,
const FileTracker& tracker) {
DCHECK(tracker.tracker_id());
const std::string id_str = base::Int64ToString(tracker.tracker_id());
db_->Put(key_prefix + id_str, std::string());
if (tracker.active())
db_->Put(active_tracker_key, id_str);
}
bool MetadataDatabaseIndexOnDisk::EraseInTrackerIDSetWithPrefix(
const std::string& active_tracker_key,
const std::string& key_prefix,
int64_t tracker_id) {
std::string value;
const std::string del_key = key_prefix + base::Int64ToString(tracker_id);
leveldb::Status status = db_->Get(del_key, &value);
if (status.IsNotFound())
return false;
db_->Delete(del_key);
status = db_->Get(active_tracker_key, &value);
int64_t active_tracker_id;
if (status.ok() && base::StringToInt64(value, &active_tracker_id) &&
active_tracker_id == tracker_id) {
db_->Delete(active_tracker_key);
}
return true;
}
void MetadataDatabaseIndexOnDisk::ActivateInTrackerIDSetWithPrefix(
const std::string& active_tracker_key,
const std::string& key_prefix,
int64_t tracker_id) {
DCHECK(DBHasKey(key_prefix + base::Int64ToString(tracker_id)));
std::string value;
leveldb::Status status = db_->Get(active_tracker_key, &value);
int64_t active_tracker_id = kInvalidTrackerID;
if (status.IsNotFound() ||
(status.ok() && base::StringToInt64(value, &active_tracker_id))) {
DCHECK(active_tracker_id != tracker_id);
db_->Put(active_tracker_key, base::Int64ToString(tracker_id));
}
}
void MetadataDatabaseIndexOnDisk::DeactivateInTrackerIDSetWithPrefix(
const std::string& active_tracker_key,
const std::string& key_prefix,
int64_t tracker_id) {
DCHECK(DBHasKey(key_prefix + base::Int64ToString(tracker_id)));
std::string value;
leveldb::Status status = db_->Get(active_tracker_key, &value);
int64_t active_tracker_id;
if (status.ok() && base::StringToInt64(value, &active_tracker_id)) {
DCHECK(active_tracker_id == tracker_id);
db_->Delete(active_tracker_key);
}
}
bool MetadataDatabaseIndexOnDisk::DBHasKey(const std::string& key) const {
std::unique_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
itr->Seek(key);
return itr->Valid() && (itr->key() == key);
}
size_t MetadataDatabaseIndexOnDisk::CountDirtyTrackerInternal() const {
size_t num_dirty_trackers = 0;
std::unique_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
for (itr->Seek(kDirtyIDKeyPrefix); itr->Valid(); itr->Next()) {
if (!base::StartsWith(itr->key().ToString(), kDirtyIDKeyPrefix,
base::CompareCase::SENSITIVE))
break;
++num_dirty_trackers;
}
return num_dirty_trackers;
}
MetadataDatabaseIndexOnDisk::NumEntries
MetadataDatabaseIndexOnDisk::CountWithPrefix(const std::string& prefix,
int64_t ignored_id) {
const std::string ignored = base::Int64ToString(ignored_id);
size_t count = 0;
std::unique_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
for (itr->Seek(prefix); itr->Valid() && count <= 1; itr->Next()) {
std::string value;
if (!RemovePrefix(itr->key().ToString(), prefix, &value))
break;
if (value == ignored)
continue;
++count;
}
if (count >= 2)
return MULTIPLE;
return count == 0 ? NONE : SINGLE;
}
void MetadataDatabaseIndexOnDisk::DeleteKeyStartsWith(
const std::string& prefix) {
std::unique_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
for (itr->Seek(prefix); itr->Valid();) {
const std::string key = itr->key().ToString();
if (!base::StartsWith(key, prefix, base::CompareCase::SENSITIVE))
break;
itr->Delete();
}
}
} // namespace drive_backend
} // namespace sync_file_system