Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(175)

Side by Side Diff: content/browser/service_worker/service_worker_database.cc

Issue 248803003: ServiceWorker: Store registration data in ServiceWorkerDatabase (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/service_worker/service_worker_database.h" 5 #include "content/browser/service_worker/service_worker_database.h"
6 6
7 #include <string> 7 #include <string>
8 8
9 #include "base/file_util.h" 9 #include "base/file_util.h"
10 #include "base/location.h"
10 #include "base/logging.h" 11 #include "base/logging.h"
11 #include "base/strings/string_number_conversions.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 "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.
12 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h" 16 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
13 #include "third_party/leveldatabase/src/include/leveldb/db.h" 17 #include "third_party/leveldatabase/src/include/leveldb/db.h"
14 #include "third_party/leveldatabase/src/include/leveldb/env.h" 18 #include "third_party/leveldatabase/src/include/leveldb/env.h"
15 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" 19 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
16 20
17 // LevelDB database schema 21 // LevelDB database schema
18 // ======================= 22 // =======================
19 // 23 //
24 // NOTE
25 // - int64 value is serialized as a string by base::Int64ToString().
26 //
20 // Version 1 (in sorted order) 27 // Version 1 (in sorted order)
21 // key: "DB_VERSION" 28 // key: "DB_VERSION"
22 // value: <int64 serialized as a string> 29 // value: "1"
23 // 30 //
24 // key: "NEXT_REGISTRATION_ID" 31 // key: "NEXT_REGISTRATION_ID"
25 // value: <int64 serialized as a string> 32 // value: <int64 'next_available_registration_id'>
26 // 33 //
27 // key: "NEXT_RESOURCE_ID" 34 // key: "NEXT_RESOURCE_ID"
28 // value: <int64 serialized as a string> 35 // value: <int64 'next_available_resource_id'>
29 // 36 //
30 // key: "NEXT_VERSION_ID" 37 // key: "NEXT_VERSION_ID"
31 // value: <int64 serialized as a string> 38 // value: <int64 'next_available_version_id'>
39 //
40 // key: "PRES:" + <int64 'purgeable_resource_id'>
41 // value: <empty>
42 //
43 // key: "REG:" + (1) + '\x00' + (2)
44 // (1) <GURL 'origin_url' serialized by GURL::spec()>
45 // (2) <int64 'registration_id'>
46 // (ex. "REG:http://example.com\x00123456")
47 // value: <ServiceWorkerRegistrationData serialized as a string>
48 //
49 // key: "RES:" + <int64 'version_id'> + '\x00' + <int64 'resource_id'>
50 // (ex. "RES:123456\x00654321")
51 // value: <ServiceWorkerResourceRecord serialized as a string>
52 //
53 // key: "URES:" + <int64 'uncommitted_resource_id'>
54 // value: <empty>
32 55
33 namespace content { 56 namespace content {
34 57
35 namespace { 58 namespace {
36 59
37 const char kDatabaseVersionKey[] = "DB_VERSION"; 60 const char kDatabaseVersionKey[] = "DB_VERSION";
38 const char kNextRegIdKey[] = "NEXT_REGISTRATION_ID"; 61 const char kNextRegIdKey[] = "NEXT_REGISTRATION_ID";
39 const char kNextResIdKey[] = "NEXT_RESOURCE_ID"; 62 const char kNextResIdKey[] = "NEXT_RESOURCE_ID";
40 const char kNextVerIdKey[] = "NEXT_VERSION_ID"; 63 const char kNextVerIdKey[] = "NEXT_VERSION_ID";
41 64
65 const char kRegKeyPrefix[] = "REG:";
66 const char kResKeyPrefix[] = "RES:";
67 const char kUncommittedResKeyPrefix[] = "URES:";
68 const char kPurgeableResKeyPrefix[] = "PRES:";
69 const char kKeySeparator = '\x00';
70
42 const int64 kCurrentSchemaVersion = 1; 71 const int64 kCurrentSchemaVersion = 1;
43 72
44 } // namespace 73 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.
45 74 const std::string& prefix) {
46 ServiceWorkerDatabase::RegistrationData::RegistrationData() 75 if (StartsWithASCII(str, prefix, true))
47 : registration_id(-1), 76 return str.substr(prefix.size());
48 version_id(-1), 77 return str;
49 is_active(false),
50 has_fetch_handler(false) {
51 } 78 }
52 79
53 ServiceWorkerDatabase::RegistrationData::~RegistrationData() { 80 std::string CreateRegistrationKey(const ServiceWorkerRegistrationData& data) {
81 GURL origin = GURL(data.scope_url()).GetOrigin();
82 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.
83 out << kRegKeyPrefix << origin.spec() << kKeySeparator
84 << base::Int64ToString(data.registration_id());
85 return out.str();
54 } 86 }
55 87
88 std::string CreateResourceKey(
89 const ServiceWorkerResourceRecord& resource,
90 int64 version_id) {
91 std::ostringstream out;
92 out << kResKeyPrefix << base::Int64ToString(version_id)
93 << kKeySeparator << base::Int64ToString(resource.resource_id());
94 return out.str();
95 }
96
97 std::string CreateUncommittedResourceKey(int64 resource_id) {
98 std::ostringstream out;
99 out << kUncommittedResKeyPrefix << base::Int64ToString(resource_id);
100 return out.str();
101 }
102
103 std::string CreatePurgeableResourceKey(int64 resource_id) {
104 std::ostringstream out;
105 out << kPurgeableResKeyPrefix << base::Int64ToString(resource_id);
106 return out.str();
107 }
108
109 void PutRegistrationDataToBatch(const ServiceWorkerRegistrationData& data,
110 leveldb::WriteBatch* batch) {
111 DCHECK(batch);
112 std::string value;
113 bool success = data.SerializeToString(&value);
114 DCHECK(success);
115 batch->Put(CreateRegistrationKey(data), value);
116 }
117
118 void PutResourceRecordToBatch(const ServiceWorkerResourceRecord& resource,
119 int64 version_id,
120 leveldb::WriteBatch* batch) {
121 DCHECK(batch);
122 std::string value;
123 bool success = resource.SerializeToString(&value);
124 DCHECK(success);
125 batch->Put(CreateResourceKey(resource, version_id), value);
126 }
127
128 bool ParseRegistrationKey(const std::string& key,
129 GURL* origin_out,
130 int64* registration_id_out) {
131 std::string unprefixed = RemovePrefix(key, kRegKeyPrefix);
132 std::vector<std::string> tokens;
133 base::SplitString(unprefixed, kKeySeparator, &tokens);
134 if (tokens.size() != 2)
135 return false;
136
137 GURL origin(tokens[0]);
138 if (!origin.is_valid())
139 return false;
140
141 int64 registration_id;
142 if (!base::StringToInt64(tokens[1], &registration_id))
143 return false;
144
145 if (origin_out)
146 *origin_out = origin;
147 if (registration_id_out)
148 *registration_id_out = registration_id;
149 return true;
150 }
151
152 bool ParseResourceKey(const std::string& key,
153 int64* version_id_out,
154 int64* resource_id_out) {
155 std::string unprefixed = RemovePrefix(key, kResKeyPrefix);
156 std::vector<std::string> tokens;
157 base::SplitString(unprefixed, kKeySeparator, &tokens);
158 if (tokens.size() != 2)
159 return false;
160
161 int64 version_id;
162 if (!base::StringToInt64(tokens[0], &version_id))
163 return false;
164
165 int64 resource_id;
166 if (!base::StringToInt64(tokens[1], &resource_id))
167 return false;
168
169 if (version_id_out)
170 *version_id_out = version_id;
171 if (resource_id_out)
172 *resource_id_out = resource_id;
173 return true;
174 }
175
176 } // namespace
177
56 ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath& path) 178 ServiceWorkerDatabase::ServiceWorkerDatabase(const base::FilePath& path)
57 : path_(path), 179 : path_(path),
58 is_disabled_(false), 180 is_disabled_(false),
59 was_corruption_detected_(false) { 181 was_corruption_detected_(false) {
60 } 182 }
61 183
62 ServiceWorkerDatabase::~ServiceWorkerDatabase() { 184 ServiceWorkerDatabase::~ServiceWorkerDatabase() {
63 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 185 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
64 db_.reset(); 186 db_.reset();
65 } 187 }
66 188
67 bool ServiceWorkerDatabase::GetNextAvailableIds( 189 bool ServiceWorkerDatabase::GetNextAvailableIds(
68 int64* next_avail_registration_id, 190 int64* next_avail_registration_id,
69 int64* next_avail_version_id, 191 int64* next_avail_version_id,
70 int64* next_avail_resource_id) { 192 int64* next_avail_resource_id) {
71 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 193 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
72 DCHECK(next_avail_registration_id); 194 DCHECK(next_avail_registration_id);
73 DCHECK(next_avail_version_id); 195 DCHECK(next_avail_version_id);
74 DCHECK(next_avail_resource_id); 196 DCHECK(next_avail_resource_id);
75 197
76 if (!LazyOpen(false) || is_disabled_) 198 if (!LazyOpen(false) || is_disabled_)
77 return false; 199 return false;
78 200
79 int64 reg_id = -1; 201 int64 reg_id = -1;
80 int64 ver_id = -1; 202 int64 ver_id = -1;
81 int64 res_id = -1; 203 int64 res_id = -1;
82 204
83 if (!ReadInt64(kNextRegIdKey, &reg_id) || 205 if (!ReadNextAvailableId(kNextRegIdKey, &reg_id) ||
84 !ReadInt64(kNextVerIdKey, &ver_id) || 206 !ReadNextAvailableId(kNextVerIdKey, &ver_id) ||
85 !ReadInt64(kNextResIdKey, &res_id)) 207 !ReadNextAvailableId(kNextResIdKey, &res_id))
86 return false; 208 return false;
87 209
88 *next_avail_registration_id = reg_id; 210 *next_avail_registration_id = reg_id;
89 *next_avail_version_id = ver_id; 211 *next_avail_version_id = ver_id;
90 *next_avail_resource_id = res_id; 212 *next_avail_resource_id = res_id;
91 return true; 213 return true;
92 } 214 }
93 215
216 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.
217 std::set<GURL>* origins) {
218 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
219 DCHECK(origins);
220
221 if (!LazyOpen(false) || is_disabled_)
222 return false;
223
224 GURL origin;
225 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
226 for (itr->Seek(kRegKeyPrefix); itr->Valid(); itr->Next()) {
227 std::string key = itr->key().ToString();
228 if (!StartsWithASCII(key, kRegKeyPrefix, true))
229 break;
230
231 if (!ParseRegistrationKey(key, &origin, NULL /* registration_id */)) {
232 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
233 origins->clear();
234 return false;
235 }
236 origins->insert(origin);
237 }
238 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.
239 }
240
241 bool ServiceWorkerDatabase::GetRegistrationsForOrigin(
242 const GURL& origin,
243 std::vector<ServiceWorkerRegistrationData>* registrations) {
244 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
245 DCHECK(registrations);
246
247 if (!LazyOpen(false) || is_disabled_)
248 return false;
249
250 // Create a key prefix for registrations.
251 std::ostringstream out;
252 out << kRegKeyPrefix << origin.spec() << kKeySeparator;
253 std::string prefix = out.str();
254
255 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
256 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
257 std::string key = itr->key().ToString();
258 if (!StartsWithASCII(key, prefix, true))
259 break;
260
261 ServiceWorkerRegistrationData registration;
262 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/
263 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
264 registrations->clear();
265 return false;
266 }
267
268 registrations->push_back(registration);
269 }
270 return true;
271 }
272
273 bool ServiceWorkerDatabase::ReadRegistration(
274 int64 registration_id,
275 ServiceWorkerRegistrationData* registration,
276 std::vector<ServiceWorkerResourceRecord>* resources) {
277 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
278 DCHECK(registration);
279 DCHECK(resources);
280
281 if (!LazyOpen(false) || is_disabled_)
282 return false;
283
284 ServiceWorkerRegistrationData value;
285 if (!ReadRegistrationData(registration_id, &value))
286 return false;
287
288 if (!ReadResourceRecords(value.version_id(), resources))
289 return false;
290
291 *registration = value;
292 return true;
293 }
294
295 bool ServiceWorkerDatabase::WriteRegistration(
296 const ServiceWorkerRegistrationData& registration,
297 const std::vector<ServiceWorkerResourceRecord>& resources) {
298 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
299 if (!LazyOpen(true) || is_disabled_)
300 return false;
301
302 leveldb::WriteBatch batch;
303 if (!BumpNextAvailableIdIfNeeded(
304 kNextRegIdKey, registration.registration_id(), &batch) ||
305 !BumpNextAvailableIdIfNeeded(
306 kNextVerIdKey, registration.version_id(), &batch)) {
307 return false;
308 }
309
310 PutRegistrationDataToBatch(registration, &batch);
311
312 std::vector<ServiceWorkerResourceRecord>::const_iterator itr;
313 for (itr = resources.begin(); itr != resources.end(); ++itr)
314 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.
315
316 return WriteBatch(&batch);
317 }
318
319 bool ServiceWorkerDatabase::UpdateVersionToActive(int64 registration_id) {
320 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
321 if (!LazyOpen(false) || is_disabled_)
322 return false;
323
324 ServiceWorkerRegistrationData data;
325 if (!ReadRegistrationData(registration_id, &data))
326 return false;
327
328 data.set_is_active(true);
329
330 leveldb::WriteBatch batch;
331 PutRegistrationDataToBatch(data, &batch);
332 return WriteBatch(&batch);
333 }
334
335 bool ServiceWorkerDatabase::UpdateLastCheckTime(
336 int64 registration_id,
337 const base::Time& time) {
338 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
339 if (!LazyOpen(false) || is_disabled_)
340 return false;
341
342 ServiceWorkerRegistrationData data;
343 if (!ReadRegistrationData(registration_id, &data))
344 return false;
345
346 data.set_last_update_check_time(time.ToInternalValue());
347
348 leveldb::WriteBatch batch;
349 PutRegistrationDataToBatch(data, &batch);
350 return WriteBatch(&batch);
351 }
352
353 bool ServiceWorkerDatabase::DeleteRegistration(
354 int64 registration_id) {
355 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
356 if (!LazyOpen(false) || is_disabled_)
357 return false;
358
359 leveldb::WriteBatch batch;
360 ServiceWorkerRegistrationData data;
361 if (!ReadRegistrationData(registration_id, &data)) {
362 if (is_disabled_)
363 return false;
364 // Just not found.
365 return true;
366 }
367 batch.Delete(CreateRegistrationKey(data));
368
369 // Create a key prefix for resource records.
370 std::ostringstream out;
371 out << kResKeyPrefix << data.version_id() << kKeySeparator;
372 std::string prefix = out.str();
373
374 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
375 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
376 std::string key = itr->key().ToString();
377 if (!StartsWithASCII(key, prefix, true))
378 break;
379 batch.Delete(key);
380 }
381
382 return WriteBatch(&batch);
383 }
384
385 bool ServiceWorkerDatabase::GetUncommittedResourceIds(std::set<int64>* ids) {
386 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
387 DCHECK(ids);
388
389 if (!LazyOpen(false) || is_disabled_)
390 return false;
391
392 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
393 for (itr->Seek(kUncommittedResKeyPrefix); itr->Valid(); itr->Next()) {
394 std::string key = itr->key().ToString();
395 if (!StartsWithASCII(key, kUncommittedResKeyPrefix, true))
396 break;
397
398 std::string unprefixed = RemovePrefix(key, kUncommittedResKeyPrefix);
399 int64 resource_id;
400 if (!base::StringToInt64(unprefixed, &resource_id)) {
401 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
402 ids->clear();
403 return false;
404 }
405 ids->insert(resource_id);
406 }
407 return true;
408 }
409
410 bool ServiceWorkerDatabase::WriteUncommittedResourceIds(
411 const std::set<int64>& ids) {
412 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
413 if (!LazyOpen(true) || is_disabled_)
414 return false;
415 if (ids.empty())
416 return true;
417
418 leveldb::WriteBatch batch;
419 for (std::set<int64>::const_iterator itr = ids.begin();
420 itr != ids.end(); ++itr) {
421 // Value should be empty.
422 batch.Put(CreateUncommittedResourceKey(*itr), "");
423 }
424 return WriteBatch(&batch);
425 }
426
427 bool ServiceWorkerDatabase::ClearUncommittedResourceIds(
428 const std::set<int64>& ids) {
429 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
430 if (!LazyOpen(true) || is_disabled_)
431 return false;
432 if (ids.empty())
433 return true;
434
435 leveldb::WriteBatch batch;
436 for (std::set<int64>::const_iterator itr = ids.begin();
437 itr != ids.end(); ++itr) {
438 batch.Delete(CreateUncommittedResourceKey(*itr));
439 }
440 return WriteBatch(&batch);
441 }
442
443 bool ServiceWorkerDatabase::GetPurgeableResourceIds(std::set<int64>* ids) {
444 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
445 DCHECK(ids);
446
447 if (!LazyOpen(false) || is_disabled_)
448 return false;
449
450 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
451 for (itr->Seek(kPurgeableResKeyPrefix); itr->Valid(); itr->Next()) {
452 std::string key = itr->key().ToString();
453 if (!StartsWithASCII(key, kPurgeableResKeyPrefix, true))
454 break;
455
456 std::string unprefixed = RemovePrefix(key, kPurgeableResKeyPrefix);
457 int64 resource_id;
458 if (!base::StringToInt64(unprefixed, &resource_id)) {
459 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
460 ids->clear();
461 return false;
462 }
463 ids->insert(resource_id);
464 }
465 return true;
466 }
467
468 bool ServiceWorkerDatabase::WritePurgeableResourceIds(
469 const std::set<int64>& ids) {
470 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
471 if (!LazyOpen(true) || is_disabled_)
472 return false;
473 if (ids.empty())
474 return true;
475
476 leveldb::WriteBatch batch;
477 for (std::set<int64>::const_iterator itr = ids.begin();
478 itr != ids.end(); ++itr) {
479 // Value should be empty.
480 batch.Put(CreatePurgeableResourceKey(*itr), "");
481 }
482 return WriteBatch(&batch);
483 }
484
485 bool ServiceWorkerDatabase::ClearPurgeableResourceIds(
486 const std::set<int64>& ids) {
487 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
488 if (!LazyOpen(true) || is_disabled_)
489 return false;
490 if (ids.empty())
491 return true;
492
493 leveldb::WriteBatch batch;
494 for (std::set<int64>::const_iterator itr = ids.begin();
495 itr != ids.end(); ++itr) {
496 batch.Delete(CreatePurgeableResourceKey(*itr));
497 }
498 return WriteBatch(&batch);
499 }
500
501 bool ServiceWorkerDatabase::DeleteAllDataForOrigin(
502 const GURL& origin) {
503 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
504 NOTIMPLEMENTED();
505 return false;
506 }
507
508 bool ServiceWorkerDatabase::DeleteAllData() {
509 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
510 NOTIMPLEMENTED();
511 return false;
512 }
513
94 bool ServiceWorkerDatabase::LazyOpen(bool create_if_needed) { 514 bool ServiceWorkerDatabase::LazyOpen(bool create_if_needed) {
95 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); 515 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
96 if (IsOpen()) 516 if (IsOpen())
97 return true; 517 return true;
98 518
99 // Do not try to open a database if we tried and failed once. 519 // Do not try to open a database if we tried and failed once.
100 if (is_disabled_) 520 if (is_disabled_)
101 return false; 521 return false;
102 522
103 // When |path_| is empty, open a database in-memory. 523 // When |path_| is empty, open a database in-memory.
(...skipping 14 matching lines...) Expand all
118 env_.reset(leveldb::NewMemEnv(leveldb::Env::Default())); 538 env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
119 options.env = env_.get(); 539 options.env = env_.get();
120 } 540 }
121 541
122 leveldb::DB* db = NULL; 542 leveldb::DB* db = NULL;
123 leveldb::Status status = 543 leveldb::Status status =
124 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db); 544 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db);
125 if (!status.ok()) { 545 if (!status.ok()) {
126 DCHECK(!db); 546 DCHECK(!db);
127 // TODO(nhiroki): Should we retry to open the database? 547 // TODO(nhiroki): Should we retry to open the database?
128 DLOG(ERROR) << "Failed to open LevelDB database: " << status.ToString(); 548 HandleError(FROM_HERE, status);
129 is_disabled_ = true;
130 return false; 549 return false;
131 } 550 }
132 db_.reset(db); 551 db_.reset(db);
133 552
134 if (IsEmpty() && !PopulateInitialData()) { 553 if (IsEmpty() && !PopulateInitialData()) {
135 DLOG(ERROR) << "Failed to populate the database."; 554 DLOG(ERROR) << "Failed to populate the database.";
136 is_disabled_ = true;
137 db_.reset(); 555 db_.reset();
138 return false; 556 return false;
139 } 557 }
140 return true; 558 return true;
141 } 559 }
142 560
143 bool ServiceWorkerDatabase::PopulateInitialData() { 561 bool ServiceWorkerDatabase::PopulateInitialData() {
144 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); 562 leveldb::WriteBatch batch;
145 batch->Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion)); 563 batch.Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion));
146 batch->Put(kNextRegIdKey, "0"); 564 batch.Put(kNextRegIdKey, "0");
147 batch->Put(kNextResIdKey, "0"); 565 batch.Put(kNextResIdKey, "0");
148 batch->Put(kNextVerIdKey, "0"); 566 batch.Put(kNextVerIdKey, "0");
149 return WriteBatch(batch.Pass()); 567 return WriteBatch(&batch);
150 } 568 }
151 569
152 bool ServiceWorkerDatabase::ReadInt64( 570 bool ServiceWorkerDatabase::ReadNextAvailableId(
153 const leveldb::Slice& key, 571 const char* id_key, int64* next_avail_id) {
154 int64* value_out) { 572 DCHECK(id_key);
155 DCHECK(value_out); 573 DCHECK(next_avail_id);
156 574
157 std::string value; 575 std::string value;
158 leveldb::Status status = db_->Get(leveldb::ReadOptions(), key, &value); 576 leveldb::Status status = db_->Get(leveldb::ReadOptions(), id_key, &value);
159 if (!status.ok()) { 577 if (!status.ok()) {
160 DLOG(ERROR) << "Failed to read data keyed by " 578 HandleError(FROM_HERE, status);
161 << key.ToString() << ": " << status.ToString();
162 is_disabled_ = true;
163 if (status.IsCorruption())
164 was_corruption_detected_ = true;
165 return false; 579 return false;
166 } 580 }
167 581
168 int64 parsed = -1; 582 int64 parsed_id;
169 if (!base::StringToInt64(value, &parsed)) { 583 if (!base::StringToInt64(value, &parsed_id)) {
170 DLOG(ERROR) << "Database might be corrupted: " 584 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
171 << key.ToString() << ", " << value;
172 is_disabled_ = true;
173 was_corruption_detected_ = true;
174 return false; 585 return false;
175 } 586 }
176 587
177 *value_out = parsed; 588 *next_avail_id = parsed_id;
178 return true; 589 return true;
179 } 590 }
180 591
181 bool ServiceWorkerDatabase::WriteBatch(scoped_ptr<leveldb::WriteBatch> batch) { 592 bool ServiceWorkerDatabase::ReadRegistrationData(
182 if (!batch) 593 int64 registration_id,
594 ServiceWorkerRegistrationData* data_out) {
595 int64 parsed_id;
596 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
597 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
598 std::string key = itr->key().ToString();
599 if (!StartsWithASCII(key, kRegKeyPrefix, true))
600 return false;
601
602 if (!ParseRegistrationKey(key, NULL /* origin */, &parsed_id)) {
603 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
604 return false;
605 }
606 if (registration_id != parsed_id)
607 continue;
608
609 ServiceWorkerRegistrationData data;
610 if (!data.ParseFromString(itr->value().ToString())) {
611 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
612 return false;
613 }
614
615 *data_out = data;
183 return true; 616 return true;
617 }
184 618
185 leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch.get()); 619 return false;
186 if (status.ok()) 620 }
187 return true;
188 621
189 DLOG(ERROR) << "Failed to write the batch: " << status.ToString(); 622 bool ServiceWorkerDatabase::ReadResourceRecords(
190 is_disabled_ = true; 623 int64 version_id,
191 if (status.IsCorruption()) 624 std::vector<ServiceWorkerResourceRecord>* resources) {
192 was_corruption_detected_ = true; 625 DCHECK(resources);
193 return false; 626
627 // Create a key prefix for resource records.
628 std::ostringstream out;
629 out << kResKeyPrefix << version_id << kKeySeparator;
630 std::string prefix = out.str();
631
632 int64 resource_id;
633 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
634 for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
635 std::string key = itr->key().ToString();
636 if (!StartsWithASCII(key, prefix, true))
637 break;
638
639 if (!ParseResourceKey(key, NULL /* version_id */, &resource_id)) {
640 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
641 resources->clear();
642 return false;
643 }
644
645 ServiceWorkerResourceRecord resource;
646 if (!resource.ParseFromString(itr->value().ToString())) {
647 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse"));
648 resources->clear();
649 return false;
650 }
651
652 resources->push_back(resource);
653 }
654 return true;
655 }
656
657 bool ServiceWorkerDatabase::WriteBatch(leveldb::WriteBatch* batch) {
658 DCHECK(batch);
659 leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch);
660 if (!status.ok()) {
661 HandleError(FROM_HERE, status);
662 return false;
663 }
664 return true;
665 }
666
667 bool ServiceWorkerDatabase::BumpNextAvailableIdIfNeeded(
668 const char* id_key, int64 used_id, leveldb::WriteBatch* batch) {
669 DCHECK(batch);
670 int64 next_available_id;
671 if (!ReadNextAvailableId(id_key, &next_available_id))
672 return false;
673 if (next_available_id <= used_id)
674 batch->Put(id_key, base::Int64ToString(used_id + 1));
675 return true;
194 } 676 }
195 677
196 bool ServiceWorkerDatabase::IsOpen() { 678 bool ServiceWorkerDatabase::IsOpen() {
197 return db_.get() != NULL; 679 return db_.get() != NULL;
198 } 680 }
199 681
200 bool ServiceWorkerDatabase::IsEmpty() { 682 bool ServiceWorkerDatabase::IsEmpty() {
201 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); 683 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions()));
202 itr->SeekToFirst(); 684 itr->SeekToFirst();
203 return !itr->Valid(); 685 return !itr->Valid();
204 } 686 }
205 687
688 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.
689 const tracked_objects::Location& from_here,
690 const leveldb::Status& status) {
691 DLOG(ERROR) << "Failed at: " << from_here.ToString()
692 << " with error: " << status.ToString();
693 is_disabled_ = true;
694 if (status.IsCorruption())
695 was_corruption_detected_ = true;
696 }
697
206 } // namespace content 698 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698