Commit e8e8e4be authored by Troy Hildebrandt's avatar Troy Hildebrandt Committed by Commit Bot

Add IsCorrupt() to ProtoDatabase wrappers/clients.

Adds the ability to check for corruption via an IsCorrupt() function
that's pure virtual on the ProtoDatabase. IsCorrupt() is also introduced
in the ProtoLevelDBWrapper, and the corruption flag gets set on the Init
callback after checking status.

Unit tests changed slightly because the init callback needed to be put
into ProtoLevelDBWrapper instead of outside so it could modify
is_corrupt_, and the new use of the WeakPtr caused a race condition that
meant the test always failed to trigger the callback if we don't wait
for init to complete.

Bug: 870813
Change-Id: Ib00435dae8cdac490053fbfc6befb95094b1edec
Reviewed-on: https://chromium-review.googlesource.com/c/1355521
Commit-Queue: Troy Hildebrandt <thildebr@chromium.org>
Reviewed-by: default avatarTommy Nyquist <nyquist@chromium.org>
Cr-Commit-Position: refs/heads/master@{#612434}
parent aa067347
...@@ -115,6 +115,8 @@ class ProtoDatabase { ...@@ -115,6 +115,8 @@ class ProtoDatabase {
// Asynchronously destroys the database. // Asynchronously destroys the database.
virtual void Destroy(DestroyCallback callback) = 0; virtual void Destroy(DestroyCallback callback) = 0;
virtual bool IsCorrupt() = 0;
protected: protected:
ProtoDatabase() = default; ProtoDatabase() = default;
}; };
......
...@@ -10,11 +10,6 @@ namespace leveldb_proto { ...@@ -10,11 +10,6 @@ namespace leveldb_proto {
namespace { namespace {
void RunInitCallback(typename ProtoLevelDBWrapper::InitCallback callback,
const leveldb::Status* status) {
std::move(callback).Run(status->ok());
}
inline void InitFromTaskRunner(LevelDB* database, inline void InitFromTaskRunner(LevelDB* database,
const base::FilePath& database_dir, const base::FilePath& database_dir,
const leveldb_env::Options& options, const leveldb_env::Options& options,
...@@ -66,19 +61,26 @@ inline void LoadKeysFromTaskRunner(LevelDB* database, ...@@ -66,19 +61,26 @@ inline void LoadKeysFromTaskRunner(LevelDB* database,
ProtoLevelDBWrapper::ProtoLevelDBWrapper( ProtoLevelDBWrapper::ProtoLevelDBWrapper(
const scoped_refptr<base::SequencedTaskRunner>& task_runner) const scoped_refptr<base::SequencedTaskRunner>& task_runner)
: task_runner_(task_runner) { : task_runner_(task_runner), weak_ptr_factory_(this) {
DETACH_FROM_SEQUENCE(sequence_checker_); DETACH_FROM_SEQUENCE(sequence_checker_);
} }
ProtoLevelDBWrapper::ProtoLevelDBWrapper( ProtoLevelDBWrapper::ProtoLevelDBWrapper(
const scoped_refptr<base::SequencedTaskRunner>& task_runner, const scoped_refptr<base::SequencedTaskRunner>& task_runner,
LevelDB* db) LevelDB* db)
: task_runner_(task_runner), db_(db) { : task_runner_(task_runner), db_(db), weak_ptr_factory_(this) {
DETACH_FROM_SEQUENCE(sequence_checker_); DETACH_FROM_SEQUENCE(sequence_checker_);
} }
ProtoLevelDBWrapper::~ProtoLevelDBWrapper() = default; ProtoLevelDBWrapper::~ProtoLevelDBWrapper() = default;
void ProtoLevelDBWrapper::RunInitCallback(
typename ProtoLevelDBWrapper::InitCallback callback,
const leveldb::Status* status) {
is_corrupt_ = status->IsCorruption();
std::move(callback).Run(status->ok());
}
void ProtoLevelDBWrapper::InitWithDatabase( void ProtoLevelDBWrapper::InitWithDatabase(
LevelDB* database, LevelDB* database,
const base::FilePath& database_dir, const base::FilePath& database_dir,
...@@ -94,7 +96,8 @@ void ProtoLevelDBWrapper::InitWithDatabase( ...@@ -94,7 +96,8 @@ void ProtoLevelDBWrapper::InitWithDatabase(
FROM_HERE, FROM_HERE,
base::BindOnce(InitFromTaskRunner, base::Unretained(db_), database_dir, base::BindOnce(InitFromTaskRunner, base::Unretained(db_), database_dir,
options, destroy_on_corruption, status, metrics_id_), options, destroy_on_corruption, status, metrics_id_),
base::BindOnce(RunInitCallback, std::move(callback), base::BindOnce(&ProtoLevelDBWrapper::RunInitCallback,
weak_ptr_factory_.GetWeakPtr(), std::move(callback),
base::Owned(status))); base::Owned(status)));
} }
...@@ -139,9 +142,16 @@ void ProtoLevelDBWrapper::SetMetricsId(const std::string& id) { ...@@ -139,9 +142,16 @@ void ProtoLevelDBWrapper::SetMetricsId(const std::string& id) {
} }
bool ProtoLevelDBWrapper::GetApproximateMemoryUse(uint64_t* approx_mem_use) { bool ProtoLevelDBWrapper::GetApproximateMemoryUse(uint64_t* approx_mem_use) {
if (db_ == nullptr)
return 0;
return db_->GetApproximateMemoryUse(approx_mem_use); return db_->GetApproximateMemoryUse(approx_mem_use);
} }
bool ProtoLevelDBWrapper::IsCorrupt() {
return is_corrupt_;
}
const scoped_refptr<base::SequencedTaskRunner>& const scoped_refptr<base::SequencedTaskRunner>&
ProtoLevelDBWrapper::task_runner() { ProtoLevelDBWrapper::task_runner() {
return task_runner_; return task_runner_;
......
...@@ -133,6 +133,9 @@ class ProtoLevelDBWrapper { ...@@ -133,6 +133,9 @@ class ProtoLevelDBWrapper {
void Destroy(DestroyCallback callback); void Destroy(DestroyCallback callback);
void RunInitCallback(typename ProtoLevelDBWrapper::InitCallback callback,
const leveldb::Status* status);
// Allow callers to provide their own Database implementation. // Allow callers to provide their own Database implementation.
void InitWithDatabase(LevelDB* database, void InitWithDatabase(LevelDB* database,
const base::FilePath& database_dir, const base::FilePath& database_dir,
...@@ -142,6 +145,7 @@ class ProtoLevelDBWrapper { ...@@ -142,6 +145,7 @@ class ProtoLevelDBWrapper {
void SetMetricsId(const std::string& id); void SetMetricsId(const std::string& id);
bool IsCorrupt();
bool GetApproximateMemoryUse(uint64_t* approx_mem_use); bool GetApproximateMemoryUse(uint64_t* approx_mem_use);
const scoped_refptr<base::SequencedTaskRunner>& task_runner(); const scoped_refptr<base::SequencedTaskRunner>& task_runner();
...@@ -149,6 +153,9 @@ class ProtoLevelDBWrapper { ...@@ -149,6 +153,9 @@ class ProtoLevelDBWrapper {
private: private:
SEQUENCE_CHECKER(sequence_checker_); SEQUENCE_CHECKER(sequence_checker_);
// Set to true if the status from the previous Init call is corruption.
bool is_corrupt_ = false;
// Used to run blocking tasks in-order, must be the TaskRunner that |db_| // Used to run blocking tasks in-order, must be the TaskRunner that |db_|
// relies on. // relies on.
scoped_refptr<base::SequencedTaskRunner> task_runner_; scoped_refptr<base::SequencedTaskRunner> task_runner_;
...@@ -158,6 +165,8 @@ class ProtoLevelDBWrapper { ...@@ -158,6 +165,8 @@ class ProtoLevelDBWrapper {
// LevelDB calls, likely the database client name. // LevelDB calls, likely the database client name.
std::string metrics_id_ = "Default"; std::string metrics_id_ = "Default";
base::WeakPtrFactory<ProtoLevelDBWrapper> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(ProtoLevelDBWrapper); DISALLOW_COPY_AND_ASSIGN(ProtoLevelDBWrapper);
}; };
......
...@@ -84,6 +84,10 @@ void SharedProtoDatabase::OnDatabaseInit( ...@@ -84,6 +84,10 @@ void SharedProtoDatabase::OnDatabaseInit(
bool success) { bool success) {
DCHECK_CALLED_ON_VALID_SEQUENCE(on_task_runner_); DCHECK_CALLED_ON_VALID_SEQUENCE(on_task_runner_);
init_state_ = success ? InitState::kSuccess : InitState::kFailure; init_state_ = success ? InitState::kSuccess : InitState::kFailure;
// TODO(thildebr): Check the db_wrapper_->IsCorrupt() and store corruption
// information to inform clients they may have lost data.
callback_task_runner->PostTask(FROM_HERE, callback_task_runner->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), success)); base::BindOnce(std::move(callback), success));
} }
......
...@@ -41,14 +41,14 @@ class SharedProtoDatabaseClient : public ProtoDatabase<T> { ...@@ -41,14 +41,14 @@ class SharedProtoDatabaseClient : public ProtoDatabase<T> {
public: public:
virtual ~SharedProtoDatabaseClient(); virtual ~SharedProtoDatabaseClient();
virtual void Init(const std::string& client_name, void Init(const std::string& client_name,
typename ProtoDatabase<T>::InitCallback callback) override; typename ProtoDatabase<T>::InitCallback callback) override;
virtual void Init(const char* client_name, void Init(const char* client_name,
const base::FilePath& database_dir, const base::FilePath& database_dir,
const leveldb_env::Options& options, const leveldb_env::Options& options,
typename ProtoDatabase<T>::InitCallback callback) override; typename ProtoDatabase<T>::InitCallback callback) override;
virtual void InitWithDatabase( void InitWithDatabase(
LevelDB* database, LevelDB* database,
const base::FilePath& database_dir, const base::FilePath& database_dir,
const leveldb_env::Options& options, const leveldb_env::Options& options,
...@@ -56,60 +56,58 @@ class SharedProtoDatabaseClient : public ProtoDatabase<T> { ...@@ -56,60 +56,58 @@ class SharedProtoDatabaseClient : public ProtoDatabase<T> {
// Overrides for prepending namespace and type prefix to all operations on the // Overrides for prepending namespace and type prefix to all operations on the
// shared database. // shared database.
virtual void UpdateEntries( void UpdateEntries(
std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector> std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector>
entries_to_save, entries_to_save,
std::unique_ptr<std::vector<std::string>> keys_to_remove, std::unique_ptr<std::vector<std::string>> keys_to_remove,
typename ProtoDatabase<T>::UpdateCallback callback) override; typename ProtoDatabase<T>::UpdateCallback callback) override;
virtual void UpdateEntriesWithRemoveFilter( void UpdateEntriesWithRemoveFilter(
std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector> std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector>
entries_to_save, entries_to_save,
const LevelDB::KeyFilter& delete_key_filter, const LevelDB::KeyFilter& delete_key_filter,
typename ProtoDatabase<T>::UpdateCallback callback) override; typename ProtoDatabase<T>::UpdateCallback callback) override;
virtual void UpdateEntriesWithRemoveFilter( void UpdateEntriesWithRemoveFilter(
std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector> std::unique_ptr<typename ProtoDatabase<T>::KeyEntryVector>
entries_to_save, entries_to_save,
const LevelDB::KeyFilter& delete_key_filter, const LevelDB::KeyFilter& delete_key_filter,
const std::string& target_prefix, const std::string& target_prefix,
typename ProtoDatabase<T>::UpdateCallback callback) override; typename ProtoDatabase<T>::UpdateCallback callback) override;
virtual void LoadEntries( void LoadEntries(typename ProtoDatabase<T>::LoadCallback callback) override;
typename ProtoDatabase<T>::LoadCallback callback) override; void LoadEntriesWithFilter(
virtual void LoadEntriesWithFilter(
const LevelDB::KeyFilter& filter, const LevelDB::KeyFilter& filter,
typename ProtoDatabase<T>::LoadCallback callback) override; typename ProtoDatabase<T>::LoadCallback callback) override;
virtual void LoadEntriesWithFilter( void LoadEntriesWithFilter(
const LevelDB::KeyFilter& key_filter, const LevelDB::KeyFilter& key_filter,
const leveldb::ReadOptions& options, const leveldb::ReadOptions& options,
const std::string& target_prefix, const std::string& target_prefix,
typename ProtoDatabase<T>::LoadCallback callback) override; typename ProtoDatabase<T>::LoadCallback callback) override;
virtual void LoadKeys( void LoadKeys(typename ProtoDatabase<T>::LoadKeysCallback callback) override;
typename ProtoDatabase<T>::LoadKeysCallback callback) override; void LoadKeys(const std::string& target_prefix,
virtual void LoadKeys( typename ProtoDatabase<T>::LoadKeysCallback callback) override;
const std::string& target_prefix,
typename ProtoDatabase<T>::LoadKeysCallback callback) override;
virtual void LoadKeysAndEntries( void LoadKeysAndEntries(
typename ProtoDatabase<T>::LoadKeysAndEntriesCallback callback) override; typename ProtoDatabase<T>::LoadKeysAndEntriesCallback callback) override;
virtual void LoadKeysAndEntriesWithFilter( void LoadKeysAndEntriesWithFilter(
const LevelDB::KeyFilter& filter, const LevelDB::KeyFilter& filter,
typename ProtoDatabase<T>::LoadKeysAndEntriesCallback callback) override; typename ProtoDatabase<T>::LoadKeysAndEntriesCallback callback) override;
virtual void LoadKeysAndEntriesWithFilter( void LoadKeysAndEntriesWithFilter(
const LevelDB::KeyFilter& filter, const LevelDB::KeyFilter& filter,
const leveldb::ReadOptions& options, const leveldb::ReadOptions& options,
const std::string& target_prefix, const std::string& target_prefix,
typename ProtoDatabase<T>::LoadKeysAndEntriesCallback callback) override; typename ProtoDatabase<T>::LoadKeysAndEntriesCallback callback) override;
virtual void GetEntry( void GetEntry(const std::string& key,
const std::string& key, typename ProtoDatabase<T>::GetCallback callback) override;
typename ProtoDatabase<T>::GetCallback callback) override;
virtual void Destroy( void Destroy(typename ProtoDatabase<T>::DestroyCallback callback) override;
typename ProtoDatabase<T>::DestroyCallback callback) override;
typename ProtoLevelDBWrapper::InitCallback GetInitCallback() const; typename ProtoLevelDBWrapper::InitCallback GetInitCallback() const;
bool IsCorrupt() override;
void SetIsCorrupt(bool is_corrupt);
private: private:
friend class SharedProtoDatabase; friend class SharedProtoDatabase;
friend class SharedProtoDatabaseTest; friend class SharedProtoDatabaseTest;
...@@ -142,6 +140,10 @@ class SharedProtoDatabaseClient : public ProtoDatabase<T> { ...@@ -142,6 +140,10 @@ class SharedProtoDatabaseClient : public ProtoDatabase<T> {
SEQUENCE_CHECKER(sequence_checker_); SEQUENCE_CHECKER(sequence_checker_);
// |is_corrupt_| should be set by the SharedProtoDatabase that creates this
// when a client is created that doesn't know about a previous shared
// database corruption.
bool is_corrupt_ = false;
std::string prefix_; std::string prefix_;
scoped_refptr<SharedProtoDatabase> parent_db_; scoped_refptr<SharedProtoDatabase> parent_db_;
...@@ -326,6 +328,16 @@ void SharedProtoDatabaseClient<T>::Destroy( ...@@ -326,6 +328,16 @@ void SharedProtoDatabaseClient<T>::Destroy(
std::move(callback))); std::move(callback)));
} }
template <typename T>
void SharedProtoDatabaseClient<T>::SetIsCorrupt(bool is_corrupt) {
is_corrupt_ = is_corrupt;
}
template <typename T>
bool SharedProtoDatabaseClient<T>::IsCorrupt() {
return is_corrupt_;
}
// static // static
template <typename T> template <typename T>
void SharedProtoDatabaseClient<T>::StripPrefixLoadKeysCallback( void SharedProtoDatabaseClient<T>::StripPrefixLoadKeysCallback(
......
...@@ -96,6 +96,8 @@ class UniqueProtoDatabase : public ProtoDatabase<T> { ...@@ -96,6 +96,8 @@ class UniqueProtoDatabase : public ProtoDatabase<T> {
bool GetApproximateMemoryUse(uint64_t* approx_mem_use); bool GetApproximateMemoryUse(uint64_t* approx_mem_use);
bool IsCorrupt() override;
// Sets the identifier used by the underlying LevelDB wrapper to record // Sets the identifier used by the underlying LevelDB wrapper to record
// metrics. // metrics.
void SetMetricsId(const std::string& id); void SetMetricsId(const std::string& id);
...@@ -288,6 +290,11 @@ void UniqueProtoDatabase<T>::SetMetricsId(const std::string& id) { ...@@ -288,6 +290,11 @@ void UniqueProtoDatabase<T>::SetMetricsId(const std::string& id) {
db_wrapper_->SetMetricsId(id); db_wrapper_->SetMetricsId(id);
} }
template <typename T>
bool UniqueProtoDatabase<T>::IsCorrupt() {
return db_wrapper_->IsCorrupt();
}
} // namespace leveldb_proto } // namespace leveldb_proto
#endif // COMPONENTS_LEVELDB_PROTO_UNIQUE_PROTO_DATABASE_H_ #endif // COMPONENTS_LEVELDB_PROTO_UNIQUE_PROTO_DATABASE_H_
...@@ -660,9 +660,16 @@ TEST(UniqueProtoDatabaseThreadingTest, TestDBDestruction) { ...@@ -660,9 +660,16 @@ TEST(UniqueProtoDatabaseThreadingTest, TestDBDestruction) {
MockDatabaseCaller caller; MockDatabaseCaller caller;
EXPECT_CALL(caller, InitCallback(_)); EXPECT_CALL(caller, InitCallback(_));
base::RunLoop init_loop;
db->Init(kTestLevelDBClientName, temp_dir.GetPath(), CreateSimpleOptions(), db->Init(kTestLevelDBClientName, temp_dir.GetPath(), CreateSimpleOptions(),
base::BindOnce(&MockDatabaseCaller::InitCallback, base::BindOnce(
base::Unretained(&caller))); [](MockDatabaseCaller* caller, base::OnceClosure closure,
bool success) {
caller->InitCallback(success);
std::move(closure).Run();
},
&caller, init_loop.QuitClosure()));
init_loop.Run();
db.reset(); db.reset();
...@@ -688,9 +695,16 @@ TEST(UniqueProtoDatabaseThreadingTest, TestDBDestroy) { ...@@ -688,9 +695,16 @@ TEST(UniqueProtoDatabaseThreadingTest, TestDBDestroy) {
MockDatabaseCaller caller; MockDatabaseCaller caller;
EXPECT_CALL(caller, InitCallback(_)); EXPECT_CALL(caller, InitCallback(_));
base::RunLoop init_loop;
db->Init(kTestLevelDBClientName, temp_dir.GetPath(), CreateSimpleOptions(), db->Init(kTestLevelDBClientName, temp_dir.GetPath(), CreateSimpleOptions(),
base::BindOnce(&MockDatabaseCaller::InitCallback, base::BindOnce(
base::Unretained(&caller))); [](MockDatabaseCaller* caller, base::OnceClosure closure,
bool success) {
caller->InitCallback(success);
std::move(closure).Run();
},
&caller, init_loop.QuitClosure()));
init_loop.Run();
EXPECT_CALL(caller, DestroyCallback(_)); EXPECT_CALL(caller, DestroyCallback(_));
db->Destroy(base::BindOnce(&MockDatabaseCaller::DestroyCallback, db->Destroy(base::BindOnce(&MockDatabaseCaller::DestroyCallback,
......
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