Commit d4ced263 authored by lijunsong's avatar lijunsong Committed by Commit Bot

[SessionStorage] Simplify database open callback graph

This changeset simplifies database callback graph in open with GetMany provided
by leveldb service.

Previously OnDatabaseOpened called OnGotDatabaseVersion, and before calling
OnConnectionFinished it was block waiting on a barrier of OnGotNamespaces and
OnGotNextMapId. This changeset gets values of database version, namespaces and
next map id in one call, and processes the result sequentially.

Bug: 965723

Test: autoninja -C out/Default content/test:content_unittests. Passed all content_unittests
Change-Id: I968d92d93414cc6918e48d870fea27dc1d74b3d2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1714487
Commit-Queue: Daniel Murphy <dmurph@chromium.org>
Reviewed-by: default avatarDaniel Murphy <dmurph@chromium.org>
Cr-Commit-Position: refs/heads/master@{#684106}
parent 16109dae
...@@ -803,119 +803,165 @@ void SessionStorageContextMojo::OnDatabaseOpened( ...@@ -803,119 +803,165 @@ void SessionStorageContextMojo::OnDatabaseOpened(
return; return;
} }
// Verify DB schema version. if (!database_) {
if (database_) { OnConnectionFinished();
return;
}
database_.set_connection_error_handler( database_.set_connection_error_handler(
base::BindOnce(&SessionStorageContextMojo::OnMojoConnectionDestroyed, base::BindOnce(&SessionStorageContextMojo::OnMojoConnectionDestroyed,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
database_->Get(
std::vector<uint8_t>( std::vector<uint8_t> database_version(
SessionStorageMetadata::kDatabaseVersionBytes, SessionStorageMetadata::kDatabaseVersionBytes,
std::end(SessionStorageMetadata::kDatabaseVersionBytes)), std::end(SessionStorageMetadata::kDatabaseVersionBytes));
base::BindOnce(&SessionStorageContextMojo::OnGotDatabaseVersion, std::vector<uint8_t> namespace_prefix(
SessionStorageMetadata::kNamespacePrefixBytes,
std::end(SessionStorageMetadata::kNamespacePrefixBytes));
std::vector<uint8_t> next_map_id_key(
SessionStorageMetadata::kNextMapIdKeyBytes,
std::end(SessionStorageMetadata::kNextMapIdKeyBytes));
std::vector<leveldb::mojom::GetManyRequestPtr> requests;
requests.emplace_back(
leveldb::mojom::GetManyRequest::NewKey(std::move(database_version)));
requests.emplace_back(leveldb::mojom::GetManyRequest::NewKeyPrefix(
std::move(namespace_prefix)));
requests.emplace_back(
leveldb::mojom::GetManyRequest::NewKey(std::move(next_map_id_key)));
database_->GetMany(
std::move(requests),
base::BindOnce(&SessionStorageContextMojo::OnGotDatabaseMetadata,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
}
void SessionStorageContextMojo::OnGotDatabaseMetadata(
std::vector<leveldb::mojom::GetManyResultPtr> results) {
DCHECK_EQ(results.size(), 3U);
std::vector<leveldb::mojom::BatchedOperationPtr> migration_operations;
OpenResult open_result;
const char* histogram_name;
std::tie(open_result, histogram_name) =
ParseDatabaseVersion(results[0], &migration_operations);
if (open_result != OpenResult::kSuccess) {
LogDatabaseOpenResult(open_result);
DeleteAndRecreateDatabase(histogram_name);
return;
}
std::tie(open_result, histogram_name) =
ParseNamespaces(results[1], std::move(migration_operations));
if (open_result != OpenResult::kSuccess) {
LogDatabaseOpenResult(open_result);
DeleteAndRecreateDatabase(histogram_name);
return;
}
std::tie(open_result, histogram_name) = ParseNextMapId(results[2]);
if (open_result != OpenResult::kSuccess) {
LogDatabaseOpenResult(open_result);
DeleteAndRecreateDatabase(histogram_name);
return; return;
} }
OnConnectionFinished(); OnConnectionFinished();
} }
void SessionStorageContextMojo::OnGotDatabaseVersion( SessionStorageContextMojo::OpenResultAndHistogramName
leveldb::mojom::DatabaseError status, SessionStorageContextMojo::ParseDatabaseVersion(
const std::vector<uint8_t>& value) { const leveldb::mojom::GetManyResultPtr& result,
std::vector<leveldb::mojom::BatchedOperationPtr> migration_operations; std::vector<leveldb::mojom::BatchedOperationPtr>* migration_operations) {
if (status == leveldb::mojom::DatabaseError::NOT_FOUND) { if (result->is_key_value()) {
// New database, or schema v0. We must treat this as a schema v0 database. const std::vector<uint8_t>& value = result->get_key_value();
metadata_.ParseDatabaseVersion(base::nullopt, &migration_operations);
} else if (status == leveldb::mojom::DatabaseError::OK) { if (!metadata_.ParseDatabaseVersion(value, migration_operations)) {
if (!metadata_.ParseDatabaseVersion(value, &migration_operations)) { return {OpenResult::kInvalidVersion,
LogDatabaseOpenResult(OpenResult::kInvalidVersion); "SessionStorageContext.OpenResultAfterInvalidVersion"};
DeleteAndRecreateDatabase(
"SessionStorageContext.OpenResultAfterInvalidVersion");
return;
} }
database_initialized_ = true; database_initialized_ = true;
} else { return {OpenResult::kSuccess, ""};
// Other read error. Possibly database corruption. }
// Failed to get DatabaseVersion, |result| contains error status
leveldb::mojom::DatabaseError status = result->get_status();
if (status == leveldb::mojom::DatabaseError::NOT_FOUND) {
// treat as v0 or new database
metadata_.ParseDatabaseVersion(base::nullopt, migration_operations);
return {OpenResult::kSuccess, ""};
}
// Other read error, Possibly database corruption
UMA_HISTOGRAM_ENUMERATION("SessionStorageContext.ReadVersionError", UMA_HISTOGRAM_ENUMERATION("SessionStorageContext.ReadVersionError",
leveldb::GetLevelDBStatusUMAValue(status), leveldb::GetLevelDBStatusUMAValue(status),
leveldb_env::LEVELDB_STATUS_MAX); leveldb_env::LEVELDB_STATUS_MAX);
LogDatabaseOpenResult(OpenResult::kVersionReadError); return {OpenResult::kVersionReadError,
DeleteAndRecreateDatabase( "SessionStorageContext.OpenResultAfterReadVersionError"};
"SessionStorageContext.OpenResultAfterReadVersionError"); }
return;
SessionStorageContextMojo::OpenResultAndHistogramName
SessionStorageContextMojo::ParseNamespaces(
const leveldb::mojom::GetManyResultPtr& result,
std::vector<leveldb::mojom::BatchedOperationPtr> migration_operations) {
DCHECK_EQ(connection_state_, CONNECTION_IN_PROGRESS);
if (result->is_status()) {
UMA_HISTOGRAM_ENUMERATION(
"SessionStorageContext.ReadNamespacesError",
leveldb::GetLevelDBStatusUMAValue(result->get_status()),
leveldb_env::LEVELDB_STATUS_MAX);
return {OpenResult::kNamespacesReadError,
"SessionStorageContext.OpenResultAfterReadNamespacesError"};
} }
base::RepeatingClosure barrier = base::BarrierClosure( DCHECK(result->is_key_prefix_values());
2, base::BindOnce(&SessionStorageContextMojo::OnConnectionFinished,
weak_ptr_factory_.GetWeakPtr()));
std::vector<uint8_t> namespace_prefix( bool parsing_success = metadata_.ParseNamespaces(
SessionStorageMetadata::kNamespacePrefixBytes, std::move(result->get_key_prefix_values()), &migration_operations);
std::end(SessionStorageMetadata::kNamespacePrefixBytes));
std::vector<uint8_t> next_map_id_key(
SessionStorageMetadata::kNextMapIdKeyBytes,
std::end(SessionStorageMetadata::kNextMapIdKeyBytes));
database_->GetPrefixed(
namespace_prefix,
base::BindOnce(&SessionStorageContextMojo::OnGotNamespaces,
weak_ptr_factory_.GetWeakPtr(), barrier,
std::move(migration_operations)));
database_->Get(next_map_id_key,
base::BindOnce(&SessionStorageContextMojo::OnGotNextMapId,
weak_ptr_factory_.GetWeakPtr(), barrier));
}
void SessionStorageContextMojo::OnGotNamespaces( if (!parsing_success) {
base::OnceClosure done, UMA_HISTOGRAM_ENUMERATION(
std::vector<leveldb::mojom::BatchedOperationPtr> migration_operations, "SessionStorageContext.ReadNamespacesError",
leveldb::mojom::DatabaseError status, leveldb::GetLevelDBStatusUMAValue(leveldb::mojom::DatabaseError::OK),
std::vector<leveldb::mojom::KeyValuePtr> values) {
DCHECK_EQ(connection_state_, CONNECTION_IN_PROGRESS);
bool parsing_failure =
status == leveldb::mojom::DatabaseError::OK &&
!metadata_.ParseNamespaces(std::move(values), &migration_operations);
if (status != leveldb::mojom::DatabaseError::OK || parsing_failure) {
UMA_HISTOGRAM_ENUMERATION("SessionStorageContext.ReadNamespacesError",
leveldb::GetLevelDBStatusUMAValue(status),
leveldb_env::LEVELDB_STATUS_MAX); leveldb_env::LEVELDB_STATUS_MAX);
LogDatabaseOpenResult(OpenResult::kNamespacesReadError); return {OpenResult::kNamespacesReadError,
DeleteAndRecreateDatabase( "SessionStorageContext.OpenResultAfterReadNamespacesError"};
"SessionStorageContext.OpenResultAfterReadNamespacesError");
return;
} }
// Write all of our migration operations if we have any.
if (!migration_operations.empty()) { if (!migration_operations.empty()) {
database_->Write(std::move(migration_operations), database_->Write(std::move(migration_operations),
base::BindOnce(&SessionStorageContextMojo::OnCommitResult, base::BindOnce(&SessionStorageContextMojo::OnCommitResult,
base::Unretained(this))); base::Unretained(this)));
} }
std::move(done).Run();
return {OpenResult::kSuccess, ""};
} }
void SessionStorageContextMojo::OnGotNextMapId( SessionStorageContextMojo::OpenResultAndHistogramName
base::OnceClosure done, SessionStorageContextMojo::ParseNextMapId(
leveldb::mojom::DatabaseError status, const leveldb::mojom::GetManyResultPtr& result) {
const std::vector<uint8_t>& map_id) { if (result->is_status()) {
DCHECK_EQ(connection_state_, CONNECTION_IN_PROGRESS); leveldb::mojom::DatabaseError status = result->get_status();
if (status == leveldb::mojom::DatabaseError::NOT_FOUND) { if (status == leveldb::mojom::DatabaseError::NOT_FOUND) {
std::move(done).Run(); return {OpenResult::kSuccess, ""};
return;
}
if (status == leveldb::mojom::DatabaseError::OK) {
metadata_.ParseNextMapId(map_id);
std::move(done).Run();
return;
} }
// Other read error. Possibly database corruption. // Other read error. Possibly database corruption.
UMA_HISTOGRAM_ENUMERATION("SessionStorageContext.ReadNextMapIdError", UMA_HISTOGRAM_ENUMERATION("SessionStorageContext.ReadNextMapIdError",
leveldb::GetLevelDBStatusUMAValue(status), leveldb::GetLevelDBStatusUMAValue(status),
leveldb_env::LEVELDB_STATUS_MAX); leveldb_env::LEVELDB_STATUS_MAX);
LogDatabaseOpenResult(OpenResult::kNamespacesReadError); return {OpenResult::kNamespacesReadError,
DeleteAndRecreateDatabase( "SessionStorageContext.OpenResultAfterReadNextMapIdError"};
"SessionStorageContext.OpenResultAfterReadNextMapIdError"); }
DCHECK(result->is_key_value());
metadata_.ParseNextMapId(result->get_key_value());
return {OpenResult::kSuccess, ""};
} }
void SessionStorageContextMojo::OnConnectionFinished() { void SessionStorageContextMojo::OnConnectionFinished() {
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <map> #include <map>
#include <memory> #include <memory>
#include <string> #include <string>
#include <tuple>
#include <vector> #include <vector>
#include "base/callback_forward.h" #include "base/callback_forward.h"
...@@ -149,6 +150,18 @@ class CONTENT_EXPORT SessionStorageContextMojo ...@@ -149,6 +150,18 @@ class CONTENT_EXPORT SessionStorageContextMojo
FRIEND_TEST_ALL_PREFIXES(SessionStorageContextMojoTest, FRIEND_TEST_ALL_PREFIXES(SessionStorageContextMojoTest,
PurgeMemoryDoesNotCrashOrHang); PurgeMemoryDoesNotCrashOrHang);
// These values are written to logs. New enum values can be added, but
// existing enums must never be renumbered or deleted and reused.
enum class OpenResult {
kDirectoryOpenFailed = 0,
kDatabaseOpenFailed = 1,
kInvalidVersion = 2,
kVersionReadError = 3,
kNamespacesReadError = 4,
kSuccess = 6,
kMaxValue = kSuccess
};
// Object deletion is done through |ShutdownAndDelete()|. // Object deletion is done through |ShutdownAndDelete()|.
~SessionStorageContextMojo() override; ~SessionStorageContextMojo() override;
...@@ -187,16 +200,20 @@ class CONTENT_EXPORT SessionStorageContextMojo ...@@ -187,16 +200,20 @@ class CONTENT_EXPORT SessionStorageContextMojo
void InitiateConnection(bool in_memory_only = false); void InitiateConnection(bool in_memory_only = false);
void OnDirectoryOpened(base::File::Error err); void OnDirectoryOpened(base::File::Error err);
void OnDatabaseOpened(bool in_memory, leveldb::mojom::DatabaseError status); void OnDatabaseOpened(bool in_memory, leveldb::mojom::DatabaseError status);
void OnGotDatabaseVersion(leveldb::mojom::DatabaseError status,
const std::vector<uint8_t>& value); void OnGotDatabaseMetadata(
void OnGotNamespaces( std::vector<leveldb::mojom::GetManyResultPtr> results);
base::OnceClosure done,
std::vector<leveldb::mojom::BatchedOperationPtr> migration_operations, using OpenResultAndHistogramName = std::tuple<OpenResult, const char*>;
leveldb::mojom::DatabaseError status, OpenResultAndHistogramName ParseDatabaseVersion(
std::vector<leveldb::mojom::KeyValuePtr> values); const leveldb::mojom::GetManyResultPtr& result,
void OnGotNextMapId(base::OnceClosure done, std::vector<leveldb::mojom::BatchedOperationPtr>* migration_operations);
leveldb::mojom::DatabaseError status, OpenResultAndHistogramName ParseNamespaces(
const std::vector<uint8_t>& map_id); const leveldb::mojom::GetManyResultPtr& result,
std::vector<leveldb::mojom::BatchedOperationPtr> migration_operations);
OpenResultAndHistogramName ParseNextMapId(
const leveldb::mojom::GetManyResultPtr& result);
void OnConnectionFinished(); void OnConnectionFinished();
void DeleteAndRecreateDatabase(const char* histogram_name); void DeleteAndRecreateDatabase(const char* histogram_name);
void OnDBDestroyed(bool recreate_in_memory, void OnDBDestroyed(bool recreate_in_memory,
...@@ -211,17 +228,6 @@ class CONTENT_EXPORT SessionStorageContextMojo ...@@ -211,17 +228,6 @@ class CONTENT_EXPORT SessionStorageContextMojo
void GetStatistics(size_t* total_cache_size, size_t* unused_areas_count); void GetStatistics(size_t* total_cache_size, size_t* unused_areas_count);
// These values are written to logs. New enum values can be added, but
// existing enums must never be renumbered or deleted and reused.
enum class OpenResult {
kDirectoryOpenFailed = 0,
kDatabaseOpenFailed = 1,
kInvalidVersion = 2,
kVersionReadError = 3,
kNamespacesReadError = 4,
kSuccess = 6,
kMaxValue = kSuccess
};
void LogDatabaseOpenResult(OpenResult result); void LogDatabaseOpenResult(OpenResult result);
......
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