Commit 910bc047 authored by mek's avatar mek Committed by Commit bot

Add callback to LevelDBWrapperImpl to allow extra operations on commit.

Initially this callback is used to delay writing the schema version until data
actually gets committed. In follow-up CLs this will also be used to store
per-origin metadata.

BUG=586194

Review-Url: https://codereview.chromium.org/2597353002
Cr-Commit-Position: refs/heads/master@{#440861}
parent 1dbe9949
...@@ -31,8 +31,6 @@ const char kOriginSeparator = '\x00'; ...@@ -31,8 +31,6 @@ const char kOriginSeparator = '\x00';
const char kDataPrefix[] = "_"; const char kDataPrefix[] = "_";
const int64_t kMinSchemaVersion = 1; const int64_t kMinSchemaVersion = 1;
const int64_t kCurrentSchemaVersion = 1; const int64_t kCurrentSchemaVersion = 1;
void NoOpResult(leveldb::mojom::DatabaseError status) {}
} }
LocalStorageContextMojo::LocalStorageContextMojo( LocalStorageContextMojo::LocalStorageContextMojo(
...@@ -79,9 +77,9 @@ void LocalStorageContextMojo::OpenLocalStorage( ...@@ -79,9 +77,9 @@ void LocalStorageContextMojo::OpenLocalStorage(
if (connection_state_ == CONNECTION_IN_PROGRESS) { if (connection_state_ == CONNECTION_IN_PROGRESS) {
// Queue this OpenLocalStorage call for when we have a level db pointer. // Queue this OpenLocalStorage call for when we have a level db pointer.
on_database_opened_callbacks_.push_back(base::Bind( on_database_opened_callbacks_.push_back(base::BindOnce(
&LocalStorageContextMojo::BindLocalStorage, &LocalStorageContextMojo::BindLocalStorage,
weak_ptr_factory_.GetWeakPtr(), origin, base::Passed(&request))); weak_ptr_factory_.GetWeakPtr(), origin, std::move(request)));
return; return;
} }
...@@ -102,6 +100,24 @@ void LocalStorageContextMojo::OnLevelDBWrapperHasNoBindings( ...@@ -102,6 +100,24 @@ void LocalStorageContextMojo::OnLevelDBWrapperHasNoBindings(
level_db_wrappers_.erase(origin); level_db_wrappers_.erase(origin);
} }
std::vector<leveldb::mojom::BatchedOperationPtr>
LocalStorageContextMojo::OnLevelDBWrapperPrepareToCommit() {
std::vector<leveldb::mojom::BatchedOperationPtr> operations;
// Write schema version if not already done so before.
if (!database_initialized_) {
leveldb::mojom::BatchedOperationPtr item =
leveldb::mojom::BatchedOperation::New();
item->key = leveldb::StdStringToUint8Vector(kVersionKey);
item->value = leveldb::StdStringToUint8Vector(
base::Int64ToString(kCurrentSchemaVersion));
operations.push_back(std::move(item));
database_initialized_ = true;
}
return operations;
}
void LocalStorageContextMojo::OnUserServiceConnectionComplete() { void LocalStorageContextMojo::OnUserServiceConnectionComplete() {
CHECK_EQ(service_manager::mojom::ConnectResult::SUCCEEDED, CHECK_EQ(service_manager::mojom::ConnectResult::SUCCEEDED,
file_service_connection_->GetResult()); file_service_connection_->GetResult());
...@@ -161,13 +177,8 @@ void LocalStorageContextMojo::OnGotDatabaseVersion( ...@@ -161,13 +177,8 @@ void LocalStorageContextMojo::OnGotDatabaseVersion(
leveldb::mojom::DatabaseError status, leveldb::mojom::DatabaseError status,
const std::vector<uint8_t>& value) { const std::vector<uint8_t>& value) {
if (status == leveldb::mojom::DatabaseError::NOT_FOUND) { if (status == leveldb::mojom::DatabaseError::NOT_FOUND) {
// New database, write current version and continue. // New database, nothing more to do. Current version will get written
// TODO(mek): Delay writing version until first actual data gets committed. // when first data is committed.
database_->Put(leveldb::StdStringToUint8Vector(kVersionKey),
leveldb::StdStringToUint8Vector(
base::Int64ToString(kCurrentSchemaVersion)),
base::Bind(&NoOpResult));
// new database
} else if (status == leveldb::mojom::DatabaseError::OK) { } else if (status == leveldb::mojom::DatabaseError::OK) {
// Existing database, check if version number matches current schema // Existing database, check if version number matches current schema
// version. // version.
...@@ -178,6 +189,8 @@ void LocalStorageContextMojo::OnGotDatabaseVersion( ...@@ -178,6 +189,8 @@ void LocalStorageContextMojo::OnGotDatabaseVersion(
// TODO(mek): delete and recreate database, rather than failing outright. // TODO(mek): delete and recreate database, rather than failing outright.
database_ = nullptr; database_ = nullptr;
} }
database_initialized_ = true;
} else { } else {
// Other read error. Possibly database corruption. // Other read error. Possibly database corruption.
// TODO(mek): delete and recreate database, rather than failing outright. // TODO(mek): delete and recreate database, rather than failing outright.
...@@ -188,7 +201,7 @@ void LocalStorageContextMojo::OnGotDatabaseVersion( ...@@ -188,7 +201,7 @@ void LocalStorageContextMojo::OnGotDatabaseVersion(
// delayed bindings. // delayed bindings.
connection_state_ = CONNECTION_FINISHED; connection_state_ = CONNECTION_FINISHED;
for (size_t i = 0; i < on_database_opened_callbacks_.size(); ++i) for (size_t i = 0; i < on_database_opened_callbacks_.size(); ++i)
on_database_opened_callbacks_[i].Run(); std::move(on_database_opened_callbacks_[i]).Run();
on_database_opened_callbacks_.clear(); on_database_opened_callbacks_.clear();
} }
...@@ -214,7 +227,9 @@ void LocalStorageContextMojo::BindLocalStorage( ...@@ -214,7 +227,9 @@ void LocalStorageContextMojo::BindLocalStorage(
base::TimeDelta::FromSeconds(kCommitDefaultDelaySecs), kMaxBytesPerHour, base::TimeDelta::FromSeconds(kCommitDefaultDelaySecs), kMaxBytesPerHour,
kMaxCommitsPerHour, kMaxCommitsPerHour,
base::Bind(&LocalStorageContextMojo::OnLevelDBWrapperHasNoBindings, base::Bind(&LocalStorageContextMojo::OnLevelDBWrapperHasNoBindings,
base::Unretained(this), origin)); base::Unretained(this), origin),
base::Bind(&LocalStorageContextMojo::OnLevelDBWrapperPrepareToCommit,
base::Unretained(this)));
found = level_db_wrappers_.find(origin); found = level_db_wrappers_.find(origin);
} }
......
...@@ -37,6 +37,8 @@ class CONTENT_EXPORT LocalStorageContextMojo { ...@@ -37,6 +37,8 @@ class CONTENT_EXPORT LocalStorageContextMojo {
private: private:
void OnLevelDBWrapperHasNoBindings(const url::Origin& origin); void OnLevelDBWrapperHasNoBindings(const url::Origin& origin);
std::vector<leveldb::mojom::BatchedOperationPtr>
OnLevelDBWrapperPrepareToCommit();
void OnUserServiceConnectionComplete(); void OnUserServiceConnectionComplete();
void OnUserServiceConnectionError(); void OnUserServiceConnectionError();
...@@ -59,6 +61,7 @@ class CONTENT_EXPORT LocalStorageContextMojo { ...@@ -59,6 +61,7 @@ class CONTENT_EXPORT LocalStorageContextMojo {
CONNECTION_IN_PROGRESS, CONNECTION_IN_PROGRESS,
CONNECTION_FINISHED CONNECTION_FINISHED
} connection_state_ = NO_CONNECTION; } connection_state_ = NO_CONNECTION;
bool database_initialized_ = false;
std::unique_ptr<service_manager::Connection> file_service_connection_; std::unique_ptr<service_manager::Connection> file_service_connection_;
...@@ -68,7 +71,7 @@ class CONTENT_EXPORT LocalStorageContextMojo { ...@@ -68,7 +71,7 @@ class CONTENT_EXPORT LocalStorageContextMojo {
leveldb::mojom::LevelDBServicePtr leveldb_service_; leveldb::mojom::LevelDBServicePtr leveldb_service_;
leveldb::mojom::LevelDBDatabasePtr database_; leveldb::mojom::LevelDBDatabasePtr database_;
std::vector<base::Closure> on_database_opened_callbacks_; std::vector<base::OnceClosure> on_database_opened_callbacks_;
// Maps between an origin and its prefixed LevelDB view. // Maps between an origin and its prefixed LevelDB view.
std::map<url::Origin, std::unique_ptr<LevelDBWrapperImpl>> level_db_wrappers_; std::map<url::Origin, std::unique_ptr<LevelDBWrapperImpl>> level_db_wrappers_;
......
...@@ -72,6 +72,7 @@ TEST_F(LocalStorageContextMojoTest, Basic) { ...@@ -72,6 +72,7 @@ TEST_F(LocalStorageContextMojoTest, Basic) {
wrapper.reset(); wrapper.reset();
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
ASSERT_EQ(2u, mock_data().size()); ASSERT_EQ(2u, mock_data().size());
EXPECT_EQ(value, mock_data().rbegin()->second); EXPECT_EQ(value, mock_data().rbegin()->second);
} }
...@@ -134,4 +135,23 @@ TEST_F(LocalStorageContextMojoTest, InvalidVersion) { ...@@ -134,4 +135,23 @@ TEST_F(LocalStorageContextMojoTest, InvalidVersion) {
EXPECT_FALSE(success); EXPECT_FALSE(success);
} }
TEST_F(LocalStorageContextMojoTest, VersionOnlyWrittenOnCommit) {
mojom::LevelDBWrapperPtr wrapper;
context()->OpenLocalStorage(url::Origin(GURL("http://foobar.com")),
MakeRequest(&wrapper));
base::RunLoop run_loop;
bool success = false;
std::vector<uint8_t> result;
wrapper->Get(
StdStringToUint8Vector("key"),
base::Bind(&GetCallback, run_loop.QuitClosure(), &success, &result));
run_loop.Run();
EXPECT_FALSE(success);
wrapper.reset();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(mock_data().empty());
}
} // namespace content } // namespace content
...@@ -41,9 +41,11 @@ LevelDBWrapperImpl::LevelDBWrapperImpl( ...@@ -41,9 +41,11 @@ LevelDBWrapperImpl::LevelDBWrapperImpl(
base::TimeDelta default_commit_delay, base::TimeDelta default_commit_delay,
int max_bytes_per_hour, int max_bytes_per_hour,
int max_commits_per_hour, int max_commits_per_hour,
const base::Closure& no_bindings_callback) const base::Closure& no_bindings_callback,
const PrepareToCommitCallback& prepare_to_commit_callback)
: prefix_(leveldb::StdStringToUint8Vector(prefix)), : prefix_(leveldb::StdStringToUint8Vector(prefix)),
no_bindings_callback_(no_bindings_callback), no_bindings_callback_(no_bindings_callback),
prepare_to_commit_callback_(prepare_to_commit_callback),
database_(database), database_(database),
bytes_used_(0), bytes_used_(0),
max_size_(max_size), max_size_(max_size),
...@@ -334,7 +336,8 @@ void LevelDBWrapperImpl::CommitChanges() { ...@@ -334,7 +336,8 @@ void LevelDBWrapperImpl::CommitChanges() {
commit_rate_limiter_.add_samples(1); commit_rate_limiter_.add_samples(1);
// Commit all our changes in a single batch. // Commit all our changes in a single batch.
std::vector<leveldb::mojom::BatchedOperationPtr> operations; std::vector<leveldb::mojom::BatchedOperationPtr> operations =
prepare_to_commit_callback_.Run();
if (commit_batch_->clear_all_first) { if (commit_batch_->clear_all_first) {
leveldb::mojom::BatchedOperationPtr item = leveldb::mojom::BatchedOperationPtr item =
leveldb::mojom::BatchedOperation::New(); leveldb::mojom::BatchedOperation::New();
......
...@@ -32,6 +32,9 @@ namespace content { ...@@ -32,6 +32,9 @@ namespace content {
// 4) Throttles requests to avoid overwhelming the disk. // 4) Throttles requests to avoid overwhelming the disk.
class CONTENT_EXPORT LevelDBWrapperImpl : public mojom::LevelDBWrapper { class CONTENT_EXPORT LevelDBWrapperImpl : public mojom::LevelDBWrapper {
public: public:
using PrepareToCommitCallback =
base::Callback<std::vector<leveldb::mojom::BatchedOperationPtr>()>;
// |no_bindings_callback| will be called when this object has no more // |no_bindings_callback| will be called when this object has no more
// bindings and all pending modifications have been processed. // bindings and all pending modifications have been processed.
LevelDBWrapperImpl(leveldb::mojom::LevelDBDatabase* database, LevelDBWrapperImpl(leveldb::mojom::LevelDBDatabase* database,
...@@ -40,7 +43,8 @@ class CONTENT_EXPORT LevelDBWrapperImpl : public mojom::LevelDBWrapper { ...@@ -40,7 +43,8 @@ class CONTENT_EXPORT LevelDBWrapperImpl : public mojom::LevelDBWrapper {
base::TimeDelta default_commit_delay, base::TimeDelta default_commit_delay,
int max_bytes_per_hour, int max_bytes_per_hour,
int max_commits_per_hour, int max_commits_per_hour,
const base::Closure& no_bindings_callback); const base::Closure& no_bindings_callback,
const PrepareToCommitCallback& prepare_to_commit_callback);
~LevelDBWrapperImpl() override; ~LevelDBWrapperImpl() override;
void Bind(mojom::LevelDBWrapperRequest request); void Bind(mojom::LevelDBWrapperRequest request);
...@@ -118,6 +122,7 @@ class CONTENT_EXPORT LevelDBWrapperImpl : public mojom::LevelDBWrapper { ...@@ -118,6 +122,7 @@ class CONTENT_EXPORT LevelDBWrapperImpl : public mojom::LevelDBWrapper {
mojo::BindingSet<mojom::LevelDBWrapper> bindings_; mojo::BindingSet<mojom::LevelDBWrapper> bindings_;
mojo::AssociatedInterfacePtrSet<mojom::LevelDBObserver> observers_; mojo::AssociatedInterfacePtrSet<mojom::LevelDBObserver> observers_;
base::Closure no_bindings_callback_; base::Closure no_bindings_callback_;
PrepareToCommitCallback prepare_to_commit_callback_;
leveldb::mojom::LevelDBDatabase* database_; leveldb::mojom::LevelDBDatabase* database_;
std::unique_ptr<ValueMap> map_; std::unique_ptr<ValueMap> map_;
std::vector<base::Closure> on_load_complete_tasks_; std::vector<base::Closure> on_load_complete_tasks_;
......
...@@ -53,6 +53,9 @@ class GetAllCallback : public mojom::LevelDBWrapperGetAllCallback { ...@@ -53,6 +53,9 @@ class GetAllCallback : public mojom::LevelDBWrapperGetAllCallback {
}; };
void NoOp() {} void NoOp() {}
std::vector<leveldb::mojom::BatchedOperationPtr> PrepareToCommitNoOp() {
return std::vector<leveldb::mojom::BatchedOperationPtr>();
}
void GetCallback(const base::Closure& callback, void GetCallback(const base::Closure& callback,
bool* success_out, bool* success_out,
...@@ -92,7 +95,8 @@ class LevelDBWrapperImplTest : public testing::Test, ...@@ -92,7 +95,8 @@ class LevelDBWrapperImplTest : public testing::Test,
base::TimeDelta::FromSeconds(5), base::TimeDelta::FromSeconds(5),
10 * 1024 * 1024 /* max_bytes_per_hour */, 10 * 1024 * 1024 /* max_bytes_per_hour */,
60 /* max_commits_per_hour */, 60 /* max_commits_per_hour */,
base::Bind(&NoOp)), base::Bind(&NoOp),
base::Bind(&PrepareToCommitNoOp)),
observer_binding_(this) { observer_binding_(this) {
set_mock_data(std::string(kTestPrefix) + "def", "defdata"); set_mock_data(std::string(kTestPrefix) + "def", "defdata");
set_mock_data(std::string(kTestPrefix) + "123", "123data"); set_mock_data(std::string(kTestPrefix) + "123", "123data");
......
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