Commit 6303ed7a authored by tzik@chromium.org's avatar tzik@chromium.org

[SyncFS] Add MetadataDatabase::UpdateTracker

BUG=

Review URL: https://chromiumcodereview.appspot.com/23288007

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@220565 0039d316-1c4b-4281-b951-d872f2087c98
parent 954bbe4d
...@@ -681,7 +681,8 @@ void MetadataDatabase::UpdateByChangeList( ...@@ -681,7 +681,8 @@ void MetadataDatabase::UpdateByChangeList(
WriteToDatabase(batch.Pass(), callback); WriteToDatabase(batch.Pass(), callback);
} }
void MetadataDatabase::PopulateFolder(const std::string& folder_id, void MetadataDatabase::PopulateFolderByChildList(
const std::string& folder_id,
const FileIDList& child_file_ids, const FileIDList& child_file_ids,
const SyncStatusCallback& callback) { const SyncStatusCallback& callback) {
TrackerSet trackers; TrackerSet trackers;
...@@ -711,7 +712,7 @@ void MetadataDatabase::PopulateFolder(const std::string& folder_id, ...@@ -711,7 +712,7 @@ void MetadataDatabase::PopulateFolder(const std::string& folder_id,
itr != child_file_ids.end(); ++itr) itr != child_file_ids.end(); ++itr)
CreateTrackerForParentAndFileID(*folder_tracker, *itr, batch.get()); CreateTrackerForParentAndFileID(*folder_tracker, *itr, batch.get());
folder_tracker->set_needs_folder_listing(false); folder_tracker->set_needs_folder_listing(false);
if (!ShouldKeepDirty(*folder_tracker)) { if (folder_tracker->dirty() && !ShouldKeepDirty(*folder_tracker)) {
folder_tracker->set_dirty(false); folder_tracker->set_dirty(false);
dirty_trackers_.erase(folder_tracker); dirty_trackers_.erase(folder_tracker);
} }
...@@ -720,6 +721,63 @@ void MetadataDatabase::PopulateFolder(const std::string& folder_id, ...@@ -720,6 +721,63 @@ void MetadataDatabase::PopulateFolder(const std::string& folder_id,
WriteToDatabase(batch.Pass(), callback); WriteToDatabase(batch.Pass(), callback);
} }
void MetadataDatabase::UpdateTracker(int64 tracker_id,
const FileDetails& updated_details,
const SyncStatusCallback& callback) {
TrackerByID::iterator found = tracker_by_id_.find(tracker_id);
if (found == tracker_by_id_.end()) {
RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
return;
}
FileTracker* tracker = found->second;
DCHECK(tracker);
scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
if (updated_details.deleted()) {
// The update deletes the local file.
FileByID::iterator found = file_by_id_.find(tracker->file_id());
if (found == file_by_id_.end() || found->second->details().deleted()) {
// Both the tracker and metadata have the deleted flag, now it's safe to
// delete the |tracker|.
RemoveTracker(tracker->tracker_id(), batch.get());
} else {
// The local file is deleted, but corresponding remote file isn't.
// Put the tracker back to the initial state.
tracker->clear_synced_details();
tracker->set_dirty(true);
tracker->set_active(false);
PutTrackerToBatch(*tracker, batch.get());
}
WriteToDatabase(batch.Pass(), callback);
return;
}
// Check if the tracker was retitled. If it was, update the title and its
// index in advance.
if (!tracker->has_synced_details() ||
tracker->synced_details().title() != updated_details.title()) {
UpdateTrackerTitle(tracker, updated_details.title(), batch.get());
}
*tracker->mutable_synced_details() = updated_details;
// Activate the tracker if:
// - There is no active tracker that tracks |tracker->file_id()|.
// - There is no active tracker that has the same |parent| and |title|.
if (!tracker->active() && CanActivateTracker(*tracker))
MakeTrackerActive(tracker->tracker_id(), batch.get());
if (tracker->dirty() && !ShouldKeepDirty(*tracker)) {
tracker->set_dirty(false);
dirty_trackers_.erase(tracker);
}
PutTrackerToBatch(*tracker, batch.get());
WriteToDatabase(batch.Pass(), callback);
}
MetadataDatabase::MetadataDatabase(base::SequencedTaskRunner* task_runner) MetadataDatabase::MetadataDatabase(base::SequencedTaskRunner* task_runner)
: task_runner_(task_runner), weak_ptr_factory_(this) { : task_runner_(task_runner), weak_ptr_factory_(this) {
DCHECK(task_runner); DCHECK(task_runner);
...@@ -961,9 +1019,21 @@ void MetadataDatabase::CreateTrackerForParentAndFileID( ...@@ -961,9 +1019,21 @@ void MetadataDatabase::CreateTrackerForParentAndFileID(
tracker_by_id_[tracker_id] = tracker.release(); tracker_by_id_[tracker_id] = tracker.release();
} }
void MetadataDatabase::RemoveTracker(int64 tracker_id,
leveldb::WriteBatch* batch) {
RemoveTrackerInternal(tracker_id, batch, false);
}
void MetadataDatabase::RemoveTrackerIgnoringSiblings( void MetadataDatabase::RemoveTrackerIgnoringSiblings(
int64 tracker_id, int64 tracker_id,
leveldb::WriteBatch* batch) { leveldb::WriteBatch* batch) {
RemoveTrackerInternal(tracker_id, batch, true);
}
void MetadataDatabase::RemoveTrackerInternal(
int64 tracker_id,
leveldb::WriteBatch* batch,
bool ignoring_siblings) {
scoped_ptr<FileTracker> tracker( scoped_ptr<FileTracker> tracker(
FindAndEraseItem(&tracker_by_id_, tracker_id)); FindAndEraseItem(&tracker_by_id_, tracker_id));
if (!tracker) if (!tracker)
...@@ -975,8 +1045,11 @@ void MetadataDatabase::RemoveTrackerIgnoringSiblings( ...@@ -975,8 +1045,11 @@ void MetadataDatabase::RemoveTrackerIgnoringSiblings(
EraseTrackerFromPathIndex(tracker.get()); EraseTrackerFromPathIndex(tracker.get());
MarkTrackersDirtyByFileID(tracker->file_id(), batch); MarkTrackersDirtyByFileID(tracker->file_id(), batch);
// Do not mark the same path trackers as dirty, since the caller is deleting if (!ignoring_siblings) {
// all its siblings. MarkTrackersDirtyByPath(tracker->parent_tracker_id(),
GetTrackerTitle(*tracker),
batch);
}
PutTrackerDeletionToBatch(tracker_id, batch); PutTrackerDeletionToBatch(tracker_id, batch);
} }
...@@ -1139,7 +1212,27 @@ void MetadataDatabase::RecursiveMarkTrackerAsDirty(int64 root_tracker_id, ...@@ -1139,7 +1212,27 @@ void MetadataDatabase::RecursiveMarkTrackerAsDirty(int64 root_tracker_id,
} }
} }
bool MetadataDatabase::CanActivateTracker(const FileTracker& tracker) {
DCHECK(!tracker.active());
DCHECK_NE(service_metadata_->sync_root_tracker_id(), tracker.tracker_id());
if (HasActiveTrackerForFileID(tracker.file_id()))
return false;
if (tracker.app_id().empty())
return false;
if (!tracker.has_synced_details())
return false;
DCHECK(tracker.parent_tracker_id());
return !HasActiveTrackerForPath(tracker.parent_tracker_id(),
tracker.synced_details().title());
}
bool MetadataDatabase::ShouldKeepDirty(const FileTracker& tracker) const { bool MetadataDatabase::ShouldKeepDirty(const FileTracker& tracker) const {
if (HasDisabledAppRoot(tracker))
return false;
DCHECK(tracker.dirty()); DCHECK(tracker.dirty());
if (!tracker.has_synced_details()) if (!tracker.has_synced_details())
return true; return true;
...@@ -1147,17 +1240,18 @@ bool MetadataDatabase::ShouldKeepDirty(const FileTracker& tracker) const { ...@@ -1147,17 +1240,18 @@ bool MetadataDatabase::ShouldKeepDirty(const FileTracker& tracker) const {
FileByID::const_iterator found = file_by_id_.find(tracker.file_id()); FileByID::const_iterator found = file_by_id_.find(tracker.file_id());
if (found == file_by_id_.end()) if (found == file_by_id_.end())
return true; return true;
const FileMetadata& file = *found->second; const FileMetadata* file = found->second;
DCHECK(file);
if (tracker.active()) { if (tracker.active()) {
if (tracker.needs_folder_listing()) if (tracker.needs_folder_listing())
return true; return true;
if (tracker.synced_details().md5() != file.details().md5()) if (tracker.synced_details().md5() != file->details().md5())
return true; return true;
} }
const FileDetails& local_details = tracker.synced_details(); const FileDetails& local_details = tracker.synced_details();
const FileDetails& remote_details = file.details(); const FileDetails& remote_details = file->details();
if (local_details.title() != remote_details.title()) if (local_details.title() != remote_details.title())
return true; return true;
...@@ -1167,6 +1261,76 @@ bool MetadataDatabase::ShouldKeepDirty(const FileTracker& tracker) const { ...@@ -1167,6 +1261,76 @@ bool MetadataDatabase::ShouldKeepDirty(const FileTracker& tracker) const {
return false; return false;
} }
bool MetadataDatabase::HasDisabledAppRoot(const FileTracker& tracker) const {
TrackerByAppID::const_iterator found =
app_root_by_app_id_.find(tracker.app_id());
if (found == app_root_by_app_id_.end())
return false;
const FileTracker* app_root_tracker = found->second;
DCHECK(app_root_tracker);
return app_root_tracker->tracker_kind() == TRACKER_KIND_DISABLED_APP_ROOT;
}
bool MetadataDatabase::HasActiveTrackerForFileID(
const std::string& file_id) const {
TrackersByFileID::const_iterator found = trackers_by_file_id_.find(file_id);
return found != trackers_by_file_id_.end() && found->second.has_active();
}
bool MetadataDatabase::HasActiveTrackerForPath(int64 parent_tracker_id,
const std::string& title) const {
TrackersByParentAndTitle::const_iterator found_by_parent =
trackers_by_parent_and_title_.find(parent_tracker_id);
if (found_by_parent == trackers_by_parent_and_title_.end())
return false;
const TrackersByTitle& trackers_by_title = found_by_parent->second;
TrackersByTitle::const_iterator found = trackers_by_title.find(title);
return found != trackers_by_title.end() && found->second.has_active();
}
void MetadataDatabase::UpdateTrackerTitle(FileTracker* tracker,
const std::string& new_title,
leveldb::WriteBatch* batch) {
int64 parent_id = tracker->parent_tracker_id();
std::string old_title = GetTrackerTitle(*tracker);
DCHECK_NE(old_title, new_title);
DCHECK(!new_title.empty());
TrackersByTitle* trackers_by_title =
&trackers_by_parent_and_title_[parent_id];
TrackerSet* old_siblings = &(*trackers_by_title)[old_title];
TrackerSet* new_siblings = &(*trackers_by_title)[new_title];
old_siblings->Erase(tracker);
if (old_siblings->empty())
trackers_by_title->erase(old_title);
else
MarkTrackerSetDirty(old_siblings, batch);
if (tracker->active() && new_siblings->has_active()) {
// Inactivate existing active tracker.
FileTracker* obstacle = new_siblings->active_tracker();
new_siblings->Inactivate(obstacle);
DCHECK_EQ(TRACKER_KIND_REGULAR, obstacle->tracker_kind());
TrackerSet* same_file_id_trackers_to_obstacle =
&trackers_by_file_id_[obstacle->file_id()];
same_file_id_trackers_to_obstacle->Inactivate(obstacle);
MarkTrackerSetDirty(same_file_id_trackers_to_obstacle, batch);
obstacle->set_active(false);
PutTrackerToBatch(*obstacle, batch);
RemoveAllDescendantTrackers(obstacle->tracker_id(), batch);
}
tracker->mutable_synced_details()->set_title(new_title);
new_siblings->Insert(tracker);
PutTrackerToBatch(*tracker, batch);
}
void MetadataDatabase::WriteToDatabase(scoped_ptr<leveldb::WriteBatch> batch, void MetadataDatabase::WriteToDatabase(scoped_ptr<leveldb::WriteBatch> batch,
const SyncStatusCallback& callback) { const SyncStatusCallback& callback) {
base::PostTaskAndReplyWithResult( base::PostTaskAndReplyWithResult(
......
...@@ -191,10 +191,15 @@ class MetadataDatabase { ...@@ -191,10 +191,15 @@ class MetadataDatabase {
// Adds |child_file_ids| to |folder_id| as its children. // Adds |child_file_ids| to |folder_id| as its children.
// This method affects the active tracker only. // This method affects the active tracker only.
// If the tracker has no further change to sync, unmarks its dirty flag. // If the tracker has no further change to sync, unmarks its dirty flag.
void PopulateFolder(const std::string& folder_id, void PopulateFolderByChildList(const std::string& folder_id,
const FileIDList& child_file_ids, const FileIDList& child_file_ids,
const SyncStatusCallback& callback); const SyncStatusCallback& callback);
// Updates |synced_details| of the tracker with |updated_details|.
void UpdateTracker(int64 tracker_id,
const FileDetails& updated_details,
const SyncStatusCallback& callback);
private: private:
struct DirtyTrackerComparator { struct DirtyTrackerComparator {
bool operator()(const FileTracker* left, bool operator()(const FileTracker* left,
...@@ -233,8 +238,13 @@ class MetadataDatabase { ...@@ -233,8 +238,13 @@ class MetadataDatabase {
void CreateTrackerForParentAndFileID(const FileTracker& parent_tracker, void CreateTrackerForParentAndFileID(const FileTracker& parent_tracker,
const std::string& file_id, const std::string& file_id,
leveldb::WriteBatch* batch); leveldb::WriteBatch* batch);
void RemoveTracker(int64 tracker_id, leveldb::WriteBatch* batch);
void RemoveTrackerIgnoringSiblings(int64 tracker_id, void RemoveTrackerIgnoringSiblings(int64 tracker_id,
leveldb::WriteBatch* batch); leveldb::WriteBatch* batch);
void RemoveTrackerInternal(int64 tracker_id,
leveldb::WriteBatch* batch,
bool ignoring_siblings);
void MaybeAddTrackersForNewFile(const FileMetadata& file, void MaybeAddTrackersForNewFile(const FileMetadata& file,
leveldb::WriteBatch* batch); leveldb::WriteBatch* batch);
...@@ -256,8 +266,18 @@ class MetadataDatabase { ...@@ -256,8 +266,18 @@ class MetadataDatabase {
void RecursiveMarkTrackerAsDirty(int64 root_tracker_id, void RecursiveMarkTrackerAsDirty(int64 root_tracker_id,
leveldb::WriteBatch* batch); leveldb::WriteBatch* batch);
bool CanActivateTracker(const FileTracker& tracker);
bool ShouldKeepDirty(const FileTracker& tracker) const; bool ShouldKeepDirty(const FileTracker& tracker) const;
bool HasDisabledAppRoot(const FileTracker& tracker) const;
bool HasActiveTrackerForFileID(const std::string& file_id) const;
bool HasActiveTrackerForPath(int64 parent_tracker,
const std::string& title) const;
void UpdateTrackerTitle(FileTracker* tracker,
const std::string& new_title,
leveldb::WriteBatch* batch);
void WriteToDatabase(scoped_ptr<leveldb::WriteBatch> batch, void WriteToDatabase(scoped_ptr<leveldb::WriteBatch> batch,
const SyncStatusCallback& callback); const SyncStatusCallback& callback);
......
...@@ -28,7 +28,11 @@ const int64 kInitialChangeID = 1234; ...@@ -28,7 +28,11 @@ const int64 kInitialChangeID = 1234;
const int64 kSyncRootTrackerID = 100; const int64 kSyncRootTrackerID = 100;
const char kSyncRootFolderID[] = "sync_root_folder_id"; const char kSyncRootFolderID[] = "sync_root_folder_id";
// This struct is used to setup initial state of the database in the test and
// also used to match to the modified content of the database as the
// expectation.
struct TrackedFile { struct TrackedFile {
// Holds the latest remote metadata which may be not-yet-synced to |tracker|.
FileMetadata metadata; FileMetadata metadata;
FileTracker tracker; FileTracker tracker;
...@@ -585,13 +589,22 @@ class MetadataDatabaseTest : public testing::Test { ...@@ -585,13 +589,22 @@ class MetadataDatabaseTest : public testing::Test {
SyncStatusCode PopulateFolder(const std::string& folder_id, SyncStatusCode PopulateFolder(const std::string& folder_id,
const FileIDList& listed_children) { const FileIDList& listed_children) {
SyncStatusCode status = SYNC_STATUS_UNKNOWN; SyncStatusCode status = SYNC_STATUS_UNKNOWN;
metadata_database_->PopulateFolder( metadata_database_->PopulateFolderByChildList(
folder_id, listed_children, folder_id, listed_children,
base::Bind(&SyncStatusResultCallback, &status)); base::Bind(&SyncStatusResultCallback, &status));
message_loop_.RunUntilIdle(); message_loop_.RunUntilIdle();
return status; return status;
} }
SyncStatusCode UpdateTracker(const FileTracker& tracker) {
SyncStatusCode status = SYNC_STATUS_UNKNOWN;
metadata_database_->UpdateTracker(
tracker.tracker_id(), tracker.synced_details(),
base::Bind(&SyncStatusResultCallback, &status));
message_loop_.RunUntilIdle();
return status;
}
private: private:
base::ScopedTempDir database_dir_; base::ScopedTempDir database_dir_;
base::MessageLoop message_loop_; base::MessageLoop message_loop_;
...@@ -908,5 +921,56 @@ TEST_F(MetadataDatabaseTest, PopulateFolderTest_DisabledAppRoot) { ...@@ -908,5 +921,56 @@ TEST_F(MetadataDatabaseTest, PopulateFolderTest_DisabledAppRoot) {
VerifyReloadConsistency(); VerifyReloadConsistency();
} }
TEST_F(MetadataDatabaseTest, UpdateTrackerTest) {
TrackedFile sync_root(CreateTrackedSyncRoot());
TrackedFile app_root(CreateTrackedAppRoot(sync_root, "app_root"));
TrackedFile file(CreateTrackedFile(app_root, "file"));
file.tracker.set_dirty(true);
file.metadata.mutable_details()->set_title("renamed file");;
TrackedFile inactive_file(CreateTrackedFile(app_root, "inactive_file"));
inactive_file.tracker.set_active(false);
inactive_file.tracker.set_dirty(true);
inactive_file.metadata.mutable_details()->set_title("renamed inactive file");
inactive_file.metadata.mutable_details()->set_md5("modified_md5");
TrackedFile new_conflict(CreateTrackedFile(app_root, "new conflict file"));
new_conflict.tracker.set_dirty(true);
new_conflict.metadata.mutable_details()->set_title("renamed file");
const TrackedFile* tracked_files[] = {
&sync_root, &app_root, &file, &inactive_file, &new_conflict
};
SetUpDatabaseByTrackedFiles(tracked_files, arraysize(tracked_files));
EXPECT_EQ(SYNC_STATUS_OK, InitializeMetadataDatabase());
VerifyTrackedFiles(tracked_files, arraysize(tracked_files));
VerifyReloadConsistency();
*file.tracker.mutable_synced_details() = file.metadata.details();
file.tracker.set_dirty(false);
EXPECT_EQ(SYNC_STATUS_OK, UpdateTracker(file.tracker));
VerifyTrackedFiles(tracked_files, arraysize(tracked_files));
VerifyReloadConsistency();
*inactive_file.tracker.mutable_synced_details() =
inactive_file.metadata.details();
inactive_file.tracker.set_dirty(false);
inactive_file.tracker.set_active(true);
EXPECT_EQ(SYNC_STATUS_OK, UpdateTracker(inactive_file.tracker));
VerifyTrackedFiles(tracked_files, arraysize(tracked_files));
VerifyReloadConsistency();
*new_conflict.tracker.mutable_synced_details() =
new_conflict.metadata.details();
new_conflict.tracker.set_dirty(false);
new_conflict.tracker.set_active(true);
file.tracker.set_dirty(true);
file.tracker.set_active(false);
EXPECT_EQ(SYNC_STATUS_OK, UpdateTracker(new_conflict.tracker));
VerifyTrackedFiles(tracked_files, arraysize(tracked_files));
VerifyReloadConsistency();
}
} // namespace drive_backend } // namespace drive_backend
} // namespace sync_file_system } // namespace sync_file_system
...@@ -33,6 +33,7 @@ class TrackerSet { ...@@ -33,6 +33,7 @@ class TrackerSet {
void Inactivate(FileTracker* tracker); void Inactivate(FileTracker* tracker);
const FileTracker* active_tracker() const { return active_tracker_; } const FileTracker* active_tracker() const { return active_tracker_; }
FileTracker* active_tracker() { return active_tracker_; }
const RawTrackerSet& tracker_set() const { return tracker_set_; } const RawTrackerSet& tracker_set() const { return tracker_set_; }
iterator begin() { return tracker_set_.begin(); } iterator begin() { return tracker_set_.begin(); }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment