Index: content/browser/service_worker/service_worker_database.cc |
diff --git a/content/browser/service_worker/service_worker_database.cc b/content/browser/service_worker/service_worker_database.cc |
index 73c4a31142881c5f453d6c33fd4a60c87442389e..f4da827e5a42f6e0165cd3339b4fa035edad34bb 100644 |
--- a/content/browser/service_worker/service_worker_database.cc |
+++ b/content/browser/service_worker/service_worker_database.cc |
@@ -9,6 +9,7 @@ |
#include "base/file_util.h" |
#include "base/location.h" |
#include "base/logging.h" |
+#include "base/metrics/histogram.h" |
#include "base/stl_util.h" |
#include "base/strings/string_number_conversions.h" |
#include "base/strings/string_split.h" |
@@ -77,6 +78,14 @@ const char kPurgeableResIdKeyPrefix[] = "PRES:"; |
const int64 kCurrentSchemaVersion = 1; |
+// For histogram. |
+const char kOpenResultHistogramLabel[] = |
+ "ServiceWorker.Database.OpenResult"; |
+const char kReadResultHistogramLabel[] = |
+ "ServiceWorker.Database.ReadResult"; |
+const char kWriteResultHistogramLabel[] = |
+ "ServiceWorker.Database.WriteResult"; |
+ |
bool RemovePrefix(const std::string& str, |
const std::string& prefix, |
std::string* out) { |
@@ -169,19 +178,51 @@ void PutPurgeableResourceIdToBatch(int64 resource_id, |
batch->Put(CreateResourceIdKey(kPurgeableResIdKeyPrefix, resource_id), ""); |
} |
-bool ParseRegistrationData(const std::string& serialized, |
- ServiceWorkerDatabase::RegistrationData* out) { |
+ServiceWorkerDatabase::Status ParseId( |
+ const std::string& serialized, |
+ int64* out) { |
+ DCHECK(out); |
+ int64 id; |
+ if (!base::StringToInt64(serialized, &id) || id < 0) |
+ return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; |
+ *out = id; |
+ return ServiceWorkerDatabase::STATUS_OK; |
+} |
+ |
+ServiceWorkerDatabase::Status ParseDatabaseVersion( |
+ const std::string& serialized, |
+ int64* out) { |
+ DCHECK(out); |
+ const int kFirstValidVersion = 1; |
+ int64 version; |
+ if (!base::StringToInt64(serialized, &version) || |
+ version < kFirstValidVersion) { |
+ return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; |
+ } |
+ if (kCurrentSchemaVersion < version) { |
+ DLOG(ERROR) << "ServiceWorkerDatabase has newer schema version" |
+ << " than the current latest version: " |
+ << version << " vs " << kCurrentSchemaVersion; |
+ return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; |
+ } |
+ *out = version; |
+ return ServiceWorkerDatabase::STATUS_OK; |
+} |
+ |
+ServiceWorkerDatabase::Status ParseRegistrationData( |
+ const std::string& serialized, |
+ ServiceWorkerDatabase::RegistrationData* out) { |
DCHECK(out); |
ServiceWorkerRegistrationData data; |
if (!data.ParseFromString(serialized)) |
- return false; |
+ return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; |
GURL scope_url(data.scope_url()); |
GURL script_url(data.script_url()); |
if (!scope_url.is_valid() || |
!script_url.is_valid() || |
scope_url.GetOrigin() != script_url.GetOrigin()) { |
- return false; |
+ return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; |
} |
// Convert ServiceWorkerRegistrationData to RegistrationData. |
@@ -193,24 +234,25 @@ bool ParseRegistrationData(const std::string& serialized, |
out->has_fetch_handler = data.has_fetch_handler(); |
out->last_update_check = |
base::Time::FromInternalValue(data.last_update_check_time()); |
- return true; |
+ return ServiceWorkerDatabase::STATUS_OK; |
} |
-bool ParseResourceRecord(const std::string& serialized, |
- ServiceWorkerDatabase::ResourceRecord* out) { |
+ServiceWorkerDatabase::Status ParseResourceRecord( |
+ const std::string& serialized, |
+ ServiceWorkerDatabase::ResourceRecord* out) { |
DCHECK(out); |
ServiceWorkerResourceRecord record; |
if (!record.ParseFromString(serialized)) |
- return false; |
+ return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; |
GURL url(record.url()); |
if (!url.is_valid()) |
- return false; |
+ return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; |
// Convert ServiceWorkerResourceRecord to ResourceRecord. |
out->resource_id = record.resource_id(); |
out->url = url; |
- return true; |
+ return ServiceWorkerDatabase::STATUS_OK; |
} |
ServiceWorkerDatabase::Status LevelDBStatusToStatus( |
@@ -219,12 +261,34 @@ ServiceWorkerDatabase::Status LevelDBStatusToStatus( |
return ServiceWorkerDatabase::STATUS_OK; |
else if (status.IsNotFound()) |
return ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND; |
+ else if (status.IsIOError()) |
+ return ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR; |
else if (status.IsCorruption()) |
return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; |
else |
return ServiceWorkerDatabase::STATUS_ERROR_FAILED; |
} |
+const char* StatusToString(ServiceWorkerDatabase::Status status) { |
+ switch (status) { |
+ case ServiceWorkerDatabase::STATUS_OK: |
+ return "Database OK"; |
+ case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND: |
+ return "Database not found"; |
+ case ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR: |
+ return "Database IO error"; |
+ case ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED: |
+ return "Database corrupted"; |
+ case ServiceWorkerDatabase::STATUS_ERROR_FAILED: |
+ return "Database operation failed"; |
+ case ServiceWorkerDatabase::STATUS_ERROR_MAX: |
+ NOTREACHED(); |
+ return "Database unknown error"; |
+ } |
+ NOTREACHED(); |
+ return "Database unknown error"; |
+} |
+ |
} // namespace |
ServiceWorkerDatabase::RegistrationData::RegistrationData() |
@@ -299,10 +363,11 @@ ServiceWorkerDatabase::GetOriginsWithRegistrations(std::set<GURL>* origins) { |
scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
for (itr->Seek(kUniqueOriginKey); itr->Valid(); itr->Next()) { |
- if (!itr->status().ok()) { |
- HandleError(FROM_HERE, itr->status()); |
+ status = LevelDBStatusToStatus(itr->status()); |
+ if (status != STATUS_OK) { |
+ HandleReadResult(FROM_HERE, status); |
origins->clear(); |
- return LevelDBStatusToStatus(itr->status()); |
+ return status; |
} |
std::string origin; |
@@ -310,7 +375,9 @@ ServiceWorkerDatabase::GetOriginsWithRegistrations(std::set<GURL>* origins) { |
break; |
origins->insert(GURL(origin)); |
} |
- return STATUS_OK; |
+ |
+ HandleReadResult(FROM_HERE, status); |
+ return status; |
} |
ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetRegistrationsForOrigin( |
@@ -331,24 +398,28 @@ ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetRegistrationsForOrigin( |
scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
for (itr->Seek(prefix); itr->Valid(); itr->Next()) { |
- if (!itr->status().ok()) { |
- HandleError(FROM_HERE, itr->status()); |
+ status = LevelDBStatusToStatus(itr->status()); |
+ if (status != STATUS_OK) { |
+ HandleReadResult(FROM_HERE, status); |
registrations->clear(); |
- return LevelDBStatusToStatus(itr->status()); |
+ return status; |
} |
if (!RemovePrefix(itr->key().ToString(), prefix, NULL)) |
break; |
RegistrationData registration; |
- if (!ParseRegistrationData(itr->value().ToString(), ®istration)) { |
- HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); |
+ status = ParseRegistrationData(itr->value().ToString(), ®istration); |
+ if (status != STATUS_OK) { |
+ HandleReadResult(FROM_HERE, status); |
registrations->clear(); |
- return STATUS_ERROR_CORRUPTED; |
+ return status; |
} |
registrations->push_back(registration); |
} |
- return STATUS_OK; |
+ |
+ HandleReadResult(FROM_HERE, status); |
+ return status; |
} |
ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetAllRegistrations( |
@@ -364,24 +435,28 @@ ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetAllRegistrations( |
scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
for (itr->Seek(kRegKeyPrefix); itr->Valid(); itr->Next()) { |
- if (!itr->status().ok()) { |
- HandleError(FROM_HERE, itr->status()); |
+ status = LevelDBStatusToStatus(itr->status()); |
+ if (status != STATUS_OK) { |
+ HandleReadResult(FROM_HERE, status); |
registrations->clear(); |
- return LevelDBStatusToStatus(itr->status()); |
+ return status; |
} |
if (!RemovePrefix(itr->key().ToString(), kRegKeyPrefix, NULL)) |
break; |
RegistrationData registration; |
- if (!ParseRegistrationData(itr->value().ToString(), ®istration)) { |
- HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); |
+ status = ParseRegistrationData(itr->value().ToString(), ®istration); |
+ if (status != STATUS_OK) { |
+ HandleReadResult(FROM_HERE, status); |
registrations->clear(); |
- return STATUS_ERROR_CORRUPTED; |
+ return status; |
} |
registrations->push_back(registration); |
} |
- return STATUS_OK; |
+ |
+ HandleReadResult(FROM_HERE, status); |
+ return status; |
} |
ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistration( |
@@ -675,18 +750,18 @@ ServiceWorkerDatabase::Status ServiceWorkerDatabase::LazyOpen( |
} |
leveldb::DB* db = NULL; |
- leveldb::Status db_status = |
- leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db); |
- if (!db_status.ok()) { |
+ Status status = LevelDBStatusToStatus( |
+ leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db)); |
+ HandleOpenResult(FROM_HERE, status); |
+ if (status != STATUS_OK) { |
DCHECK(!db); |
// TODO(nhiroki): Should we retry to open the database? |
- HandleError(FROM_HERE, db_status); |
- return LevelDBStatusToStatus(db_status); |
+ return status; |
} |
db_.reset(db); |
int64 db_version; |
- Status status = ReadDatabaseVersion(&db_version); |
+ status = ReadDatabaseVersion(&db_version); |
if (status != STATUS_OK) |
return status; |
DCHECK_LE(0, db_version); |
@@ -711,26 +786,21 @@ ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadNextAvailableId( |
DCHECK(next_avail_id); |
std::string value; |
- leveldb::Status status = db_->Get(leveldb::ReadOptions(), id_key, &value); |
- if (status.IsNotFound()) { |
+ Status status = LevelDBStatusToStatus( |
+ db_->Get(leveldb::ReadOptions(), id_key, &value)); |
+ if (status == STATUS_ERROR_NOT_FOUND) { |
// Nobody has gotten the next resource id for |id_key|. |
*next_avail_id = 0; |
+ HandleReadResult(FROM_HERE, STATUS_OK); |
return STATUS_OK; |
+ } else if (status != STATUS_OK) { |
+ HandleReadResult(FROM_HERE, status); |
+ return status; |
} |
- if (!status.ok()) { |
- HandleError(FROM_HERE, status); |
- return LevelDBStatusToStatus(status); |
- } |
- |
- int64 parsed; |
- if (!base::StringToInt64(value, &parsed)) { |
- HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); |
- return STATUS_ERROR_CORRUPTED; |
- } |
- |
- *next_avail_id = parsed; |
- return STATUS_OK; |
+ status = ParseId(value, next_avail_id); |
+ HandleReadResult(FROM_HERE, status); |
+ return status; |
} |
ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistrationData( |
@@ -739,52 +809,54 @@ ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistrationData( |
RegistrationData* registration) { |
DCHECK(registration); |
- std::string key = CreateRegistrationKey(registration_id, origin); |
- |
+ const std::string key = CreateRegistrationKey(registration_id, origin); |
std::string value; |
- leveldb::Status status = db_->Get(leveldb::ReadOptions(), key, &value); |
- if (!status.ok()) { |
- if (!status.IsNotFound()) |
- HandleError(FROM_HERE, status); |
- return LevelDBStatusToStatus(status); |
- } |
- |
- RegistrationData parsed; |
- if (!ParseRegistrationData(value, &parsed)) { |
- HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); |
- return STATUS_ERROR_CORRUPTED; |
+ Status status = LevelDBStatusToStatus( |
+ db_->Get(leveldb::ReadOptions(), key, &value)); |
+ if (status != STATUS_OK) { |
+ HandleReadResult( |
+ FROM_HERE, |
+ status == STATUS_ERROR_NOT_FOUND ? STATUS_OK : status); |
+ return status; |
} |
- *registration = parsed; |
- return STATUS_OK; |
+ status = ParseRegistrationData(value, registration); |
+ HandleReadResult(FROM_HERE, status); |
+ return status; |
} |
ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceRecords( |
int64 version_id, |
std::vector<ResourceRecord>* resources) { |
- DCHECK(resources); |
+ DCHECK(resources->empty()); |
+ |
+ Status status = STATUS_OK; |
+ const std::string prefix = CreateResourceRecordKeyPrefix(version_id); |
- std::string prefix = CreateResourceRecordKeyPrefix(version_id); |
scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
for (itr->Seek(prefix); itr->Valid(); itr->Next()) { |
- if (!itr->status().ok()) { |
- HandleError(FROM_HERE, itr->status()); |
+ Status status = LevelDBStatusToStatus(itr->status()); |
+ if (status != STATUS_OK) { |
+ HandleReadResult(FROM_HERE, status); |
resources->clear(); |
- return LevelDBStatusToStatus(itr->status()); |
+ return status; |
} |
if (!RemovePrefix(itr->key().ToString(), prefix, NULL)) |
break; |
ResourceRecord resource; |
- if (!ParseResourceRecord(itr->value().ToString(), &resource)) { |
- HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); |
+ status = ParseResourceRecord(itr->value().ToString(), &resource); |
+ if (status != STATUS_OK) { |
+ HandleReadResult(FROM_HERE, status); |
resources->clear(); |
- return STATUS_ERROR_CORRUPTED; |
+ return status; |
} |
resources->push_back(resource); |
} |
- return STATUS_OK; |
+ |
+ HandleReadResult(FROM_HERE, status); |
+ return status; |
} |
ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceRecords( |
@@ -793,23 +865,27 @@ ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceRecords( |
leveldb::WriteBatch* batch) { |
DCHECK(batch); |
- std::string prefix = CreateResourceRecordKeyPrefix(version_id); |
+ Status status = STATUS_OK; |
+ const std::string prefix = CreateResourceRecordKeyPrefix(version_id); |
+ |
scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
for (itr->Seek(prefix); itr->Valid(); itr->Next()) { |
- if (!itr->status().ok()) { |
- HandleError(FROM_HERE, itr->status()); |
- return LevelDBStatusToStatus(itr->status()); |
+ status = LevelDBStatusToStatus(itr->status()); |
+ if (status != STATUS_OK) { |
+ HandleReadResult(FROM_HERE, status); |
+ return status; |
} |
- std::string key = itr->key().ToString(); |
+ const std::string key = itr->key().ToString(); |
std::string unprefixed; |
if (!RemovePrefix(key, prefix, &unprefixed)) |
break; |
int64 resource_id; |
- if (!base::StringToInt64(unprefixed, &resource_id)) { |
- HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); |
- return STATUS_ERROR_CORRUPTED; |
+ status = ParseId(unprefixed, &resource_id); |
+ if (status != STATUS_OK) { |
+ HandleReadResult(FROM_HERE, status); |
+ return status; |
} |
// Remove a resource record. |
@@ -820,7 +896,9 @@ ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceRecords( |
PutPurgeableResourceIdToBatch(resource_id, batch); |
newly_purgeable_resources->push_back(resource_id); |
} |
- return STATUS_OK; |
+ |
+ HandleReadResult(FROM_HERE, status); |
+ return status; |
} |
ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceIds( |
@@ -838,10 +916,11 @@ ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceIds( |
scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
for (itr->Seek(id_key_prefix); itr->Valid(); itr->Next()) { |
- if (!itr->status().ok()) { |
- HandleError(FROM_HERE, itr->status()); |
+ status = LevelDBStatusToStatus(itr->status()); |
+ if (status != STATUS_OK) { |
+ HandleReadResult(FROM_HERE, status); |
ids->clear(); |
- return LevelDBStatusToStatus(itr->status()); |
+ return status; |
} |
std::string unprefixed; |
@@ -849,14 +928,17 @@ ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceIds( |
break; |
int64 resource_id; |
- if (!base::StringToInt64(unprefixed, &resource_id)) { |
- HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); |
+ status = ParseId(unprefixed, &resource_id); |
+ if (status != STATUS_OK) { |
+ HandleReadResult(FROM_HERE, status); |
ids->clear(); |
- return STATUS_ERROR_CORRUPTED; |
+ return status; |
} |
ids->insert(resource_id); |
} |
- return STATUS_OK; |
+ |
+ HandleReadResult(FROM_HERE, status); |
+ return status; |
} |
ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIds( |
@@ -921,32 +1003,23 @@ ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceIdsInBatch( |
ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadDatabaseVersion( |
int64* db_version) { |
std::string value; |
- leveldb::Status status = |
- db_->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value); |
- if (status.IsNotFound()) { |
+ Status status = LevelDBStatusToStatus( |
+ db_->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value)); |
+ if (status == STATUS_ERROR_NOT_FOUND) { |
// The database hasn't been initialized yet. |
*db_version = 0; |
+ HandleReadResult(FROM_HERE, STATUS_OK); |
return STATUS_OK; |
} |
- if (!status.ok()) { |
- HandleError(FROM_HERE, status); |
- return LevelDBStatusToStatus(status); |
- } |
- int64 parsed; |
- if (!base::StringToInt64(value, &parsed)) { |
- HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); |
- return STATUS_ERROR_CORRUPTED; |
- } |
- |
- const int kFirstValidVersion = 1; |
- if (parsed < kFirstValidVersion || kCurrentSchemaVersion < parsed) { |
- HandleError(FROM_HERE, leveldb::Status::Corruption("invalid DB version")); |
- return STATUS_ERROR_CORRUPTED; |
+ if (status != STATUS_OK) { |
+ HandleReadResult(FROM_HERE, status); |
+ return status; |
} |
- *db_version = parsed; |
- return STATUS_OK; |
+ status = ParseDatabaseVersion(value, db_version); |
+ HandleReadResult(FROM_HERE, status); |
+ return status; |
} |
ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteBatch( |
@@ -960,10 +1033,10 @@ ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteBatch( |
state_ = INITIALIZED; |
} |
- leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch); |
- if (!status.ok()) |
- HandleError(FROM_HERE, status); |
- return LevelDBStatusToStatus(status); |
+ Status status = LevelDBStatusToStatus( |
+ db_->Write(leveldb::WriteOptions(), batch)); |
+ HandleWriteResult(FROM_HERE, status); |
+ return status; |
} |
void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded( |
@@ -988,14 +1061,44 @@ bool ServiceWorkerDatabase::IsOpen() { |
return db_ != NULL; |
} |
-void ServiceWorkerDatabase::HandleError( |
+void ServiceWorkerDatabase::Disable( |
const tracked_objects::Location& from_here, |
- const leveldb::Status& status) { |
- // TODO(nhiroki): Add an UMA histogram. |
+ Status status) { |
DLOG(ERROR) << "Failed at: " << from_here.ToString() |
- << " with error: " << status.ToString(); |
+ << " with error: " << StatusToString(status); |
+ DLOG(ERROR) << "ServiceWorkerDatabase is disabled."; |
state_ = DISABLED; |
db_.reset(); |
} |
+void ServiceWorkerDatabase::HandleOpenResult( |
+ const tracked_objects::Location& from_here, |
+ Status status) { |
+ if (status != ServiceWorkerDatabase::STATUS_OK) |
+ Disable(from_here, status); |
+ UMA_HISTOGRAM_ENUMERATION(kOpenResultHistogramLabel, |
+ status, |
+ ServiceWorkerDatabase::STATUS_ERROR_MAX); |
+} |
+ |
+void ServiceWorkerDatabase::HandleReadResult( |
+ const tracked_objects::Location& from_here, |
+ Status status) { |
+ if (status != ServiceWorkerDatabase::STATUS_OK) |
+ Disable(from_here, status); |
+ UMA_HISTOGRAM_ENUMERATION(kReadResultHistogramLabel, |
+ status, |
+ ServiceWorkerDatabase::STATUS_ERROR_MAX); |
+} |
+ |
+void ServiceWorkerDatabase::HandleWriteResult( |
+ const tracked_objects::Location& from_here, |
+ Status status) { |
+ if (status != ServiceWorkerDatabase::STATUS_OK) |
+ Disable(from_here, status); |
+ UMA_HISTOGRAM_ENUMERATION(kWriteResultHistogramLabel, |
+ status, |
+ ServiceWorkerDatabase::STATUS_ERROR_MAX); |
+} |
+ |
} // namespace content |