1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/service_worker/service_worker_database.h"
9 #include "base/file_util.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_split.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "content/browser/service_worker/service_worker_database.pb.h"
17 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
18 #include "third_party/leveldatabase/src/include/leveldb/db.h"
19 #include "third_party/leveldatabase/src/include/leveldb/env.h"
20 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
22 // LevelDB database schema
23 // =======================
26 // - int64 value is serialized as a string by base::Int64ToString().
27 // - GURL value is serialized as a string by GURL::spec().
29 // Version 1 (in sorted order)
30 // key: "INITDATA_DB_VERSION"
33 // key: "INITDATA_NEXT_REGISTRATION_ID"
34 // value: <int64 'next_available_registration_id'>
36 // key: "INITDATA_NEXT_RESOURCE_ID"
37 // value: <int64 'next_available_resource_id'>
39 // key: "INITDATA_NEXT_VERSION_ID"
40 // value: <int64 'next_available_version_id'>
42 // key: "INITDATA_UNIQUE_ORIGIN:" + <GURL 'origin'>
45 // key: "PRES:" + <int64 'purgeable_resource_id'>
48 // key: "REG:" + <GURL 'origin'> + '\x00' + <int64 'registration_id'>
49 // (ex. "REG:http://example.com\x00123456")
50 // value: <ServiceWorkerRegistrationData serialized as a string>
52 // key: "RES:" + <int64 'version_id'> + '\x00' + <int64 'resource_id'>
53 // (ex. "RES:123456\x00654321")
54 // value: <ServiceWorkerResourceRecord serialized as a string>
56 // key: "URES:" + <int64 'uncommitted_resource_id'>
63 const char kDatabaseVersionKey[] = "INITDATA_DB_VERSION";
64 const char kNextRegIdKey[] = "INITDATA_NEXT_REGISTRATION_ID";
65 const char kNextResIdKey[] = "INITDATA_NEXT_RESOURCE_ID";
66 const char kNextVerIdKey[] = "INITDATA_NEXT_VERSION_ID";
67 const char kUniqueOriginKey[] = "INITDATA_UNIQUE_ORIGIN:";
69 const char kRegKeyPrefix[] = "REG:";
70 const char kResKeyPrefix[] = "RES:";
71 const char kKeySeparator = '\x00';
73 const char kUncommittedResIdKeyPrefix[] = "URES:";
74 const char kPurgeableResIdKeyPrefix[] = "PRES:";
76 const int64 kCurrentSchemaVersion = 1;
78 bool RemovePrefix(const std::string& str,
79 const std::string& prefix,
81 if (!StartsWithASCII(str, prefix, true))
84 *out = str.substr(prefix.size());
88 std::string CreateRegistrationKey(int64 registration_id,
90 return base::StringPrintf("%s%s%c%s",
92 origin.spec().c_str(),
94 base::Int64ToString(registration_id).c_str());
97 std::string CreateResourceRecordKeyPrefix(int64 version_id) {
98 return base::StringPrintf("%s%s%c",
100 base::Int64ToString(version_id).c_str(),
104 std::string CreateResourceRecordKey(int64 version_id,
106 return CreateResourceRecordKeyPrefix(version_id).append(
107 base::Int64ToString(resource_id));
110 std::string CreateUniqueOriginKey(const GURL& origin) {
111 return base::StringPrintf("%s%s", kUniqueOriginKey, origin.spec().c_str());
114 std::string CreateResourceIdKey(const char* key_prefix, int64 resource_id) {
115 return base::StringPrintf(
116 "%s%s", key_prefix, base::Int64ToString(resource_id).c_str());
119 void PutRegistrationDataToBatch(
120 const ServiceWorkerDatabase::RegistrationData& input,
121 leveldb::WriteBatch* batch) {
124 // Convert RegistrationData to ServiceWorkerRegistrationData.
125 ServiceWorkerRegistrationData data;
126 data.set_registration_id(input.registration_id);
127 data.set_scope_url(input.scope.spec());
128 data.set_script_url(input.script.spec());
129 data.set_version_id(input.version_id);
130 data.set_is_active(input.is_active);
131 data.set_has_fetch_handler(input.has_fetch_handler);
132 data.set_last_update_check_time(input.last_update_check.ToInternalValue());
135 bool success = data.SerializeToString(&value);
137 GURL origin = input.scope.GetOrigin();
138 batch->Put(CreateRegistrationKey(data.registration_id(), origin), value);
141 void PutResourceRecordToBatch(
142 const ServiceWorkerDatabase::ResourceRecord& input,
144 leveldb::WriteBatch* batch) {
147 // Convert ResourceRecord to ServiceWorkerResourceRecord.
148 ServiceWorkerResourceRecord record;
149 record.set_resource_id(input.resource_id);
150 record.set_url(input.url.spec());
153 bool success = record.SerializeToString(&value);
155 batch->Put(CreateResourceRecordKey(version_id, input.resource_id), value);
158 void PutUniqueOriginToBatch(const GURL& origin,
159 leveldb::WriteBatch* batch) {
160 // Value should be empty.
161 batch->Put(CreateUniqueOriginKey(origin), "");
164 void PutPurgeableResourceIdToBatch(int64 resource_id,
165 leveldb::WriteBatch* batch) {
166 // Value should be empty.
167 batch->Put(CreateResourceIdKey(kPurgeableResIdKeyPrefix, resource_id), "");
170 bool ParseRegistrationData(const std::string& serialized,
171 ServiceWorkerDatabase::RegistrationData* out) {
173 ServiceWorkerRegistrationData data;
174 if (!data.ParseFromString(serialized))
177 GURL scope_url(data.scope_url());
178 GURL script_url(data.script_url());
179 if (!scope_url.is_valid() ||
180 !script_url.is_valid() ||
181 scope_url.GetOrigin() != script_url.GetOrigin()) {
185 // Convert ServiceWorkerRegistrationData to RegistrationData.
186 out->registration_id = data.registration_id();
187 out->scope = scope_url;
188 out->script = script_url;
189 out->version_id = data.version_id();
190 out->is_active = data.is_active();
191 out->has_fetch_handler = data.has_fetch_handler();
192 out->last_update_check =
193 base::Time::FromInternalValue(data.last_update_check_time());
197 bool ParseResourceRecord(const std::string& serialized,
198 ServiceWorkerDatabase::ResourceRecord* out) {
200 ServiceWorkerResourceRecord record;
201 if (!record.ParseFromString(serialized))
204 GURL url(record.url());
208 // Convert ServiceWorkerResourceRecord to ResourceRecord.
209 out->resource_id = record.resource_id();
216 ServiceWorkerDatabase::RegistrationData::RegistrationData()
217 : registration_id(-1),
220 has_fetch_handler(false) {
223 ServiceWorkerDatabase::RegistrationData::~RegistrationData() {
226 ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath& path)
228 next_avail_registration_id_(0),
229 next_avail_resource_id_(0),
230 next_avail_version_id_(0),
232 was_corruption_detected_(false),
233 is_initialized_(false) {
234 sequence_checker_.DetachFromSequence();
237 ServiceWorkerDatabase::~ServiceWorkerDatabase() {
238 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
242 bool ServiceWorkerDatabase::GetNextAvailableIds(
243 int64* next_avail_registration_id,
244 int64* next_avail_version_id,
245 int64* next_avail_resource_id) {
246 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
247 DCHECK(next_avail_registration_id);
248 DCHECK(next_avail_version_id);
249 DCHECK(next_avail_resource_id);
251 if (!LazyOpen(false)) {
254 // Database has never been used.
255 *next_avail_registration_id = 0;
256 *next_avail_version_id = 0;
257 *next_avail_resource_id = 0;
261 if (!ReadNextAvailableId(kNextRegIdKey, &next_avail_registration_id_) ||
262 !ReadNextAvailableId(kNextVerIdKey, &next_avail_version_id_) ||
263 !ReadNextAvailableId(kNextResIdKey, &next_avail_resource_id_)) {
267 *next_avail_registration_id = next_avail_registration_id_;
268 *next_avail_version_id = next_avail_version_id_;
269 *next_avail_resource_id = next_avail_resource_id_;
273 bool ServiceWorkerDatabase::GetOriginsWithRegistrations(
274 std::set<GURL>* origins) {
275 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
278 if (!LazyOpen(false)) {
281 // Database has never been used.
286 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
287 for (itr->Seek(kUniqueOriginKey); itr->Valid(); itr->Next()) {
288 if (!itr->status().ok()) {
289 HandleError(FROM_HERE, itr->status());
295 if (!RemovePrefix(itr->key().ToString(), kUniqueOriginKey, &origin))
297 origins->insert(GURL(origin));
302 bool ServiceWorkerDatabase::GetRegistrationsForOrigin(
304 std::vector<RegistrationData>* registrations) {
305 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
306 DCHECK(registrations);
308 if (!LazyOpen(false)) {
311 // Database has never been used.
312 registrations->clear();
316 // Create a key prefix for registrations.
317 std::string prefix = base::StringPrintf(
318 "%s%s%c", kRegKeyPrefix, origin.spec().c_str(), kKeySeparator);
320 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
321 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
322 if (!itr->status().ok()) {
323 HandleError(FROM_HERE, itr->status());
324 registrations->clear();
328 if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
331 RegistrationData registration;
332 if (!ParseRegistrationData(itr->value().ToString(), ®istration)) {
333 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
334 registrations->clear();
337 registrations->push_back(registration);
342 bool ServiceWorkerDatabase::GetAllRegistrations(
343 std::vector<RegistrationData>* registrations) {
344 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
345 DCHECK(registrations);
347 if (!LazyOpen(false)) {
350 // Database has never been used.
351 registrations->clear();
355 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
356 for (itr->Seek(kRegKeyPrefix); itr->Valid(); itr->Next()) {
357 if (!itr->status().ok()) {
358 HandleError(FROM_HERE, itr->status());
359 registrations->clear();
363 if (!RemovePrefix(itr->key().ToString(), kRegKeyPrefix, NULL))
366 RegistrationData registration;
367 if (!ParseRegistrationData(itr->value().ToString(), ®istration)) {
368 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
369 registrations->clear();
372 registrations->push_back(registration);
377 bool ServiceWorkerDatabase::ReadRegistration(
378 int64 registration_id,
380 RegistrationData* registration,
381 std::vector<ResourceRecord>* resources) {
382 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
383 DCHECK(registration);
386 if (!LazyOpen(false) || is_disabled_)
389 RegistrationData value;
390 if (!ReadRegistrationData(registration_id, origin, &value))
393 if (!ReadResourceRecords(value.version_id, resources))
396 *registration = value;
400 bool ServiceWorkerDatabase::WriteRegistration(
401 const RegistrationData& registration,
402 const std::vector<ResourceRecord>& resources) {
403 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
404 if (!LazyOpen(true) || is_disabled_)
407 leveldb::WriteBatch batch;
408 BumpNextRegistrationIdIfNeeded(registration.registration_id, &batch);
409 BumpNextVersionIdIfNeeded(registration.version_id, &batch);
411 // TODO(nhiroki): Skip to add the origin into the unique origin list if it
412 // has already been added.
413 PutUniqueOriginToBatch(registration.scope.GetOrigin(), &batch);
415 PutRegistrationDataToBatch(registration, &batch);
417 // Retrieve a previous version to sweep purgeable resources.
418 RegistrationData old_registration;
419 if (!ReadRegistrationData(registration.registration_id,
420 registration.scope.GetOrigin(),
421 &old_registration)) {
426 DCHECK_LT(old_registration.version_id, registration.version_id);
427 // Currently resource sharing across versions and registrations is not
428 // suppported, so resource ids should not be overlapped between
429 // |registration| and |old_registration|.
430 // TODO(nhiroki): Add DCHECK to make sure the overlap does not exist.
431 if (!DeleteResourceRecords(old_registration.version_id, &batch))
435 // Used for avoiding multiple writes for the same resource id or url.
436 std::set<int64> pushed_resources;
437 std::set<GURL> pushed_urls;
438 for (std::vector<ResourceRecord>::const_iterator itr = resources.begin();
439 itr != resources.end(); ++itr) {
440 if (!itr->url.is_valid())
443 // Duplicated resource id or url should not exist.
444 DCHECK(pushed_resources.insert(itr->resource_id).second);
445 DCHECK(pushed_urls.insert(itr->url).second);
447 PutResourceRecordToBatch(*itr, registration.version_id, &batch);
449 // Delete a resource from the uncommitted list.
450 batch.Delete(CreateResourceIdKey(
451 kUncommittedResIdKeyPrefix, itr->resource_id));
454 return WriteBatch(&batch);
457 bool ServiceWorkerDatabase::UpdateVersionToActive(int64 registration_id,
458 const GURL& origin) {
459 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
460 if (!LazyOpen(false) || is_disabled_)
463 RegistrationData registration;
464 if (!ReadRegistrationData(registration_id, origin, ®istration))
467 registration.is_active = true;
469 leveldb::WriteBatch batch;
470 PutRegistrationDataToBatch(registration, &batch);
471 return WriteBatch(&batch);
474 bool ServiceWorkerDatabase::UpdateLastCheckTime(int64 registration_id,
476 const base::Time& time) {
477 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
478 if (!LazyOpen(false) || is_disabled_)
481 RegistrationData registration;
482 if (!ReadRegistrationData(registration_id, origin, ®istration))
485 registration.last_update_check = time;
487 leveldb::WriteBatch batch;
488 PutRegistrationDataToBatch(registration, &batch);
489 return WriteBatch(&batch);
492 bool ServiceWorkerDatabase::DeleteRegistration(int64 registration_id,
493 const GURL& origin) {
494 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
495 if (!LazyOpen(false) || is_disabled_)
498 leveldb::WriteBatch batch;
500 // Remove |origin| from unique origins if a registration specified by
501 // |registration_id| is the only one for |origin|.
502 // TODO(nhiroki): Check the uniqueness by more efficient way.
503 std::vector<RegistrationData> registrations;
504 if (!GetRegistrationsForOrigin(origin, ®istrations))
506 if (registrations.size() == 1 &&
507 registrations[0].registration_id == registration_id) {
508 batch.Delete(CreateUniqueOriginKey(origin));
511 // Delete a registration specified by |registration_id|.
512 batch.Delete(CreateRegistrationKey(registration_id, origin));
514 // Delete resource records associated with the registration.
515 for (std::vector<RegistrationData>::const_iterator itr =
516 registrations.begin(); itr != registrations.end(); ++itr) {
517 if (itr->registration_id == registration_id) {
518 if (!DeleteResourceRecords(itr->version_id, &batch))
524 return WriteBatch(&batch);
527 bool ServiceWorkerDatabase::GetUncommittedResourceIds(std::set<int64>* ids) {
528 return ReadResourceIds(kUncommittedResIdKeyPrefix, ids);
531 bool ServiceWorkerDatabase::WriteUncommittedResourceIds(
532 const std::set<int64>& ids) {
533 return WriteResourceIds(kUncommittedResIdKeyPrefix, ids);
536 bool ServiceWorkerDatabase::ClearUncommittedResourceIds(
537 const std::set<int64>& ids) {
538 return DeleteResourceIds(kUncommittedResIdKeyPrefix, ids);
541 bool ServiceWorkerDatabase::GetPurgeableResourceIds(std::set<int64>* ids) {
542 return ReadResourceIds(kPurgeableResIdKeyPrefix, ids);
545 bool ServiceWorkerDatabase::WritePurgeableResourceIds(
546 const std::set<int64>& ids) {
547 return WriteResourceIds(kPurgeableResIdKeyPrefix, ids);
550 bool ServiceWorkerDatabase::ClearPurgeableResourceIds(
551 const std::set<int64>& ids) {
552 return DeleteResourceIds(kPurgeableResIdKeyPrefix, ids);
555 bool ServiceWorkerDatabase::DeleteAllDataForOrigin(const GURL& origin) {
556 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
557 if (!LazyOpen(true) || is_disabled_ || !origin.is_valid())
560 leveldb::WriteBatch batch;
562 // Delete from the unique origin list.
563 batch.Delete(CreateUniqueOriginKey(origin));
565 std::vector<RegistrationData> registrations;
566 if (!GetRegistrationsForOrigin(origin, ®istrations))
569 // Delete registrations and resource records.
570 for (std::vector<RegistrationData>::const_iterator itr =
571 registrations.begin(); itr != registrations.end(); ++itr) {
572 batch.Delete(CreateRegistrationKey(itr->registration_id, origin));
573 if (!DeleteResourceRecords(itr->version_id, &batch))
577 return WriteBatch(&batch);
580 bool ServiceWorkerDatabase::LazyOpen(bool create_if_needed) {
581 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
585 // Do not try to open a database if we tried and failed once.
589 // When |path_| is empty, open a database in-memory.
590 bool use_in_memory_db = path_.empty();
592 if (!create_if_needed) {
593 // Avoid opening a database if it does not exist at the |path_|.
594 if (use_in_memory_db ||
595 !base::PathExists(path_) ||
596 base::IsDirectoryEmpty(path_)) {
601 leveldb::Options options;
602 options.create_if_missing = create_if_needed;
603 if (use_in_memory_db) {
604 env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
605 options.env = env_.get();
608 leveldb::DB* db = NULL;
609 leveldb::Status status =
610 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db);
613 // TODO(nhiroki): Should we retry to open the database?
614 HandleError(FROM_HERE, status);
620 if (!ReadDatabaseVersion(&db_version))
623 is_initialized_ = true;
627 bool ServiceWorkerDatabase::ReadNextAvailableId(
628 const char* id_key, int64* next_avail_id) {
630 DCHECK(next_avail_id);
633 leveldb::Status status = db_->Get(leveldb::ReadOptions(), id_key, &value);
634 if (status.IsNotFound()) {
635 // Nobody has gotten the next resource id for |id_key|.
641 HandleError(FROM_HERE, status);
646 if (!base::StringToInt64(value, &parsed)) {
647 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
651 *next_avail_id = parsed;
655 bool ServiceWorkerDatabase::ReadRegistrationData(
656 int64 registration_id,
658 RegistrationData* registration) {
659 DCHECK(registration);
661 std::string key = CreateRegistrationKey(registration_id, origin);
664 leveldb::Status status = db_->Get(leveldb::ReadOptions(), key, &value);
666 if (!status.IsNotFound())
667 HandleError(FROM_HERE, status);
671 RegistrationData parsed;
672 if (!ParseRegistrationData(value, &parsed)) {
673 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
677 *registration = parsed;
681 bool ServiceWorkerDatabase::ReadResourceRecords(
683 std::vector<ResourceRecord>* resources) {
686 std::string prefix = CreateResourceRecordKeyPrefix(version_id);
687 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
688 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
689 if (!itr->status().ok()) {
690 HandleError(FROM_HERE, itr->status());
695 if (!RemovePrefix(itr->key().ToString(), prefix, NULL))
698 ResourceRecord resource;
699 if (!ParseResourceRecord(itr->value().ToString(), &resource)) {
700 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
704 resources->push_back(resource);
709 bool ServiceWorkerDatabase::DeleteResourceRecords(
711 leveldb::WriteBatch* batch) {
714 std::string prefix = CreateResourceRecordKeyPrefix(version_id);
715 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
716 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
717 if (!itr->status().ok()) {
718 HandleError(FROM_HERE, itr->status());
722 std::string key = itr->key().ToString();
723 std::string unprefixed;
724 if (!RemovePrefix(key, prefix, &unprefixed))
728 if (!base::StringToInt64(unprefixed, &resource_id)) {
729 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
733 // Remove a resource record.
736 // Currently resource sharing across versions and registrations is not
737 // supported, so we can purge this without caring about it.
738 PutPurgeableResourceIdToBatch(resource_id, batch);
743 bool ServiceWorkerDatabase::ReadResourceIds(const char* id_key_prefix,
744 std::set<int64>* ids) {
745 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
746 DCHECK(id_key_prefix);
749 if (!LazyOpen(false) || is_disabled_)
752 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
753 for (itr->Seek(id_key_prefix); itr->Valid(); itr->Next()) {
754 if (!itr->status().ok()) {
755 HandleError(FROM_HERE, itr->status());
760 std::string unprefixed;
761 if (!RemovePrefix(itr->key().ToString(), id_key_prefix, &unprefixed))
765 if (!base::StringToInt64(unprefixed, &resource_id)) {
766 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
770 ids->insert(resource_id);
775 bool ServiceWorkerDatabase::WriteResourceIds(const char* id_key_prefix,
776 const std::set<int64>& ids) {
777 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
778 DCHECK(id_key_prefix);
780 if (!LazyOpen(true) || is_disabled_)
785 leveldb::WriteBatch batch;
786 for (std::set<int64>::const_iterator itr = ids.begin();
787 itr != ids.end(); ++itr) {
788 // Value should be empty.
789 batch.Put(CreateResourceIdKey(id_key_prefix, *itr), "");
791 return WriteBatch(&batch);
794 bool ServiceWorkerDatabase::DeleteResourceIds(const char* id_key_prefix,
795 const std::set<int64>& ids) {
796 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
797 DCHECK(id_key_prefix);
799 if (!LazyOpen(true) || is_disabled_)
804 leveldb::WriteBatch batch;
805 for (std::set<int64>::const_iterator itr = ids.begin();
806 itr != ids.end(); ++itr) {
807 batch.Delete(CreateResourceIdKey(id_key_prefix, *itr));
809 return WriteBatch(&batch);
812 bool ServiceWorkerDatabase::ReadDatabaseVersion(int64* db_version) {
814 leveldb::Status status =
815 db_->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value);
816 if (status.IsNotFound()) {
817 // The database hasn't been initialized yet.
822 HandleError(FROM_HERE, status);
827 if (!base::StringToInt64(value, &parsed)) {
828 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
832 const int kFirstValidVersion = 1;
833 if (parsed < kFirstValidVersion || kCurrentSchemaVersion < parsed) {
834 HandleError(FROM_HERE, leveldb::Status::Corruption("invalid DB version"));
838 *db_version = parsed;
842 bool ServiceWorkerDatabase::WriteBatch(leveldb::WriteBatch* batch) {
844 DCHECK(!is_disabled_);
846 if (!is_initialized_) {
847 // Write the database schema version.
848 batch->Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion));
849 is_initialized_ = true;
852 leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch);
854 HandleError(FROM_HERE, status);
860 void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded(
861 int64 used_id, leveldb::WriteBatch* batch) {
863 if (next_avail_registration_id_ <= used_id) {
864 next_avail_registration_id_ = used_id + 1;
865 batch->Put(kNextRegIdKey, base::Int64ToString(next_avail_registration_id_));
869 void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded(
870 int64 used_id, leveldb::WriteBatch* batch) {
872 if (next_avail_version_id_ <= used_id) {
873 next_avail_version_id_ = used_id + 1;
874 batch->Put(kNextVerIdKey, base::Int64ToString(next_avail_version_id_));
878 bool ServiceWorkerDatabase::IsOpen() {
879 return db_.get() != NULL;
882 void ServiceWorkerDatabase::HandleError(
883 const tracked_objects::Location& from_here,
884 const leveldb::Status& status) {
885 // TODO(nhiroki): Add an UMA histogram.
886 DLOG(ERROR) << "Failed at: " << from_here.ToString()
887 << " with error: " << status.ToString();
889 if (status.IsCorruption())
890 was_corruption_detected_ = true;
894 } // namespace content