Commit a3ed68bb authored by stanisc's avatar stanisc Committed by Commit bot

Sync: Avoid 3 passes over SyncDarta DB when loading the directory from the disk

According to profiling the first two read passes over SyncData DB
are from DirectoryBackingStore::DropDeletedEntries. This function
executes two SQL queries that drop entries pending deletion.
Since it is uncommon to have entries pending deletion it is far
more efficient to remove DirectoryBackingStore::DropDeletedEntries
and instead check for the entries matching this criteria inside
DirectoryBackingStore::LoadEntries, skip those entries from being
loaded and put them straight into Directory's metahandles_to_purge
collection which sets them up for deletion during a subsequent database
save.

See the bug for the preliminary performance results.

BUG=464073

Review URL: https://codereview.chromium.org/1008103002

Cr-Commit-Position: refs/heads/master@{#320861}
parent 69ca6641
...@@ -52,6 +52,7 @@ bool DeferredOnDiskDirectoryBackingStore::SaveChanges( ...@@ -52,6 +52,7 @@ bool DeferredOnDiskDirectoryBackingStore::SaveChanges(
DirOpenResult DeferredOnDiskDirectoryBackingStore::Load( DirOpenResult DeferredOnDiskDirectoryBackingStore::Load(
Directory::MetahandlesMap* handles_map, Directory::MetahandlesMap* handles_map,
JournalIndex* delete_journals, JournalIndex* delete_journals,
MetahandleSet* metahandles_to_purge,
Directory::KernelLoadInfo* kernel_load_info) { Directory::KernelLoadInfo* kernel_load_info) {
// Open an in-memory database at first to create initial sync data needed by // Open an in-memory database at first to create initial sync data needed by
// Directory. // Directory.
...@@ -61,7 +62,7 @@ DirOpenResult DeferredOnDiskDirectoryBackingStore::Load( ...@@ -61,7 +62,7 @@ DirOpenResult DeferredOnDiskDirectoryBackingStore::Load(
if (!InitializeTables()) if (!InitializeTables())
return FAILED_OPEN_DATABASE; return FAILED_OPEN_DATABASE;
if (!LoadEntries(handles_map)) if (!LoadEntries(handles_map, metahandles_to_purge))
return FAILED_DATABASE_CORRUPT; return FAILED_DATABASE_CORRUPT;
if (!LoadInfo(kernel_load_info)) if (!LoadInfo(kernel_load_info))
return FAILED_DATABASE_CORRUPT; return FAILED_DATABASE_CORRUPT;
......
...@@ -25,6 +25,7 @@ class SYNC_EXPORT_PRIVATE DeferredOnDiskDirectoryBackingStore ...@@ -25,6 +25,7 @@ class SYNC_EXPORT_PRIVATE DeferredOnDiskDirectoryBackingStore
~DeferredOnDiskDirectoryBackingStore() override; ~DeferredOnDiskDirectoryBackingStore() override;
DirOpenResult Load(Directory::MetahandlesMap* handles_map, DirOpenResult Load(Directory::MetahandlesMap* handles_map,
JournalIndex* delete_journals, JournalIndex* delete_journals,
MetahandleSet* metahandles_to_purge,
Directory::KernelLoadInfo* kernel_load_info) override; Directory::KernelLoadInfo* kernel_load_info) override;
bool SaveChanges(const Directory::SaveChangesSnapshot& snapshot) override; bool SaveChanges(const Directory::SaveChangesSnapshot& snapshot) override;
......
...@@ -31,6 +31,7 @@ class DeferredOnDiskDirectoryBackingStoreTest : public testing::Test { ...@@ -31,6 +31,7 @@ class DeferredOnDiskDirectoryBackingStoreTest : public testing::Test {
base::FilePath db_path_; base::FilePath db_path_;
Directory::MetahandlesMap handles_map_; Directory::MetahandlesMap handles_map_;
JournalIndex delete_journals_; JournalIndex delete_journals_;
MetahandleSet metahandles_to_purge_;
Directory::KernelLoadInfo kernel_load_info_; Directory::KernelLoadInfo kernel_load_info_;
}; };
...@@ -38,8 +39,9 @@ class DeferredOnDiskDirectoryBackingStoreTest : public testing::Test { ...@@ -38,8 +39,9 @@ class DeferredOnDiskDirectoryBackingStoreTest : public testing::Test {
TEST_F(DeferredOnDiskDirectoryBackingStoreTest, Load) { TEST_F(DeferredOnDiskDirectoryBackingStoreTest, Load) {
DeferredOnDiskDirectoryBackingStore store("test", db_path_); DeferredOnDiskDirectoryBackingStore store("test", db_path_);
EXPECT_EQ(OPENED, store.Load(&handles_map_, &delete_journals_, EXPECT_EQ(OPENED, store.Load(&handles_map_, &delete_journals_,
&kernel_load_info_)); &metahandles_to_purge_, &kernel_load_info_));
EXPECT_TRUE(delete_journals_.empty()); EXPECT_TRUE(delete_journals_.empty());
EXPECT_TRUE(metahandles_to_purge_.empty());
ASSERT_EQ(1u, handles_map_.size()); // root node ASSERT_EQ(1u, handles_map_.size()); // root node
ASSERT_TRUE(handles_map_.count(1)); ASSERT_TRUE(handles_map_.count(1));
EntryKernel* root = handles_map_[1]; EntryKernel* root = handles_map_[1];
...@@ -55,7 +57,7 @@ TEST_F(DeferredOnDiskDirectoryBackingStoreTest, ...@@ -55,7 +57,7 @@ TEST_F(DeferredOnDiskDirectoryBackingStoreTest,
// Open and close. // Open and close.
DeferredOnDiskDirectoryBackingStore store("test", db_path_); DeferredOnDiskDirectoryBackingStore store("test", db_path_);
EXPECT_EQ(OPENED, store.Load(&handles_map_, &delete_journals_, EXPECT_EQ(OPENED, store.Load(&handles_map_, &delete_journals_,
&kernel_load_info_)); &metahandles_to_purge_, &kernel_load_info_));
} }
EXPECT_FALSE(base::PathExists(db_path_)); EXPECT_FALSE(base::PathExists(db_path_));
...@@ -68,7 +70,7 @@ TEST_F(DeferredOnDiskDirectoryBackingStoreTest, ...@@ -68,7 +70,7 @@ TEST_F(DeferredOnDiskDirectoryBackingStoreTest,
// Open and close. // Open and close.
DeferredOnDiskDirectoryBackingStore store("test", db_path_); DeferredOnDiskDirectoryBackingStore store("test", db_path_);
EXPECT_EQ(OPENED, store.Load(&handles_map_, &delete_journals_, EXPECT_EQ(OPENED, store.Load(&handles_map_, &delete_journals_,
&kernel_load_info_)); &metahandles_to_purge_, &kernel_load_info_));
Directory::SaveChangesSnapshot snapshot; Directory::SaveChangesSnapshot snapshot;
store.SaveChanges(snapshot); store.SaveChanges(snapshot);
...@@ -83,7 +85,7 @@ TEST_F(DeferredOnDiskDirectoryBackingStoreTest, PersistWhenSavingValidChanges) { ...@@ -83,7 +85,7 @@ TEST_F(DeferredOnDiskDirectoryBackingStoreTest, PersistWhenSavingValidChanges) {
// Open and close. // Open and close.
DeferredOnDiskDirectoryBackingStore store("test", db_path_); DeferredOnDiskDirectoryBackingStore store("test", db_path_);
EXPECT_EQ(OPENED, store.Load(&handles_map_, &delete_journals_, EXPECT_EQ(OPENED, store.Load(&handles_map_, &delete_journals_,
&kernel_load_info_)); &metahandles_to_purge_, &kernel_load_info_));
Directory::SaveChangesSnapshot snapshot; Directory::SaveChangesSnapshot snapshot;
EntryKernel* entry = new EntryKernel(); EntryKernel* entry = new EntryKernel();
...@@ -98,8 +100,9 @@ TEST_F(DeferredOnDiskDirectoryBackingStoreTest, PersistWhenSavingValidChanges) { ...@@ -98,8 +100,9 @@ TEST_F(DeferredOnDiskDirectoryBackingStoreTest, PersistWhenSavingValidChanges) {
ASSERT_TRUE(base::PathExists(db_path_)); ASSERT_TRUE(base::PathExists(db_path_));
OnDiskDirectoryBackingStore read_store("test", db_path_); OnDiskDirectoryBackingStore read_store("test", db_path_);
EXPECT_EQ(OPENED, read_store.Load(&handles_map_, &delete_journals_, EXPECT_EQ(OPENED,
&kernel_load_info_)); read_store.Load(&handles_map_, &delete_journals_,
&metahandles_to_purge_, &kernel_load_info_));
ASSERT_EQ(2u, handles_map_.size()); ASSERT_EQ(2u, handles_map_.size());
ASSERT_TRUE(handles_map_.count(1)); // root node ASSERT_TRUE(handles_map_.count(1)); // root node
ASSERT_TRUE(handles_map_.count(2)); ASSERT_TRUE(handles_map_.count(2));
......
...@@ -77,7 +77,8 @@ Directory::SaveChangesSnapshot::~SaveChangesSnapshot() { ...@@ -77,7 +77,8 @@ Directory::SaveChangesSnapshot::~SaveChangesSnapshot() {
Directory::Kernel::Kernel( Directory::Kernel::Kernel(
const std::string& name, const std::string& name,
const KernelLoadInfo& info, DirectoryChangeDelegate* delegate, const KernelLoadInfo& info,
DirectoryChangeDelegate* delegate,
const WeakHandle<TransactionObserver>& transaction_observer) const WeakHandle<TransactionObserver>& transaction_observer)
: next_write_transaction_id(0), : next_write_transaction_id(0),
name(name), name(name),
...@@ -178,12 +179,13 @@ DirOpenResult Directory::OpenImpl( ...@@ -178,12 +179,13 @@ DirOpenResult Directory::OpenImpl(
// Avoids mem leaks on failure. Harmlessly deletes the empty hash map after // Avoids mem leaks on failure. Harmlessly deletes the empty hash map after
// the swap in the success case. // the swap in the success case.
STLValueDeleter<Directory::MetahandlesMap> deleter(&tmp_handles_map); STLValueDeleter<MetahandlesMap> deleter(&tmp_handles_map);
JournalIndex delete_journals; JournalIndex delete_journals;
MetahandleSet metahandles_to_purge;
DirOpenResult result = DirOpenResult result = store_->Load(&tmp_handles_map, &delete_journals,
store_->Load(&tmp_handles_map, &delete_journals, &info); &metahandles_to_purge, &info);
if (OPENED != result) if (OPENED != result)
return result; return result;
...@@ -195,6 +197,8 @@ DirOpenResult Directory::OpenImpl( ...@@ -195,6 +197,8 @@ DirOpenResult Directory::OpenImpl(
// prevent local ID reuse in the case of an early crash. See the comments in // prevent local ID reuse in the case of an early crash. See the comments in
// TakeSnapshotForSaveChanges() or crbug.com/142987 for more information. // TakeSnapshotForSaveChanges() or crbug.com/142987 for more information.
kernel_->info_status = KERNEL_SHARE_INFO_DIRTY; kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
kernel_->metahandles_to_purge.swap(metahandles_to_purge);
if (!SaveChanges()) if (!SaveChanges())
return FAILED_INITIAL_WRITE; return FAILED_INITIAL_WRITE;
......
...@@ -599,11 +599,12 @@ class SYNC_EXPORT Directory { ...@@ -599,11 +599,12 @@ class SYNC_EXPORT Directory {
// processed |snapshot| failed, for example, due to no disk space. // processed |snapshot| failed, for example, due to no disk space.
void HandleSaveChangesFailure(const SaveChangesSnapshot& snapshot); void HandleSaveChangesFailure(const SaveChangesSnapshot& snapshot);
// Used by CheckTreeInvariants // Used by CheckTreeInvariants.
void GetAllMetaHandles(BaseTransaction* trans, MetahandleSet* result); void GetAllMetaHandles(BaseTransaction* trans, MetahandleSet* result);
// Used by VacuumAfterSaveChanges.
bool SafeToPurgeFromMemory(WriteTransaction* trans, bool SafeToPurgeFromMemory(WriteTransaction* trans,
const EntryKernel* const entry) const; const EntryKernel* const entry) const;
// A helper used by GetTotalNodeCount. // A helper used by GetTotalNodeCount.
void GetChildSetForKernel( void GetChildSetForKernel(
BaseTransaction*, BaseTransaction*,
......
...@@ -538,8 +538,8 @@ bool DirectoryBackingStore::RefreshColumns() { ...@@ -538,8 +538,8 @@ bool DirectoryBackingStore::RefreshColumns() {
return true; return true;
} }
bool DirectoryBackingStore::LoadEntries( bool DirectoryBackingStore::LoadEntries(Directory::MetahandlesMap* handles_map,
Directory::MetahandlesMap* handles_map) { MetahandleSet* metahandles_to_purge) {
string select; string select;
select.reserve(kUpdateStatementBufferSize); select.reserve(kUpdateStatementBufferSize);
select.append("SELECT "); select.append("SELECT ");
...@@ -555,11 +555,25 @@ bool DirectoryBackingStore::LoadEntries( ...@@ -555,11 +555,25 @@ bool DirectoryBackingStore::LoadEntries(
return false; return false;
int64 handle = kernel->ref(META_HANDLE); int64 handle = kernel->ref(META_HANDLE);
(*handles_map)[handle] = kernel.release(); if (SafeToPurgeOnLoading(*kernel))
metahandles_to_purge->insert(handle);
else
(*handles_map)[handle] = kernel.release();
} }
return s.Succeeded(); return s.Succeeded();
} }
bool DirectoryBackingStore::SafeToPurgeOnLoading(
const EntryKernel& entry) const {
if (entry.ref(IS_DEL)) {
if (!entry.ref(IS_UNSYNCED) && !entry.ref(IS_UNAPPLIED_UPDATE))
return true;
else if (!entry.ref(ID).ServerKnows())
return true;
}
return false;
}
bool DirectoryBackingStore::LoadDeleteJournals( bool DirectoryBackingStore::LoadDeleteJournals(
JournalIndex* delete_journals) { JournalIndex* delete_journals) {
string select; string select;
...@@ -643,21 +657,6 @@ bool DirectoryBackingStore::SaveEntryToDB(sql::Statement* save_statement, ...@@ -643,21 +657,6 @@ bool DirectoryBackingStore::SaveEntryToDB(sql::Statement* save_statement,
return save_statement->Run(); return save_statement->Run();
} }
bool DirectoryBackingStore::DropDeletedEntries() {
if (!db_->Execute("DELETE FROM metas "
"WHERE is_del > 0 "
"AND is_unsynced < 1 "
"AND is_unapplied_update < 1")) {
return false;
}
if (!db_->Execute("DELETE FROM metas "
"WHERE is_del > 0 "
"AND id LIKE 'c%'")) {
return false;
}
return true;
}
bool DirectoryBackingStore::SafeDropTable(const char* table_name) { bool DirectoryBackingStore::SafeDropTable(const char* table_name) {
string query = "DROP TABLE IF EXISTS "; string query = "DROP TABLE IF EXISTS ";
query.append(table_name); query.append(table_name);
......
...@@ -49,16 +49,18 @@ class SYNC_EXPORT_PRIVATE DirectoryBackingStore : public base::NonThreadSafe { ...@@ -49,16 +49,18 @@ class SYNC_EXPORT_PRIVATE DirectoryBackingStore : public base::NonThreadSafe {
virtual ~DirectoryBackingStore(); virtual ~DirectoryBackingStore();
// Loads and drops all currently persisted meta entries into |handles_map| // Loads and drops all currently persisted meta entries into |handles_map|
// and loads appropriate persisted kernel info into |info_bucket|. // and loads appropriate persisted kernel info into |kernel_load_info|.
// The function determines which entries can be safely dropped and inserts
// their keys into |metahandles_to_purge|. It is up to the caller to
// perform the actual cleanup.
// //
// This function can perform some cleanup tasks behind the scenes. It will // This function will migrate to the latest database version.
// clean up unused entries from the database and migrate to the latest
// database version. The caller can safely ignore these details.
// //
// NOTE: On success (return value of OPENED), the buckets are populated with // NOTE: On success (return value of OPENED), the buckets are populated with
// newly allocated items, meaning ownership is bestowed upon the caller. // newly allocated items, meaning ownership is bestowed upon the caller.
virtual DirOpenResult Load(Directory::MetahandlesMap* handles_map, virtual DirOpenResult Load(Directory::MetahandlesMap* handles_map,
JournalIndex* delete_journals, JournalIndex* delete_journals,
MetahandleSet* metahandles_to_purge,
Directory::KernelLoadInfo* kernel_load_info) = 0; Directory::KernelLoadInfo* kernel_load_info) = 0;
// Updates the on-disk store with the input |snapshot| as a database // Updates the on-disk store with the input |snapshot| as a database
...@@ -90,16 +92,15 @@ class SYNC_EXPORT_PRIVATE DirectoryBackingStore : public base::NonThreadSafe { ...@@ -90,16 +92,15 @@ class SYNC_EXPORT_PRIVATE DirectoryBackingStore : public base::NonThreadSafe {
bool CreateV75ModelsTable(); bool CreateV75ModelsTable();
bool CreateV81ModelsTable(); bool CreateV81ModelsTable();
// We don't need to load any synced and applied deleted entries, we can
// in fact just purge them forever on startup.
bool DropDeletedEntries();
// Drops a table if it exists, harmless if the table did not already exist. // Drops a table if it exists, harmless if the table did not already exist.
bool SafeDropTable(const char* table_name); bool SafeDropTable(const char* table_name);
// Load helpers for entries and attributes. // Load helpers for entries and attributes.
bool LoadEntries(Directory::MetahandlesMap* handles_map); bool LoadEntries(Directory::MetahandlesMap* handles_map,
MetahandleSet* metahandles_to_purge);
bool LoadDeleteJournals(JournalIndex* delete_journals); bool LoadDeleteJournals(JournalIndex* delete_journals);
bool LoadInfo(Directory::KernelLoadInfo* info); bool LoadInfo(Directory::KernelLoadInfo* info);
bool SafeToPurgeOnLoading(const EntryKernel& entry) const;
// Save/update helpers for entries. Return false if sqlite commit fails. // Save/update helpers for entries. Return false if sqlite commit fails.
static bool SaveEntryToDB(sql::Statement* save_statement, static bool SaveEntryToDB(sql::Statement* save_statement,
......
...@@ -46,10 +46,11 @@ class MigrationTest : public testing::TestWithParam<int> { ...@@ -46,10 +46,11 @@ class MigrationTest : public testing::TestWithParam<int> {
static bool LoadAndIgnoreReturnedData(DirectoryBackingStore *dbs) { static bool LoadAndIgnoreReturnedData(DirectoryBackingStore *dbs) {
Directory::MetahandlesMap tmp_handles_map; Directory::MetahandlesMap tmp_handles_map;
JournalIndex delete_journals; JournalIndex delete_journals;
MetahandleSet metahandles_to_purge;
STLValueDeleter<Directory::MetahandlesMap> deleter(&tmp_handles_map); STLValueDeleter<Directory::MetahandlesMap> deleter(&tmp_handles_map);
Directory::KernelLoadInfo kernel_load_info; Directory::KernelLoadInfo kernel_load_info;
return dbs->Load(&tmp_handles_map, &delete_journals, &kernel_load_info) == return dbs->Load(&tmp_handles_map, &delete_journals, &metahandles_to_purge,
OPENED; &kernel_load_info) == OPENED;
} }
void SetUpVersion67Database(sql::Connection* connection); void SetUpVersion67Database(sql::Connection* connection);
...@@ -3202,12 +3203,14 @@ TEST_F(DirectoryBackingStoreTest, MigrateVersion78To79) { ...@@ -3202,12 +3203,14 @@ TEST_F(DirectoryBackingStoreTest, MigrateVersion78To79) {
// Ensure the next_id has been incremented. // Ensure the next_id has been incremented.
Directory::MetahandlesMap handles_map; Directory::MetahandlesMap handles_map;
JournalIndex delete_journals;; JournalIndex delete_journals;
MetahandleSet metahandles_to_purge;
STLValueDeleter<Directory::MetahandlesMap> deleter(&handles_map); STLValueDeleter<Directory::MetahandlesMap> deleter(&handles_map);
Directory::KernelLoadInfo load_info; Directory::KernelLoadInfo load_info;
s.Clear(); s.Clear();
ASSERT_TRUE(dbs->Load(&handles_map, &delete_journals, &load_info)); ASSERT_TRUE(dbs->Load(&handles_map, &delete_journals, &metahandles_to_purge,
&load_info));
EXPECT_LE(load_info.kernel_info.next_id, kInitialNextId - 65536); EXPECT_LE(load_info.kernel_info.next_id, kInitialNextId - 65536);
} }
...@@ -3225,11 +3228,13 @@ TEST_F(DirectoryBackingStoreTest, MigrateVersion79To80) { ...@@ -3225,11 +3228,13 @@ TEST_F(DirectoryBackingStoreTest, MigrateVersion79To80) {
// Ensure the bag_of_chips has been set. // Ensure the bag_of_chips has been set.
Directory::MetahandlesMap handles_map; Directory::MetahandlesMap handles_map;
JournalIndex delete_journals;; JournalIndex delete_journals;
MetahandleSet metahandles_to_purge;
STLValueDeleter<Directory::MetahandlesMap> deleter(&handles_map); STLValueDeleter<Directory::MetahandlesMap> deleter(&handles_map);
Directory::KernelLoadInfo load_info; Directory::KernelLoadInfo load_info;
ASSERT_TRUE(dbs->Load(&handles_map, &delete_journals, &load_info)); ASSERT_TRUE(dbs->Load(&handles_map, &delete_journals, &metahandles_to_purge,
&load_info));
// Check that the initial value is the serialization of an empty ChipBag. // Check that the initial value is the serialization of an empty ChipBag.
sync_pb::ChipBag chip_bag; sync_pb::ChipBag chip_bag;
std::string serialized_chip_bag; std::string serialized_chip_bag;
...@@ -3438,11 +3443,13 @@ TEST_F(DirectoryBackingStoreTest, DetectInvalidPosition) { ...@@ -3438,11 +3443,13 @@ TEST_F(DirectoryBackingStoreTest, DetectInvalidPosition) {
// Trying to unpack this entry should signal that the DB is corrupted. // Trying to unpack this entry should signal that the DB is corrupted.
Directory::MetahandlesMap handles_map; Directory::MetahandlesMap handles_map;
JournalIndex delete_journals;; JournalIndex delete_journals;
MetahandleSet metahandles_to_purge;
STLValueDeleter<Directory::MetahandlesMap> deleter(&handles_map); STLValueDeleter<Directory::MetahandlesMap> deleter(&handles_map);
Directory::KernelLoadInfo kernel_load_info; Directory::KernelLoadInfo kernel_load_info;
ASSERT_EQ(FAILED_DATABASE_CORRUPT, ASSERT_EQ(FAILED_DATABASE_CORRUPT,
dbs->Load(&handles_map, &delete_journals, &kernel_load_info)); dbs->Load(&handles_map, &delete_journals, &metahandles_to_purge,
&kernel_load_info));
} }
TEST_P(MigrationTest, ToCurrentVersion) { TEST_P(MigrationTest, ToCurrentVersion) {
...@@ -3529,13 +3536,17 @@ TEST_P(MigrationTest, ToCurrentVersion) { ...@@ -3529,13 +3536,17 @@ TEST_P(MigrationTest, ToCurrentVersion) {
syncable::Directory::KernelLoadInfo dir_info; syncable::Directory::KernelLoadInfo dir_info;
Directory::MetahandlesMap handles_map; Directory::MetahandlesMap handles_map;
JournalIndex delete_journals;; JournalIndex delete_journals;
MetahandleSet metahandles_to_purge;
STLValueDeleter<Directory::MetahandlesMap> index_deleter(&handles_map); STLValueDeleter<Directory::MetahandlesMap> index_deleter(&handles_map);
{ {
scoped_ptr<TestDirectoryBackingStore> dbs( scoped_ptr<TestDirectoryBackingStore> dbs(
new TestDirectoryBackingStore(GetUsername(), &connection)); new TestDirectoryBackingStore(GetUsername(), &connection));
ASSERT_EQ(OPENED, dbs->Load(&handles_map, &delete_journals, &dir_info)); ASSERT_EQ(OPENED, dbs->Load(&handles_map, &delete_journals,
&metahandles_to_purge, &dir_info));
if (!metahandles_to_purge.empty())
dbs->DeleteEntries(metahandles_to_purge);
ASSERT_FALSE(dbs->needs_column_refresh_); ASSERT_FALSE(dbs->needs_column_refresh_);
ASSERT_EQ(kCurrentDBVersion, dbs->GetVersion()); ASSERT_EQ(kCurrentDBVersion, dbs->GetVersion());
} }
...@@ -3903,20 +3914,22 @@ TEST_F(DirectoryBackingStoreTest, DeleteEntries) { ...@@ -3903,20 +3914,22 @@ TEST_F(DirectoryBackingStoreTest, DeleteEntries) {
new TestDirectoryBackingStore(GetUsername(), &connection)); new TestDirectoryBackingStore(GetUsername(), &connection));
Directory::MetahandlesMap handles_map; Directory::MetahandlesMap handles_map;
JournalIndex delete_journals; JournalIndex delete_journals;
MetahandleSet metahandles_to_purge;
Directory::KernelLoadInfo kernel_load_info; Directory::KernelLoadInfo kernel_load_info;
STLValueDeleter<Directory::MetahandlesMap> index_deleter(&handles_map); STLValueDeleter<Directory::MetahandlesMap> index_deleter(&handles_map);
dbs->Load(&handles_map, &delete_journals, &kernel_load_info); dbs->Load(&handles_map, &delete_journals, &metahandles_to_purge,
&kernel_load_info);
size_t initial_size = handles_map.size(); size_t initial_size = handles_map.size();
ASSERT_LT(0U, initial_size) << "Test requires handles_map to delete."; ASSERT_LT(0U, initial_size) << "Test requires handles_map to delete.";
int64 first_to_die = handles_map.begin()->second->ref(META_HANDLE); int64 first_to_die = handles_map.begin()->second->ref(META_HANDLE);
MetahandleSet to_delete; MetahandleSet to_delete;
to_delete.insert(first_to_die); to_delete.insert(first_to_die);
EXPECT_TRUE(dbs->DeleteEntries(TestDirectoryBackingStore::METAS_TABLE, EXPECT_TRUE(dbs->DeleteEntries(to_delete));
to_delete));
STLDeleteValues(&handles_map); STLDeleteValues(&handles_map);
dbs->LoadEntries(&handles_map); metahandles_to_purge.clear();
dbs->LoadEntries(&handles_map, &metahandles_to_purge);
EXPECT_EQ(initial_size - 1, handles_map.size()); EXPECT_EQ(initial_size - 1, handles_map.size());
bool delete_failed = false; bool delete_failed = false;
...@@ -3935,11 +3948,11 @@ TEST_F(DirectoryBackingStoreTest, DeleteEntries) { ...@@ -3935,11 +3948,11 @@ TEST_F(DirectoryBackingStoreTest, DeleteEntries) {
to_delete.insert(it->first); to_delete.insert(it->first);
} }
EXPECT_TRUE(dbs->DeleteEntries(TestDirectoryBackingStore::METAS_TABLE, EXPECT_TRUE(dbs->DeleteEntries(to_delete));
to_delete));
STLDeleteValues(&handles_map); STLDeleteValues(&handles_map);
dbs->LoadEntries(&handles_map); metahandles_to_purge.clear();
dbs->LoadEntries(&handles_map, &metahandles_to_purge);
EXPECT_EQ(0U, handles_map.size()); EXPECT_EQ(0U, handles_map.size());
} }
......
...@@ -552,6 +552,87 @@ TEST_F(SyncableDirectoryTest, ManageDeleteJournals) { ...@@ -552,6 +552,87 @@ TEST_F(SyncableDirectoryTest, ManageDeleteJournals) {
} }
} }
TEST_F(SyncableDirectoryTest, TestPurgeDeletedEntriesOnReload) {
sync_pb::EntitySpecifics specifics;
AddDefaultFieldValue(PREFERENCES, &specifics);
const int kClientCount = 2;
const int kServerCount = 5;
const int kTestCount = kClientCount + kServerCount;
int64 handles[kTestCount];
// The idea is to recreate various combinations of IDs, IS_DEL,
// IS_UNSYNCED, and IS_UNAPPLIED_UPDATE flags to test all combinations
// for DirectoryBackingStore::SafeToPurgeOnLoading.
// 0: client ID, IS_DEL, IS_UNSYNCED
// 1: client ID, IS_UNSYNCED
// 2: server ID, IS_DEL, IS_UNSYNCED, IS_UNAPPLIED_UPDATE
// 3: server ID, IS_DEL, IS_UNSYNCED
// 4: server ID, IS_DEL, IS_UNAPPLIED_UPDATE
// 5: server ID, IS_DEL
// 6: server ID
{
WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
for (int i = 0; i < kTestCount; i++) {
std::string name = base::StringPrintf("item%d", i);
MutableEntry item(&trans, CREATE, PREFERENCES, trans.root_id(), name);
ASSERT_TRUE(item.good());
handles[i] = item.GetMetahandle();
if (i < kClientCount) {
item.PutId(TestIdFactory::FromNumber(i - kClientCount));
} else {
item.PutId(TestIdFactory::FromNumber(i));
}
item.PutUniqueClientTag(name);
item.PutIsUnsynced(true);
item.PutSpecifics(specifics);
item.PutServerSpecifics(specifics);
if (i >= kClientCount) {
item.PutBaseVersion(10);
item.PutServerVersion(10);
}
// Set flags
if (i != 1 && i != 6)
item.PutIsDel(true);
if (i >= 4)
item.PutIsUnsynced(false);
if (i == 2 || i == 4)
item.PutIsUnappliedUpdate(true);
}
}
ASSERT_EQ(OPENED, SimulateSaveAndReloadDir());
// Expect items 0 and 5 to be purged according to
// DirectoryBackingStore::SafeToPurgeOnLoading:
// - Item 0 is an item with IS_DEL flag and client ID.
// - Item 5 is an item with IS_DEL flag which has both
// IS_UNSYNCED and IS_UNAPPLIED_UPDATE unset.
std::vector<int64> expected_purged;
expected_purged.push_back(0);
expected_purged.push_back(5);
std::vector<int64> actually_purged;
{
ReadTransaction trans(FROM_HERE, dir().get());
for (int i = 0; i < kTestCount; i++) {
Entry item(&trans, GET_BY_HANDLE, handles[i]);
if (!item.good()) {
actually_purged.push_back(i);
}
}
}
EXPECT_EQ(expected_purged, actually_purged);
}
TEST_F(SyncableDirectoryTest, TestBasicLookupNonExistantID) { TEST_F(SyncableDirectoryTest, TestBasicLookupNonExistantID) {
ReadTransaction rtrans(FROM_HERE, dir().get()); ReadTransaction rtrans(FROM_HERE, dir().get());
Entry e(&rtrans, GET_BY_ID, TestIdFactory::FromNumber(-99)); Entry e(&rtrans, GET_BY_ID, TestIdFactory::FromNumber(-99));
......
...@@ -16,6 +16,7 @@ InMemoryDirectoryBackingStore::InMemoryDirectoryBackingStore( ...@@ -16,6 +16,7 @@ InMemoryDirectoryBackingStore::InMemoryDirectoryBackingStore(
DirOpenResult InMemoryDirectoryBackingStore::Load( DirOpenResult InMemoryDirectoryBackingStore::Load(
Directory::MetahandlesMap* handles_map, Directory::MetahandlesMap* handles_map,
JournalIndex* delete_journals, JournalIndex* delete_journals,
MetahandleSet* metahandles_to_purge,
Directory::KernelLoadInfo* kernel_load_info) { Directory::KernelLoadInfo* kernel_load_info) {
if (!db_->is_open()) { if (!db_->is_open()) {
if (!db_->OpenInMemory()) if (!db_->OpenInMemory())
...@@ -32,9 +33,7 @@ DirOpenResult InMemoryDirectoryBackingStore::Load( ...@@ -32,9 +33,7 @@ DirOpenResult InMemoryDirectoryBackingStore::Load(
} }
} }
if (!DropDeletedEntries()) if (!LoadEntries(handles_map, metahandles_to_purge))
return FAILED_DATABASE_CORRUPT;
if (!LoadEntries(handles_map))
return FAILED_DATABASE_CORRUPT; return FAILED_DATABASE_CORRUPT;
if (!LoadDeleteJournals(delete_journals)) if (!LoadDeleteJournals(delete_journals))
return FAILED_DATABASE_CORRUPT; return FAILED_DATABASE_CORRUPT;
......
...@@ -26,6 +26,7 @@ class SYNC_EXPORT_PRIVATE InMemoryDirectoryBackingStore ...@@ -26,6 +26,7 @@ class SYNC_EXPORT_PRIVATE InMemoryDirectoryBackingStore
explicit InMemoryDirectoryBackingStore(const std::string& dir_name); explicit InMemoryDirectoryBackingStore(const std::string& dir_name);
DirOpenResult Load(Directory::MetahandlesMap* handles_map, DirOpenResult Load(Directory::MetahandlesMap* handles_map,
JournalIndex* delete_journals, JournalIndex* delete_journals,
MetahandleSet* metahandles_to_purge,
Directory::KernelLoadInfo* kernel_load_info) override; Directory::KernelLoadInfo* kernel_load_info) override;
void request_consistent_cache_guid() { void request_consistent_cache_guid() {
......
...@@ -17,6 +17,7 @@ InvalidDirectoryBackingStore::~InvalidDirectoryBackingStore() { ...@@ -17,6 +17,7 @@ InvalidDirectoryBackingStore::~InvalidDirectoryBackingStore() {
DirOpenResult InvalidDirectoryBackingStore::Load( DirOpenResult InvalidDirectoryBackingStore::Load(
Directory::MetahandlesMap* handles_map, Directory::MetahandlesMap* handles_map,
JournalIndex* delete_journals, JournalIndex* delete_journals,
MetahandleSet* metahandles_to_purge,
Directory::KernelLoadInfo* kernel_load_info) { Directory::KernelLoadInfo* kernel_load_info) {
return FAILED_OPEN_DATABASE; return FAILED_OPEN_DATABASE;
} }
......
...@@ -19,6 +19,7 @@ class SYNC_EXPORT_PRIVATE InvalidDirectoryBackingStore ...@@ -19,6 +19,7 @@ class SYNC_EXPORT_PRIVATE InvalidDirectoryBackingStore
~InvalidDirectoryBackingStore() override; ~InvalidDirectoryBackingStore() override;
DirOpenResult Load(Directory::MetahandlesMap* handles_map, DirOpenResult Load(Directory::MetahandlesMap* handles_map,
JournalIndex* delete_journals, JournalIndex* delete_journals,
MetahandleSet* metahandles_to_purge,
Directory::KernelLoadInfo* kernel_load_info) override; Directory::KernelLoadInfo* kernel_load_info) override;
private: private:
......
...@@ -184,9 +184,9 @@ void MutableEntry::PutIsDel(bool value) { ...@@ -184,9 +184,9 @@ void MutableEntry::PutIsDel(bool value) {
// - Ensure that the item is never committed to the server. // - Ensure that the item is never committed to the server.
// - Allow any items with the same UNIQUE_CLIENT_TAG created on other // - Allow any items with the same UNIQUE_CLIENT_TAG created on other
// clients to override this entry. // clients to override this entry.
// - Let us delete this entry permanently through // - Let us delete this entry permanently when we next restart sync - see
// DirectoryBackingStore::DropDeletedEntries() when we next restart sync. // DirectoryBackingStore::SafeToPurgeOnLoading.
// This will save memory and avoid crbug.com/125381. // This will avoid crbug.com/125381.
// Note: do not unset IsUnsynced if the syncer is in the middle of // Note: do not unset IsUnsynced if the syncer is in the middle of
// attempting to commit this entity. // attempting to commit this entity.
if (!GetId().ServerKnows() && !GetSyncing()) { if (!GetId().ServerKnows() && !GetSyncing()) {
......
...@@ -37,6 +37,7 @@ OnDiskDirectoryBackingStore::~OnDiskDirectoryBackingStore() { } ...@@ -37,6 +37,7 @@ OnDiskDirectoryBackingStore::~OnDiskDirectoryBackingStore() { }
DirOpenResult OnDiskDirectoryBackingStore::TryLoad( DirOpenResult OnDiskDirectoryBackingStore::TryLoad(
Directory::MetahandlesMap* handles_map, Directory::MetahandlesMap* handles_map,
JournalIndex* delete_journals, JournalIndex* delete_journals,
MetahandleSet* metahandles_to_purge,
Directory::KernelLoadInfo* kernel_load_info) { Directory::KernelLoadInfo* kernel_load_info) {
DCHECK(CalledOnValidThread()); DCHECK(CalledOnValidThread());
if (!db_->is_open()) { if (!db_->is_open()) {
...@@ -47,9 +48,7 @@ DirOpenResult OnDiskDirectoryBackingStore::TryLoad( ...@@ -47,9 +48,7 @@ DirOpenResult OnDiskDirectoryBackingStore::TryLoad(
if (!InitializeTables()) if (!InitializeTables())
return FAILED_OPEN_DATABASE; return FAILED_OPEN_DATABASE;
if (!DropDeletedEntries()) if (!LoadEntries(handles_map, metahandles_to_purge))
return FAILED_DATABASE_CORRUPT;
if (!LoadEntries(handles_map))
return FAILED_DATABASE_CORRUPT; return FAILED_DATABASE_CORRUPT;
if (!LoadDeleteJournals(delete_journals)) if (!LoadDeleteJournals(delete_journals))
return FAILED_DATABASE_CORRUPT; return FAILED_DATABASE_CORRUPT;
...@@ -65,9 +64,10 @@ DirOpenResult OnDiskDirectoryBackingStore::TryLoad( ...@@ -65,9 +64,10 @@ DirOpenResult OnDiskDirectoryBackingStore::TryLoad(
DirOpenResult OnDiskDirectoryBackingStore::Load( DirOpenResult OnDiskDirectoryBackingStore::Load(
Directory::MetahandlesMap* handles_map, Directory::MetahandlesMap* handles_map,
JournalIndex* delete_journals, JournalIndex* delete_journals,
MetahandleSet* metahandles_to_purge,
Directory::KernelLoadInfo* kernel_load_info) { Directory::KernelLoadInfo* kernel_load_info) {
DirOpenResult result = TryLoad(handles_map, delete_journals, DirOpenResult result = TryLoad(handles_map, delete_journals,
kernel_load_info); metahandles_to_purge, kernel_load_info);
if (result == OPENED) { if (result == OPENED) {
UMA_HISTOGRAM_ENUMERATION( UMA_HISTOGRAM_ENUMERATION(
"Sync.DirectoryOpenResult", FIRST_TRY_SUCCESS, RESULT_COUNT); "Sync.DirectoryOpenResult", FIRST_TRY_SUCCESS, RESULT_COUNT);
...@@ -89,7 +89,8 @@ DirOpenResult OnDiskDirectoryBackingStore::Load( ...@@ -89,7 +89,8 @@ DirOpenResult OnDiskDirectoryBackingStore::Load(
db_->set_histogram_tag("SyncDirectory"); db_->set_histogram_tag("SyncDirectory");
base::DeleteFile(backing_filepath_, false); base::DeleteFile(backing_filepath_, false);
result = TryLoad(handles_map, delete_journals, kernel_load_info); result = TryLoad(handles_map, delete_journals, metahandles_to_purge,
kernel_load_info);
if (result == OPENED) { if (result == OPENED) {
UMA_HISTOGRAM_ENUMERATION( UMA_HISTOGRAM_ENUMERATION(
"Sync.DirectoryOpenResult", SECOND_TRY_SUCCESS, RESULT_COUNT); "Sync.DirectoryOpenResult", SECOND_TRY_SUCCESS, RESULT_COUNT);
......
...@@ -22,14 +22,15 @@ class SYNC_EXPORT_PRIVATE OnDiskDirectoryBackingStore ...@@ -22,14 +22,15 @@ class SYNC_EXPORT_PRIVATE OnDiskDirectoryBackingStore
~OnDiskDirectoryBackingStore() override; ~OnDiskDirectoryBackingStore() override;
DirOpenResult Load(Directory::MetahandlesMap* handles_map, DirOpenResult Load(Directory::MetahandlesMap* handles_map,
JournalIndex* delete_journals, JournalIndex* delete_journals,
MetahandleSet* metahandles_to_purge,
Directory::KernelLoadInfo* kernel_load_info) override; Directory::KernelLoadInfo* kernel_load_info) override;
// A helper function that will make one attempt to load the directory. // A helper function that will make one attempt to load the directory.
// Unlike Load(), it does not attempt to recover from failure. // Unlike Load(), it does not attempt to recover from failure.
DirOpenResult TryLoad( DirOpenResult TryLoad(Directory::MetahandlesMap* handles_map,
Directory::MetahandlesMap* handles_map, JournalIndex* delete_journals,
JournalIndex* delete_journals, MetahandleSet* metahandles_to_purge,
Directory::KernelLoadInfo* kernel_load_info); Directory::KernelLoadInfo* kernel_load_info);
protected: protected:
// Subclasses may override this to avoid a possible DCHECK. // Subclasses may override this to avoid a possible DCHECK.
......
...@@ -24,15 +24,14 @@ TestDirectoryBackingStore::~TestDirectoryBackingStore() { ...@@ -24,15 +24,14 @@ TestDirectoryBackingStore::~TestDirectoryBackingStore() {
DirOpenResult TestDirectoryBackingStore::Load( DirOpenResult TestDirectoryBackingStore::Load(
Directory::MetahandlesMap* handles_map, Directory::MetahandlesMap* handles_map,
JournalIndex* delete_journals, JournalIndex* delete_journals,
MetahandleSet* metahandles_to_purge,
Directory::KernelLoadInfo* kernel_load_info) { Directory::KernelLoadInfo* kernel_load_info) {
DCHECK(db_->is_open()); DCHECK(db_->is_open());
if (!InitializeTables()) if (!InitializeTables())
return FAILED_OPEN_DATABASE; return FAILED_OPEN_DATABASE;
if (!DropDeletedEntries()) if (!LoadEntries(handles_map, metahandles_to_purge))
return FAILED_DATABASE_CORRUPT;
if (!LoadEntries(handles_map))
return FAILED_DATABASE_CORRUPT; return FAILED_DATABASE_CORRUPT;
if (!LoadDeleteJournals(delete_journals)) if (!LoadDeleteJournals(delete_journals))
return FAILED_DATABASE_CORRUPT; return FAILED_DATABASE_CORRUPT;
...@@ -44,5 +43,10 @@ DirOpenResult TestDirectoryBackingStore::Load( ...@@ -44,5 +43,10 @@ DirOpenResult TestDirectoryBackingStore::Load(
return OPENED; return OPENED;
} }
bool TestDirectoryBackingStore::DeleteEntries(const MetahandleSet& handles) {
return DirectoryBackingStore::DeleteEntries(
DirectoryBackingStore::METAS_TABLE, handles);
}
} // namespace syncable } // namespace syncable
} // namespace syncer } // namespace syncer
...@@ -27,8 +27,9 @@ class TestDirectoryBackingStore : public DirectoryBackingStore { ...@@ -27,8 +27,9 @@ class TestDirectoryBackingStore : public DirectoryBackingStore {
~TestDirectoryBackingStore() override; ~TestDirectoryBackingStore() override;
DirOpenResult Load(Directory::MetahandlesMap* handles_map, DirOpenResult Load(Directory::MetahandlesMap* handles_map,
JournalIndex* delete_journals, JournalIndex* delete_journals,
MetahandleSet* metahandles_to_purge,
Directory::KernelLoadInfo* kernel_load_info) override; Directory::KernelLoadInfo* kernel_load_info) override;
bool DeleteEntries(const MetahandleSet& handles);
FRIEND_TEST_ALL_PREFIXES(DirectoryBackingStoreTest, MigrateVersion67To68); FRIEND_TEST_ALL_PREFIXES(DirectoryBackingStoreTest, MigrateVersion67To68);
FRIEND_TEST_ALL_PREFIXES(DirectoryBackingStoreTest, MigrateVersion68To69); FRIEND_TEST_ALL_PREFIXES(DirectoryBackingStoreTest, MigrateVersion68To69);
FRIEND_TEST_ALL_PREFIXES(DirectoryBackingStoreTest, MigrateVersion69To70); FRIEND_TEST_ALL_PREFIXES(DirectoryBackingStoreTest, MigrateVersion69To70);
......
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