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) {
236 ServiceWorkerDatabase::~ServiceWorkerDatabase() {
237 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
241 bool ServiceWorkerDatabase::GetNextAvailableIds(
242 int64
* next_avail_registration_id
,
243 int64
* next_avail_version_id
,
244 int64
* next_avail_resource_id
) {
245 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
246 DCHECK(next_avail_registration_id
);
247 DCHECK(next_avail_version_id
);
248 DCHECK(next_avail_resource_id
);
250 if (!LazyOpen(false) || is_disabled_
)
253 if (!ReadNextAvailableId(kNextRegIdKey
, &next_avail_registration_id_
) ||
254 !ReadNextAvailableId(kNextVerIdKey
, &next_avail_version_id_
) ||
255 !ReadNextAvailableId(kNextResIdKey
, &next_avail_resource_id_
)) {
259 *next_avail_registration_id
= next_avail_registration_id_
;
260 *next_avail_version_id
= next_avail_version_id_
;
261 *next_avail_resource_id
= next_avail_resource_id_
;
265 bool ServiceWorkerDatabase::GetOriginsWithRegistrations(
266 std::set
<GURL
>* origins
) {
267 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
270 if (!LazyOpen(false) || is_disabled_
)
273 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
274 for (itr
->Seek(kUniqueOriginKey
); itr
->Valid(); itr
->Next()) {
275 if (!itr
->status().ok()) {
276 HandleError(FROM_HERE
, itr
->status());
282 if (!RemovePrefix(itr
->key().ToString(), kUniqueOriginKey
, &origin
))
284 origins
->insert(GURL(origin
));
289 bool ServiceWorkerDatabase::GetRegistrationsForOrigin(
291 std::vector
<RegistrationData
>* registrations
) {
292 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
293 DCHECK(registrations
);
295 if (!LazyOpen(false) || is_disabled_
)
298 // Create a key prefix for registrations.
299 std::string prefix
= base::StringPrintf(
300 "%s%s%c", kRegKeyPrefix
, origin
.spec().c_str(), kKeySeparator
);
302 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
303 for (itr
->Seek(prefix
); itr
->Valid(); itr
->Next()) {
304 if (!itr
->status().ok()) {
305 HandleError(FROM_HERE
, itr
->status());
306 registrations
->clear();
310 if (!RemovePrefix(itr
->key().ToString(), prefix
, NULL
))
313 RegistrationData registration
;
314 if (!ParseRegistrationData(itr
->value().ToString(), ®istration
)) {
315 HandleError(FROM_HERE
, leveldb::Status::Corruption("failed to parse"));
316 registrations
->clear();
319 registrations
->push_back(registration
);
324 bool ServiceWorkerDatabase::ReadRegistration(
325 int64 registration_id
,
327 RegistrationData
* registration
,
328 std::vector
<ResourceRecord
>* resources
) {
329 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
330 DCHECK(registration
);
333 if (!LazyOpen(false) || is_disabled_
)
336 RegistrationData value
;
337 if (!ReadRegistrationData(registration_id
, origin
, &value
))
340 if (!ReadResourceRecords(value
.version_id
, resources
))
343 *registration
= value
;
347 bool ServiceWorkerDatabase::WriteRegistration(
348 const RegistrationData
& registration
,
349 const std::vector
<ResourceRecord
>& resources
) {
350 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
351 if (!LazyOpen(true) || is_disabled_
)
354 leveldb::WriteBatch batch
;
355 BumpNextRegistrationIdIfNeeded(registration
.registration_id
, &batch
);
356 BumpNextVersionIdIfNeeded(registration
.version_id
, &batch
);
358 // TODO(nhiroki): Skip to add the origin into the unique origin list if it
359 // has already been added.
360 PutUniqueOriginToBatch(registration
.scope
.GetOrigin(), &batch
);
362 PutRegistrationDataToBatch(registration
, &batch
);
364 // Retrieve a previous version to sweep purgeable resources.
365 RegistrationData old_registration
;
366 if (!ReadRegistrationData(registration
.registration_id
,
367 registration
.scope
.GetOrigin(),
368 &old_registration
)) {
373 DCHECK_LT(old_registration
.version_id
, registration
.version_id
);
374 // Currently resource sharing across versions and registrations is not
375 // suppported, so resource ids should not be overlapped between
376 // |registration| and |old_registration|.
377 // TODO(nhiroki): Add DCHECK to make sure the overlap does not exist.
378 if (!DeleteResourceRecords(old_registration
.version_id
, &batch
))
382 // Used for avoiding multiple writes for the same resource id or url.
383 std::set
<int64
> pushed_resources
;
384 std::set
<GURL
> pushed_urls
;
385 for (std::vector
<ResourceRecord
>::const_iterator itr
= resources
.begin();
386 itr
!= resources
.end(); ++itr
) {
387 if (!itr
->url
.is_valid())
390 // Duplicated resource id or url should not exist.
391 DCHECK(pushed_resources
.insert(itr
->resource_id
).second
);
392 DCHECK(pushed_urls
.insert(itr
->url
).second
);
394 PutResourceRecordToBatch(*itr
, registration
.version_id
, &batch
);
396 // Delete a resource from the uncommitted list.
397 batch
.Delete(CreateResourceIdKey(
398 kUncommittedResIdKeyPrefix
, itr
->resource_id
));
401 return WriteBatch(&batch
);
404 bool ServiceWorkerDatabase::UpdateVersionToActive(int64 registration_id
,
405 const GURL
& origin
) {
406 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
407 if (!LazyOpen(false) || is_disabled_
)
410 RegistrationData registration
;
411 if (!ReadRegistrationData(registration_id
, origin
, ®istration
))
414 registration
.is_active
= true;
416 leveldb::WriteBatch batch
;
417 PutRegistrationDataToBatch(registration
, &batch
);
418 return WriteBatch(&batch
);
421 bool ServiceWorkerDatabase::UpdateLastCheckTime(int64 registration_id
,
423 const base::Time
& time
) {
424 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
425 if (!LazyOpen(false) || is_disabled_
)
428 RegistrationData registration
;
429 if (!ReadRegistrationData(registration_id
, origin
, ®istration
))
432 registration
.last_update_check
= time
;
434 leveldb::WriteBatch batch
;
435 PutRegistrationDataToBatch(registration
, &batch
);
436 return WriteBatch(&batch
);
439 bool ServiceWorkerDatabase::DeleteRegistration(int64 registration_id
,
440 const GURL
& origin
) {
441 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
442 if (!LazyOpen(false) || is_disabled_
)
445 leveldb::WriteBatch batch
;
447 // Remove |origin| from unique origins if a registration specified by
448 // |registration_id| is the only one for |origin|.
449 // TODO(nhiroki): Check the uniqueness by more efficient way.
450 std::vector
<RegistrationData
> registrations
;
451 if (!GetRegistrationsForOrigin(origin
, ®istrations
))
453 if (registrations
.size() == 1 &&
454 registrations
[0].registration_id
== registration_id
) {
455 batch
.Delete(CreateUniqueOriginKey(origin
));
458 // Delete a registration specified by |registration_id|.
459 batch
.Delete(CreateRegistrationKey(registration_id
, origin
));
461 // Delete resource records associated with the registration.
462 for (std::vector
<RegistrationData
>::const_iterator itr
=
463 registrations
.begin(); itr
!= registrations
.end(); ++itr
) {
464 if (itr
->registration_id
== registration_id
) {
465 if (!DeleteResourceRecords(itr
->version_id
, &batch
))
471 return WriteBatch(&batch
);
474 bool ServiceWorkerDatabase::GetUncommittedResourceIds(std::set
<int64
>* ids
) {
475 return ReadResourceIds(kUncommittedResIdKeyPrefix
, ids
);
478 bool ServiceWorkerDatabase::WriteUncommittedResourceIds(
479 const std::set
<int64
>& ids
) {
480 return WriteResourceIds(kUncommittedResIdKeyPrefix
, ids
);
483 bool ServiceWorkerDatabase::ClearUncommittedResourceIds(
484 const std::set
<int64
>& ids
) {
485 return DeleteResourceIds(kUncommittedResIdKeyPrefix
, ids
);
488 bool ServiceWorkerDatabase::GetPurgeableResourceIds(std::set
<int64
>* ids
) {
489 return ReadResourceIds(kPurgeableResIdKeyPrefix
, ids
);
492 bool ServiceWorkerDatabase::WritePurgeableResourceIds(
493 const std::set
<int64
>& ids
) {
494 return WriteResourceIds(kPurgeableResIdKeyPrefix
, ids
);
497 bool ServiceWorkerDatabase::ClearPurgeableResourceIds(
498 const std::set
<int64
>& ids
) {
499 return DeleteResourceIds(kPurgeableResIdKeyPrefix
, ids
);
502 bool ServiceWorkerDatabase::LazyOpen(bool create_if_needed
) {
503 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
507 // Do not try to open a database if we tried and failed once.
511 // When |path_| is empty, open a database in-memory.
512 bool use_in_memory_db
= path_
.empty();
514 if (!create_if_needed
) {
515 // Avoid opening a database if it does not exist at the |path_|.
516 if (use_in_memory_db
||
517 !base::PathExists(path_
) ||
518 base::IsDirectoryEmpty(path_
)) {
523 leveldb::Options options
;
524 options
.create_if_missing
= create_if_needed
;
525 if (use_in_memory_db
) {
526 env_
.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
527 options
.env
= env_
.get();
530 leveldb::DB
* db
= NULL
;
531 leveldb::Status status
=
532 leveldb::DB::Open(options
, path_
.AsUTF8Unsafe(), &db
);
535 // TODO(nhiroki): Should we retry to open the database?
536 HandleError(FROM_HERE
, status
);
542 if (!ReadDatabaseVersion(&db_version
))
545 is_initialized_
= true;
549 bool ServiceWorkerDatabase::ReadNextAvailableId(
550 const char* id_key
, int64
* next_avail_id
) {
552 DCHECK(next_avail_id
);
555 leveldb::Status status
= db_
->Get(leveldb::ReadOptions(), id_key
, &value
);
556 if (status
.IsNotFound()) {
557 // Nobody has gotten the next resource id for |id_key|.
563 HandleError(FROM_HERE
, status
);
568 if (!base::StringToInt64(value
, &parsed
)) {
569 HandleError(FROM_HERE
, leveldb::Status::Corruption("failed to parse"));
573 *next_avail_id
= parsed
;
577 bool ServiceWorkerDatabase::ReadRegistrationData(
578 int64 registration_id
,
580 RegistrationData
* registration
) {
581 DCHECK(registration
);
583 std::string key
= CreateRegistrationKey(registration_id
, origin
);
586 leveldb::Status status
= db_
->Get(leveldb::ReadOptions(), key
, &value
);
588 if (!status
.IsNotFound())
589 HandleError(FROM_HERE
, status
);
593 RegistrationData parsed
;
594 if (!ParseRegistrationData(value
, &parsed
)) {
595 HandleError(FROM_HERE
, leveldb::Status::Corruption("failed to parse"));
599 *registration
= parsed
;
603 bool ServiceWorkerDatabase::ReadResourceRecords(
605 std::vector
<ResourceRecord
>* resources
) {
608 std::string prefix
= CreateResourceRecordKeyPrefix(version_id
);
609 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
610 for (itr
->Seek(prefix
); itr
->Valid(); itr
->Next()) {
611 if (!itr
->status().ok()) {
612 HandleError(FROM_HERE
, itr
->status());
617 if (!RemovePrefix(itr
->key().ToString(), prefix
, NULL
))
620 ResourceRecord resource
;
621 if (!ParseResourceRecord(itr
->value().ToString(), &resource
)) {
622 HandleError(FROM_HERE
, leveldb::Status::Corruption("failed to parse"));
626 resources
->push_back(resource
);
631 bool ServiceWorkerDatabase::DeleteResourceRecords(
633 leveldb::WriteBatch
* batch
) {
636 std::string prefix
= CreateResourceRecordKeyPrefix(version_id
);
637 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
638 for (itr
->Seek(prefix
); itr
->Valid(); itr
->Next()) {
639 if (!itr
->status().ok()) {
640 HandleError(FROM_HERE
, itr
->status());
644 std::string key
= itr
->key().ToString();
645 std::string unprefixed
;
646 if (!RemovePrefix(key
, prefix
, &unprefixed
))
650 if (!base::StringToInt64(unprefixed
, &resource_id
)) {
651 HandleError(FROM_HERE
, leveldb::Status::Corruption("failed to parse"));
655 // Remove a resource record.
658 // Currently resource sharing across versions and registrations is not
659 // supported, so we can purge this without caring about it.
660 PutPurgeableResourceIdToBatch(resource_id
, batch
);
665 bool ServiceWorkerDatabase::ReadResourceIds(const char* id_key_prefix
,
666 std::set
<int64
>* ids
) {
667 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
668 DCHECK(id_key_prefix
);
671 if (!LazyOpen(false) || is_disabled_
)
674 scoped_ptr
<leveldb::Iterator
> itr(db_
->NewIterator(leveldb::ReadOptions()));
675 for (itr
->Seek(id_key_prefix
); itr
->Valid(); itr
->Next()) {
676 if (!itr
->status().ok()) {
677 HandleError(FROM_HERE
, itr
->status());
682 std::string unprefixed
;
683 if (!RemovePrefix(itr
->key().ToString(), id_key_prefix
, &unprefixed
))
687 if (!base::StringToInt64(unprefixed
, &resource_id
)) {
688 HandleError(FROM_HERE
, leveldb::Status::Corruption("failed to parse"));
692 ids
->insert(resource_id
);
697 bool ServiceWorkerDatabase::WriteResourceIds(const char* id_key_prefix
,
698 const std::set
<int64
>& ids
) {
699 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
700 DCHECK(id_key_prefix
);
702 if (!LazyOpen(true) || is_disabled_
)
707 leveldb::WriteBatch batch
;
708 for (std::set
<int64
>::const_iterator itr
= ids
.begin();
709 itr
!= ids
.end(); ++itr
) {
710 // Value should be empty.
711 batch
.Put(CreateResourceIdKey(id_key_prefix
, *itr
), "");
713 return WriteBatch(&batch
);
716 bool ServiceWorkerDatabase::DeleteResourceIds(const char* id_key_prefix
,
717 const std::set
<int64
>& ids
) {
718 DCHECK(sequence_checker_
.CalledOnValidSequencedThread());
719 DCHECK(id_key_prefix
);
721 if (!LazyOpen(true) || is_disabled_
)
726 leveldb::WriteBatch batch
;
727 for (std::set
<int64
>::const_iterator itr
= ids
.begin();
728 itr
!= ids
.end(); ++itr
) {
729 batch
.Delete(CreateResourceIdKey(id_key_prefix
, *itr
));
731 return WriteBatch(&batch
);
734 bool ServiceWorkerDatabase::ReadDatabaseVersion(int64
* db_version
) {
736 leveldb::Status status
=
737 db_
->Get(leveldb::ReadOptions(), kDatabaseVersionKey
, &value
);
738 if (status
.IsNotFound()) {
739 // The database hasn't been initialized yet.
744 HandleError(FROM_HERE
, status
);
749 if (!base::StringToInt64(value
, &parsed
)) {
750 HandleError(FROM_HERE
, leveldb::Status::Corruption("failed to parse"));
754 const int kFirstValidVersion
= 1;
755 if (parsed
< kFirstValidVersion
|| kCurrentSchemaVersion
< parsed
) {
756 HandleError(FROM_HERE
, leveldb::Status::Corruption("invalid DB version"));
760 *db_version
= parsed
;
764 bool ServiceWorkerDatabase::WriteBatch(leveldb::WriteBatch
* batch
) {
766 DCHECK(!is_disabled_
);
768 if (!is_initialized_
) {
769 // Write the database schema version.
770 batch
->Put(kDatabaseVersionKey
, base::Int64ToString(kCurrentSchemaVersion
));
771 is_initialized_
= true;
774 leveldb::Status status
= db_
->Write(leveldb::WriteOptions(), batch
);
776 HandleError(FROM_HERE
, status
);
782 void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded(
783 int64 used_id
, leveldb::WriteBatch
* batch
) {
785 if (next_avail_registration_id_
<= used_id
) {
786 next_avail_registration_id_
= used_id
+ 1;
787 batch
->Put(kNextRegIdKey
, base::Int64ToString(next_avail_registration_id_
));
791 void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded(
792 int64 used_id
, leveldb::WriteBatch
* batch
) {
794 if (next_avail_version_id_
<= used_id
) {
795 next_avail_version_id_
= used_id
+ 1;
796 batch
->Put(kNextVerIdKey
, base::Int64ToString(next_avail_version_id_
));
800 bool ServiceWorkerDatabase::IsOpen() {
801 return db_
.get() != NULL
;
804 void ServiceWorkerDatabase::HandleError(
805 const tracked_objects::Location
& from_here
,
806 const leveldb::Status
& status
) {
807 // TODO(nhiroki): Add an UMA histogram.
808 DLOG(ERROR
) << "Failed at: " << from_here
.ToString()
809 << " with error: " << status
.ToString();
811 if (status
.IsCorruption())
812 was_corruption_detected_
= true;
816 } // namespace content