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 77e33473f439b968c3b8be3c9d0900cab5e45e01..bb909dee9f67669bb8a459aaee751219486c4602 100644 |
--- a/content/browser/service_worker/service_worker_database.cc |
+++ b/content/browser/service_worker/service_worker_database.cc |
@@ -7,8 +7,12 @@ |
#include <string> |
#include "base/file_util.h" |
+#include "base/location.h" |
#include "base/logging.h" |
#include "base/strings/string_number_conversions.h" |
+#include "base/strings/string_split.h" |
+#include "base/strings/string_util.h" |
+#include "content/browser/service_worker/service_worker_database.pb.h" |
michaeln
2014/04/24 00:35:04
so a build step on the .proto file produces c stru
nhiroki
2014/04/24 05:57:30
Yes, you're right.
|
#include "third_party/leveldatabase/src/helpers/memenv/memenv.h" |
#include "third_party/leveldatabase/src/include/leveldb/db.h" |
#include "third_party/leveldatabase/src/include/leveldb/env.h" |
@@ -17,18 +21,37 @@ |
// LevelDB database schema |
// ======================= |
// |
+// NOTE |
+// - int64 value is serialized as a string by base::Int64ToString(). |
+// |
// Version 1 (in sorted order) |
// key: "DB_VERSION" |
-// value: <int64 serialized as a string> |
+// value: "1" |
// |
// key: "NEXT_REGISTRATION_ID" |
-// value: <int64 serialized as a string> |
+// value: <int64 'next_available_registration_id'> |
// |
// key: "NEXT_RESOURCE_ID" |
-// value: <int64 serialized as a string> |
+// value: <int64 'next_available_resource_id'> |
// |
// key: "NEXT_VERSION_ID" |
-// value: <int64 serialized as a string> |
+// value: <int64 'next_available_version_id'> |
+// |
+// key: "PRES:" + <int64 'purgeable_resource_id'> |
+// value: <empty> |
+// |
+// key: "REG:" + (1) + '\x00' + (2) |
+// (1) <GURL 'origin_url' serialized by GURL::spec()> |
+// (2) <int64 'registration_id'> |
+// (ex. "REG:http://example.com\x00123456") |
+// value: <ServiceWorkerRegistrationData serialized as a string> |
+// |
+// key: "RES:" + <int64 'version_id'> + '\x00' + <int64 'resource_id'> |
+// (ex. "RES:123456\x00654321") |
+// value: <ServiceWorkerResourceRecord serialized as a string> |
+// |
+// key: "URES:" + <int64 'uncommitted_resource_id'> |
+// value: <empty> |
namespace content { |
@@ -39,20 +62,119 @@ const char kNextRegIdKey[] = "NEXT_REGISTRATION_ID"; |
const char kNextResIdKey[] = "NEXT_RESOURCE_ID"; |
const char kNextVerIdKey[] = "NEXT_VERSION_ID"; |
+const char kRegKeyPrefix[] = "REG:"; |
+const char kResKeyPrefix[] = "RES:"; |
+const char kUncommittedResKeyPrefix[] = "URES:"; |
+const char kPurgeableResKeyPrefix[] = "PRES:"; |
+const char kKeySeparator = '\x00'; |
+ |
const int64 kCurrentSchemaVersion = 1; |
-} // namespace |
+std::string RemovePrefix(const std::string& str, |
jsbell
2014/04/23 22:55:54
This is a little weird since you can't tell if it
nhiroki
2014/04/24 12:12:38
Done.
|
+ const std::string& prefix) { |
+ if (StartsWithASCII(str, prefix, true)) |
+ return str.substr(prefix.size()); |
+ return str; |
+} |
+ |
+std::string CreateRegistrationKey(const ServiceWorkerRegistrationData& data) { |
+ GURL origin = GURL(data.scope_url()).GetOrigin(); |
+ std::ostringstream out; |
jsbell
2014/04/23 22:55:54
#include <sstream> for ostringstream?
michaeln
2014/04/24 00:35:04
should probably use #include "base/strings/stringp
nhiroki
2014/04/24 12:12:38
Replaced them with StringPrintf.
|
+ out << kRegKeyPrefix << origin.spec() << kKeySeparator |
+ << base::Int64ToString(data.registration_id()); |
+ return out.str(); |
+} |
+ |
+std::string CreateResourceKey( |
+ const ServiceWorkerResourceRecord& resource, |
+ int64 version_id) { |
+ std::ostringstream out; |
+ out << kResKeyPrefix << base::Int64ToString(version_id) |
+ << kKeySeparator << base::Int64ToString(resource.resource_id()); |
+ return out.str(); |
+} |
+ |
+std::string CreateUncommittedResourceKey(int64 resource_id) { |
+ std::ostringstream out; |
+ out << kUncommittedResKeyPrefix << base::Int64ToString(resource_id); |
+ return out.str(); |
+} |
-ServiceWorkerDatabase::RegistrationData::RegistrationData() |
- : registration_id(-1), |
- version_id(-1), |
- is_active(false), |
- has_fetch_handler(false) { |
+std::string CreatePurgeableResourceKey(int64 resource_id) { |
+ std::ostringstream out; |
+ out << kPurgeableResKeyPrefix << base::Int64ToString(resource_id); |
+ return out.str(); |
} |
-ServiceWorkerDatabase::RegistrationData::~RegistrationData() { |
+void PutRegistrationDataToBatch(const ServiceWorkerRegistrationData& data, |
+ leveldb::WriteBatch* batch) { |
+ DCHECK(batch); |
+ std::string value; |
+ bool success = data.SerializeToString(&value); |
+ DCHECK(success); |
+ batch->Put(CreateRegistrationKey(data), value); |
+} |
+ |
+void PutResourceRecordToBatch(const ServiceWorkerResourceRecord& resource, |
+ int64 version_id, |
+ leveldb::WriteBatch* batch) { |
+ DCHECK(batch); |
+ std::string value; |
+ bool success = resource.SerializeToString(&value); |
+ DCHECK(success); |
+ batch->Put(CreateResourceKey(resource, version_id), value); |
+} |
+ |
+bool ParseRegistrationKey(const std::string& key, |
+ GURL* origin_out, |
+ int64* registration_id_out) { |
+ std::string unprefixed = RemovePrefix(key, kRegKeyPrefix); |
+ std::vector<std::string> tokens; |
+ base::SplitString(unprefixed, kKeySeparator, &tokens); |
+ if (tokens.size() != 2) |
+ return false; |
+ |
+ GURL origin(tokens[0]); |
+ if (!origin.is_valid()) |
+ return false; |
+ |
+ int64 registration_id; |
+ if (!base::StringToInt64(tokens[1], ®istration_id)) |
+ return false; |
+ |
+ if (origin_out) |
+ *origin_out = origin; |
+ if (registration_id_out) |
+ *registration_id_out = registration_id; |
+ return true; |
} |
+bool ParseResourceKey(const std::string& key, |
+ int64* version_id_out, |
+ int64* resource_id_out) { |
+ std::string unprefixed = RemovePrefix(key, kResKeyPrefix); |
+ std::vector<std::string> tokens; |
+ base::SplitString(unprefixed, kKeySeparator, &tokens); |
+ if (tokens.size() != 2) |
+ return false; |
+ |
+ int64 version_id; |
+ if (!base::StringToInt64(tokens[0], &version_id)) |
+ return false; |
+ |
+ int64 resource_id; |
+ if (!base::StringToInt64(tokens[1], &resource_id)) |
+ return false; |
+ |
+ if (version_id_out) |
+ *version_id_out = version_id; |
+ if (resource_id_out) |
+ *resource_id_out = resource_id; |
+ return true; |
+} |
+ |
+} // namespace |
+ |
ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath& path) |
: path_(path), |
is_disabled_(false), |
@@ -80,9 +202,9 @@ bool ServiceWorkerDatabase::GetNextAvailableIds( |
int64 ver_id = -1; |
int64 res_id = -1; |
- if (!ReadInt64(kNextRegIdKey, ®_id) || |
- !ReadInt64(kNextVerIdKey, &ver_id) || |
- !ReadInt64(kNextResIdKey, &res_id)) |
+ if (!ReadNextAvailableId(kNextRegIdKey, ®_id) || |
+ !ReadNextAvailableId(kNextVerIdKey, &ver_id) || |
+ !ReadNextAvailableId(kNextResIdKey, &res_id)) |
return false; |
*next_avail_registration_id = reg_id; |
@@ -91,6 +213,304 @@ bool ServiceWorkerDatabase::GetNextAvailableIds( |
return true; |
} |
+bool ServiceWorkerDatabase::GetOriginsWithRegistrations( |
michaeln
2014/04/24 00:35:04
This method and GetNextAvailableIds will be called
nhiroki
2014/04/24 05:57:30
I looked over some docs and sourcecode for leveldb
nhiroki
2014/04/24 12:12:38
Done.
|
+ std::set<GURL>* origins) { |
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
+ DCHECK(origins); |
+ |
+ if (!LazyOpen(false) || is_disabled_) |
+ return false; |
+ |
+ GURL origin; |
+ scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
+ for (itr->Seek(kRegKeyPrefix); itr->Valid(); itr->Next()) { |
+ std::string key = itr->key().ToString(); |
+ if (!StartsWithASCII(key, kRegKeyPrefix, true)) |
+ break; |
+ |
+ if (!ParseRegistrationKey(key, &origin, NULL /* registration_id */)) { |
+ HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); |
+ origins->clear(); |
+ return false; |
+ } |
+ origins->insert(origin); |
+ } |
+ return true; |
jsbell
2014/04/23 22:55:54
If the iterator fails (!itr->status().ok()) should
nhiroki
2014/04/24 12:12:38
Done.
|
+} |
+ |
+bool ServiceWorkerDatabase::GetRegistrationsForOrigin( |
+ const GURL& origin, |
+ std::vector<ServiceWorkerRegistrationData>* registrations) { |
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
+ DCHECK(registrations); |
+ |
+ if (!LazyOpen(false) || is_disabled_) |
+ return false; |
+ |
+ // Create a key prefix for registrations. |
+ std::ostringstream out; |
+ out << kRegKeyPrefix << origin.spec() << kKeySeparator; |
+ std::string prefix = out.str(); |
+ |
+ scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
+ for (itr->Seek(prefix); itr->Valid(); itr->Next()) { |
+ std::string key = itr->key().ToString(); |
+ if (!StartsWithASCII(key, prefix, true)) |
+ break; |
+ |
+ ServiceWorkerRegistrationData registration; |
+ if (!registration.ParseFromString(itr->value().ToString())) { |
jsbell
2014/04/23 22:55:54
Is there any other post-parsing sanity checking th
nhiroki
2014/04/24 12:12:38
For now, we seems to be able to check only script/
|
+ HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); |
+ registrations->clear(); |
+ return false; |
+ } |
+ |
+ registrations->push_back(registration); |
+ } |
+ return true; |
+} |
+ |
+bool ServiceWorkerDatabase::ReadRegistration( |
+ int64 registration_id, |
+ ServiceWorkerRegistrationData* registration, |
+ std::vector<ServiceWorkerResourceRecord>* resources) { |
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
+ DCHECK(registration); |
+ DCHECK(resources); |
+ |
+ if (!LazyOpen(false) || is_disabled_) |
+ return false; |
+ |
+ ServiceWorkerRegistrationData value; |
+ if (!ReadRegistrationData(registration_id, &value)) |
+ return false; |
+ |
+ if (!ReadResourceRecords(value.version_id(), resources)) |
+ return false; |
+ |
+ *registration = value; |
+ return true; |
+} |
+ |
+bool ServiceWorkerDatabase::WriteRegistration( |
+ const ServiceWorkerRegistrationData& registration, |
+ const std::vector<ServiceWorkerResourceRecord>& resources) { |
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
+ if (!LazyOpen(true) || is_disabled_) |
+ return false; |
+ |
+ leveldb::WriteBatch batch; |
+ if (!BumpNextAvailableIdIfNeeded( |
+ kNextRegIdKey, registration.registration_id(), &batch) || |
+ !BumpNextAvailableIdIfNeeded( |
+ kNextVerIdKey, registration.version_id(), &batch)) { |
+ return false; |
+ } |
+ |
+ PutRegistrationDataToBatch(registration, &batch); |
+ |
+ std::vector<ServiceWorkerResourceRecord>::const_iterator itr; |
+ for (itr = resources.begin(); itr != resources.end(); ++itr) |
+ PutResourceRecordToBatch(*itr, registration.version_id(), &batch); |
michaeln
2014/04/24 00:35:04
If it helps, we could defer dealing with ResourceR
nhiroki
2014/04/24 12:12:38
Okay, dropped them.
|
+ |
+ return WriteBatch(&batch); |
+} |
+ |
+bool ServiceWorkerDatabase::UpdateVersionToActive(int64 registration_id) { |
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
+ if (!LazyOpen(false) || is_disabled_) |
+ return false; |
+ |
+ ServiceWorkerRegistrationData data; |
+ if (!ReadRegistrationData(registration_id, &data)) |
+ return false; |
+ |
+ data.set_is_active(true); |
+ |
+ leveldb::WriteBatch batch; |
+ PutRegistrationDataToBatch(data, &batch); |
+ return WriteBatch(&batch); |
+} |
+ |
+bool ServiceWorkerDatabase::UpdateLastCheckTime( |
+ int64 registration_id, |
+ const base::Time& time) { |
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
+ if (!LazyOpen(false) || is_disabled_) |
+ return false; |
+ |
+ ServiceWorkerRegistrationData data; |
+ if (!ReadRegistrationData(registration_id, &data)) |
+ return false; |
+ |
+ data.set_last_update_check_time(time.ToInternalValue()); |
+ |
+ leveldb::WriteBatch batch; |
+ PutRegistrationDataToBatch(data, &batch); |
+ return WriteBatch(&batch); |
+} |
+ |
+bool ServiceWorkerDatabase::DeleteRegistration( |
+ int64 registration_id) { |
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
+ if (!LazyOpen(false) || is_disabled_) |
+ return false; |
+ |
+ leveldb::WriteBatch batch; |
+ ServiceWorkerRegistrationData data; |
+ if (!ReadRegistrationData(registration_id, &data)) { |
+ if (is_disabled_) |
+ return false; |
+ // Just not found. |
+ return true; |
+ } |
+ batch.Delete(CreateRegistrationKey(data)); |
+ |
+ // Create a key prefix for resource records. |
+ std::ostringstream out; |
+ out << kResKeyPrefix << data.version_id() << kKeySeparator; |
+ std::string prefix = out.str(); |
+ |
+ scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
+ for (itr->Seek(prefix); itr->Valid(); itr->Next()) { |
+ std::string key = itr->key().ToString(); |
+ if (!StartsWithASCII(key, prefix, true)) |
+ break; |
+ batch.Delete(key); |
+ } |
+ |
+ return WriteBatch(&batch); |
+} |
+ |
+bool ServiceWorkerDatabase::GetUncommittedResourceIds(std::set<int64>* ids) { |
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
+ DCHECK(ids); |
+ |
+ if (!LazyOpen(false) || is_disabled_) |
+ return false; |
+ |
+ scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
+ for (itr->Seek(kUncommittedResKeyPrefix); itr->Valid(); itr->Next()) { |
+ std::string key = itr->key().ToString(); |
+ if (!StartsWithASCII(key, kUncommittedResKeyPrefix, true)) |
+ break; |
+ |
+ std::string unprefixed = RemovePrefix(key, kUncommittedResKeyPrefix); |
+ int64 resource_id; |
+ if (!base::StringToInt64(unprefixed, &resource_id)) { |
+ HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); |
+ ids->clear(); |
+ return false; |
+ } |
+ ids->insert(resource_id); |
+ } |
+ return true; |
+} |
+ |
+bool ServiceWorkerDatabase::WriteUncommittedResourceIds( |
+ const std::set<int64>& ids) { |
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
+ if (!LazyOpen(true) || is_disabled_) |
+ return false; |
+ if (ids.empty()) |
+ return true; |
+ |
+ leveldb::WriteBatch batch; |
+ for (std::set<int64>::const_iterator itr = ids.begin(); |
+ itr != ids.end(); ++itr) { |
+ // Value should be empty. |
+ batch.Put(CreateUncommittedResourceKey(*itr), ""); |
+ } |
+ return WriteBatch(&batch); |
+} |
+ |
+bool ServiceWorkerDatabase::ClearUncommittedResourceIds( |
+ const std::set<int64>& ids) { |
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
+ if (!LazyOpen(true) || is_disabled_) |
+ return false; |
+ if (ids.empty()) |
+ return true; |
+ |
+ leveldb::WriteBatch batch; |
+ for (std::set<int64>::const_iterator itr = ids.begin(); |
+ itr != ids.end(); ++itr) { |
+ batch.Delete(CreateUncommittedResourceKey(*itr)); |
+ } |
+ return WriteBatch(&batch); |
+} |
+ |
+bool ServiceWorkerDatabase::GetPurgeableResourceIds(std::set<int64>* ids) { |
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
+ DCHECK(ids); |
+ |
+ if (!LazyOpen(false) || is_disabled_) |
+ return false; |
+ |
+ scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
+ for (itr->Seek(kPurgeableResKeyPrefix); itr->Valid(); itr->Next()) { |
+ std::string key = itr->key().ToString(); |
+ if (!StartsWithASCII(key, kPurgeableResKeyPrefix, true)) |
+ break; |
+ |
+ std::string unprefixed = RemovePrefix(key, kPurgeableResKeyPrefix); |
+ int64 resource_id; |
+ if (!base::StringToInt64(unprefixed, &resource_id)) { |
+ HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); |
+ ids->clear(); |
+ return false; |
+ } |
+ ids->insert(resource_id); |
+ } |
+ return true; |
+} |
+ |
+bool ServiceWorkerDatabase::WritePurgeableResourceIds( |
+ const std::set<int64>& ids) { |
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
+ if (!LazyOpen(true) || is_disabled_) |
+ return false; |
+ if (ids.empty()) |
+ return true; |
+ |
+ leveldb::WriteBatch batch; |
+ for (std::set<int64>::const_iterator itr = ids.begin(); |
+ itr != ids.end(); ++itr) { |
+ // Value should be empty. |
+ batch.Put(CreatePurgeableResourceKey(*itr), ""); |
+ } |
+ return WriteBatch(&batch); |
+} |
+ |
+bool ServiceWorkerDatabase::ClearPurgeableResourceIds( |
+ const std::set<int64>& ids) { |
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
+ if (!LazyOpen(true) || is_disabled_) |
+ return false; |
+ if (ids.empty()) |
+ return true; |
+ |
+ leveldb::WriteBatch batch; |
+ for (std::set<int64>::const_iterator itr = ids.begin(); |
+ itr != ids.end(); ++itr) { |
+ batch.Delete(CreatePurgeableResourceKey(*itr)); |
+ } |
+ return WriteBatch(&batch); |
+} |
+ |
+bool ServiceWorkerDatabase::DeleteAllDataForOrigin( |
+ const GURL& origin) { |
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
+ NOTIMPLEMENTED(); |
+ return false; |
+} |
+ |
+bool ServiceWorkerDatabase::DeleteAllData() { |
+ DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
+ NOTIMPLEMENTED(); |
+ return false; |
+} |
+ |
bool ServiceWorkerDatabase::LazyOpen(bool create_if_needed) { |
DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
if (IsOpen()) |
@@ -125,15 +545,13 @@ bool ServiceWorkerDatabase::LazyOpen(bool create_if_needed) { |
if (!status.ok()) { |
DCHECK(!db); |
// TODO(nhiroki): Should we retry to open the database? |
- DLOG(ERROR) << "Failed to open LevelDB database: " << status.ToString(); |
- is_disabled_ = true; |
+ HandleError(FROM_HERE, status); |
return false; |
} |
db_.reset(db); |
if (IsEmpty() && !PopulateInitialData()) { |
DLOG(ERROR) << "Failed to populate the database."; |
- is_disabled_ = true; |
db_.reset(); |
return false; |
} |
@@ -141,58 +559,122 @@ bool ServiceWorkerDatabase::LazyOpen(bool create_if_needed) { |
} |
bool ServiceWorkerDatabase::PopulateInitialData() { |
- scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); |
- batch->Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion)); |
- batch->Put(kNextRegIdKey, "0"); |
- batch->Put(kNextResIdKey, "0"); |
- batch->Put(kNextVerIdKey, "0"); |
- return WriteBatch(batch.Pass()); |
+ leveldb::WriteBatch batch; |
+ batch.Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion)); |
+ batch.Put(kNextRegIdKey, "0"); |
+ batch.Put(kNextResIdKey, "0"); |
+ batch.Put(kNextVerIdKey, "0"); |
+ return WriteBatch(&batch); |
} |
-bool ServiceWorkerDatabase::ReadInt64( |
- const leveldb::Slice& key, |
- int64* value_out) { |
- DCHECK(value_out); |
+bool ServiceWorkerDatabase::ReadNextAvailableId( |
+ const char* id_key, int64* next_avail_id) { |
+ DCHECK(id_key); |
+ DCHECK(next_avail_id); |
std::string value; |
- leveldb::Status status = db_->Get(leveldb::ReadOptions(), key, &value); |
+ leveldb::Status status = db_->Get(leveldb::ReadOptions(), id_key, &value); |
if (!status.ok()) { |
- DLOG(ERROR) << "Failed to read data keyed by " |
- << key.ToString() << ": " << status.ToString(); |
- is_disabled_ = true; |
- if (status.IsCorruption()) |
- was_corruption_detected_ = true; |
+ HandleError(FROM_HERE, status); |
return false; |
} |
- int64 parsed = -1; |
- if (!base::StringToInt64(value, &parsed)) { |
- DLOG(ERROR) << "Database might be corrupted: " |
- << key.ToString() << ", " << value; |
- is_disabled_ = true; |
- was_corruption_detected_ = true; |
+ int64 parsed_id; |
+ if (!base::StringToInt64(value, &parsed_id)) { |
+ HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); |
return false; |
} |
- *value_out = parsed; |
+ *next_avail_id = parsed_id; |
return true; |
} |
-bool ServiceWorkerDatabase::WriteBatch(scoped_ptr<leveldb::WriteBatch> batch) { |
- if (!batch) |
- return true; |
+bool ServiceWorkerDatabase::ReadRegistrationData( |
+ int64 registration_id, |
+ ServiceWorkerRegistrationData* data_out) { |
+ int64 parsed_id; |
+ scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
+ for (itr->Seek(kRegKeyPrefix); itr->Valid(); itr->Next()) { |
michaeln
2014/04/24 00:35:04
Oh no... linear scans. Let's massage things so thi
nhiroki
2014/04/24 05:57:30
That sounds reasonable and I thought I'd like to c
|
+ std::string key = itr->key().ToString(); |
+ if (!StartsWithASCII(key, kRegKeyPrefix, true)) |
+ return false; |
+ |
+ if (!ParseRegistrationKey(key, NULL /* origin */, &parsed_id)) { |
+ HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); |
+ return false; |
+ } |
+ if (registration_id != parsed_id) |
+ continue; |
+ |
+ ServiceWorkerRegistrationData data; |
+ if (!data.ParseFromString(itr->value().ToString())) { |
+ HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); |
+ return false; |
+ } |
- leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch.get()); |
- if (status.ok()) |
+ *data_out = data; |
return true; |
+ } |
- DLOG(ERROR) << "Failed to write the batch: " << status.ToString(); |
- is_disabled_ = true; |
- if (status.IsCorruption()) |
- was_corruption_detected_ = true; |
return false; |
} |
+bool ServiceWorkerDatabase::ReadResourceRecords( |
+ int64 version_id, |
+ std::vector<ServiceWorkerResourceRecord>* resources) { |
+ DCHECK(resources); |
+ |
+ // Create a key prefix for resource records. |
+ std::ostringstream out; |
+ out << kResKeyPrefix << version_id << kKeySeparator; |
+ std::string prefix = out.str(); |
+ |
+ int64 resource_id; |
+ scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
+ for (itr->Seek(prefix); itr->Valid(); itr->Next()) { |
+ std::string key = itr->key().ToString(); |
+ if (!StartsWithASCII(key, prefix, true)) |
+ break; |
+ |
+ if (!ParseResourceKey(key, NULL /* version_id */, &resource_id)) { |
+ HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); |
+ resources->clear(); |
+ return false; |
+ } |
+ |
+ ServiceWorkerResourceRecord resource; |
+ if (!resource.ParseFromString(itr->value().ToString())) { |
+ HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); |
+ resources->clear(); |
+ return false; |
+ } |
+ |
+ resources->push_back(resource); |
+ } |
+ return true; |
+} |
+ |
+bool ServiceWorkerDatabase::WriteBatch(leveldb::WriteBatch* batch) { |
+ DCHECK(batch); |
+ leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch); |
+ if (!status.ok()) { |
+ HandleError(FROM_HERE, status); |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+bool ServiceWorkerDatabase::BumpNextAvailableIdIfNeeded( |
+ const char* id_key, int64 used_id, leveldb::WriteBatch* batch) { |
+ DCHECK(batch); |
+ int64 next_available_id; |
+ if (!ReadNextAvailableId(id_key, &next_available_id)) |
+ return false; |
+ if (next_available_id <= used_id) |
+ batch->Put(id_key, base::Int64ToString(used_id + 1)); |
+ return true; |
+} |
+ |
bool ServiceWorkerDatabase::IsOpen() { |
return db_.get() != NULL; |
} |
@@ -203,4 +685,14 @@ bool ServiceWorkerDatabase::IsEmpty() { |
return !itr->Valid(); |
} |
+void ServiceWorkerDatabase::HandleError( |
jsbell
2014/04/23 22:55:54
Can you add a TODO: for adding an UMA histogram?
nhiroki
2014/04/24 12:12:38
Done.
|
+ const tracked_objects::Location& from_here, |
+ const leveldb::Status& status) { |
+ DLOG(ERROR) << "Failed at: " << from_here.ToString() |
+ << " with error: " << status.ToString(); |
+ is_disabled_ = true; |
+ if (status.IsCorruption()) |
+ was_corruption_detected_ = true; |
+} |
+ |
} // namespace content |