OLD | NEW |
---|---|
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/location.h" |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/metrics/histogram.h" | |
12 #include "base/stl_util.h" | 13 #include "base/stl_util.h" |
13 #include "base/strings/string_number_conversions.h" | 14 #include "base/strings/string_number_conversions.h" |
14 #include "base/strings/string_split.h" | 15 #include "base/strings/string_split.h" |
15 #include "base/strings/string_util.h" | 16 #include "base/strings/string_util.h" |
16 #include "base/strings/stringprintf.h" | 17 #include "base/strings/stringprintf.h" |
17 #include "content/browser/service_worker/service_worker_database.pb.h" | 18 #include "content/browser/service_worker/service_worker_database.pb.h" |
18 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h" | 19 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h" |
19 #include "third_party/leveldatabase/src/include/leveldb/db.h" | 20 #include "third_party/leveldatabase/src/include/leveldb/db.h" |
20 #include "third_party/leveldatabase/src/include/leveldb/env.h" | 21 #include "third_party/leveldatabase/src/include/leveldb/env.h" |
21 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" | 22 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
69 | 70 |
70 const char kRegKeyPrefix[] = "REG:"; | 71 const char kRegKeyPrefix[] = "REG:"; |
71 const char kResKeyPrefix[] = "RES:"; | 72 const char kResKeyPrefix[] = "RES:"; |
72 const char kKeySeparator = '\x00'; | 73 const char kKeySeparator = '\x00'; |
73 | 74 |
74 const char kUncommittedResIdKeyPrefix[] = "URES:"; | 75 const char kUncommittedResIdKeyPrefix[] = "URES:"; |
75 const char kPurgeableResIdKeyPrefix[] = "PRES:"; | 76 const char kPurgeableResIdKeyPrefix[] = "PRES:"; |
76 | 77 |
77 const int64 kCurrentSchemaVersion = 1; | 78 const int64 kCurrentSchemaVersion = 1; |
78 | 79 |
80 // For histogram. | |
81 const char kOpenResultHistogramLabel[] = | |
82 "ServiceWorker.Database.OpenResult"; | |
83 const char kReadResultHistogramLabel[] = | |
84 "ServiceWorker.Database.ReadResult"; | |
85 const char kWriteResultHistogramLabel[] = | |
86 "ServiceWorker.Database.WriteResult"; | |
michaeln
2014/06/03 02:09:09
I think this set of stats is good.
| |
87 | |
79 bool RemovePrefix(const std::string& str, | 88 bool RemovePrefix(const std::string& str, |
80 const std::string& prefix, | 89 const std::string& prefix, |
81 std::string* out) { | 90 std::string* out) { |
82 if (!StartsWithASCII(str, prefix, true)) | 91 if (!StartsWithASCII(str, prefix, true)) |
83 return false; | 92 return false; |
84 if (out) | 93 if (out) |
85 *out = str.substr(prefix.size()); | 94 *out = str.substr(prefix.size()); |
86 return true; | 95 return true; |
87 } | 96 } |
88 | 97 |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
161 // Value should be empty. | 170 // Value should be empty. |
162 batch->Put(CreateUniqueOriginKey(origin), ""); | 171 batch->Put(CreateUniqueOriginKey(origin), ""); |
163 } | 172 } |
164 | 173 |
165 void PutPurgeableResourceIdToBatch(int64 resource_id, | 174 void PutPurgeableResourceIdToBatch(int64 resource_id, |
166 leveldb::WriteBatch* batch) { | 175 leveldb::WriteBatch* batch) { |
167 // Value should be empty. | 176 // Value should be empty. |
168 batch->Put(CreateResourceIdKey(kPurgeableResIdKeyPrefix, resource_id), ""); | 177 batch->Put(CreateResourceIdKey(kPurgeableResIdKeyPrefix, resource_id), ""); |
169 } | 178 } |
170 | 179 |
171 bool ParseRegistrationData(const std::string& serialized, | 180 ServiceWorkerDatabase::Status ParseId( |
172 ServiceWorkerDatabase::RegistrationData* out) { | 181 const std::string& serialized, |
182 int64* out) { | |
183 DCHECK(out); | |
184 int64 id; | |
185 if (!base::StringToInt64(serialized, &id) || id < 0) | |
186 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; | |
187 *out = id; | |
188 return ServiceWorkerDatabase::STATUS_OK; | |
189 } | |
190 | |
191 ServiceWorkerDatabase::Status ParseDatabaseVersion( | |
192 const std::string& serialized, | |
193 int64* out) { | |
194 DCHECK(out); | |
195 const int kFirstValidVersion = 1; | |
196 int64 version; | |
197 if (!base::StringToInt64(serialized, &version) || | |
198 version < kFirstValidVersion || | |
199 kCurrentSchemaVersion < version) { | |
michaeln
2014/06/03 02:09:09
The last case may not necessarily mean corruption,
nhiroki
2014/06/03 06:59:32
Ah, good point! Added special logging for that cas
| |
200 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; | |
201 } | |
202 *out = version; | |
203 return ServiceWorkerDatabase::STATUS_OK; | |
204 } | |
205 | |
206 ServiceWorkerDatabase::Status ParseRegistrationData( | |
207 const std::string& serialized, | |
208 ServiceWorkerDatabase::RegistrationData* out) { | |
173 DCHECK(out); | 209 DCHECK(out); |
174 ServiceWorkerRegistrationData data; | 210 ServiceWorkerRegistrationData data; |
175 if (!data.ParseFromString(serialized)) | 211 if (!data.ParseFromString(serialized)) |
176 return false; | 212 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; |
177 | 213 |
178 GURL scope_url(data.scope_url()); | 214 GURL scope_url(data.scope_url()); |
179 GURL script_url(data.script_url()); | 215 GURL script_url(data.script_url()); |
180 if (!scope_url.is_valid() || | 216 if (!scope_url.is_valid() || |
181 !script_url.is_valid() || | 217 !script_url.is_valid() || |
182 scope_url.GetOrigin() != script_url.GetOrigin()) { | 218 scope_url.GetOrigin() != script_url.GetOrigin()) { |
183 return false; | 219 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; |
184 } | 220 } |
185 | 221 |
186 // Convert ServiceWorkerRegistrationData to RegistrationData. | 222 // Convert ServiceWorkerRegistrationData to RegistrationData. |
187 out->registration_id = data.registration_id(); | 223 out->registration_id = data.registration_id(); |
188 out->scope = scope_url; | 224 out->scope = scope_url; |
189 out->script = script_url; | 225 out->script = script_url; |
190 out->version_id = data.version_id(); | 226 out->version_id = data.version_id(); |
191 out->is_active = data.is_active(); | 227 out->is_active = data.is_active(); |
192 out->has_fetch_handler = data.has_fetch_handler(); | 228 out->has_fetch_handler = data.has_fetch_handler(); |
193 out->last_update_check = | 229 out->last_update_check = |
194 base::Time::FromInternalValue(data.last_update_check_time()); | 230 base::Time::FromInternalValue(data.last_update_check_time()); |
195 return true; | 231 return ServiceWorkerDatabase::STATUS_OK; |
196 } | 232 } |
197 | 233 |
198 bool ParseResourceRecord(const std::string& serialized, | 234 ServiceWorkerDatabase::Status ParseResourceRecord( |
199 ServiceWorkerDatabase::ResourceRecord* out) { | 235 const std::string& serialized, |
236 ServiceWorkerDatabase::ResourceRecord* out) { | |
200 DCHECK(out); | 237 DCHECK(out); |
201 ServiceWorkerResourceRecord record; | 238 ServiceWorkerResourceRecord record; |
202 if (!record.ParseFromString(serialized)) | 239 if (!record.ParseFromString(serialized)) |
203 return false; | 240 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; |
204 | 241 |
205 GURL url(record.url()); | 242 GURL url(record.url()); |
206 if (!url.is_valid()) | 243 if (!url.is_valid()) |
207 return false; | 244 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; |
208 | 245 |
209 // Convert ServiceWorkerResourceRecord to ResourceRecord. | 246 // Convert ServiceWorkerResourceRecord to ResourceRecord. |
210 out->resource_id = record.resource_id(); | 247 out->resource_id = record.resource_id(); |
211 out->url = url; | 248 out->url = url; |
212 return true; | 249 return ServiceWorkerDatabase::STATUS_OK; |
213 } | 250 } |
214 | 251 |
215 ServiceWorkerDatabase::Status LevelDBStatusToStatus( | 252 ServiceWorkerDatabase::Status LevelDBStatusToStatus( |
216 const leveldb::Status& status) { | 253 const leveldb::Status& status) { |
217 if (status.ok()) | 254 if (status.ok()) |
218 return ServiceWorkerDatabase::STATUS_OK; | 255 return ServiceWorkerDatabase::STATUS_OK; |
219 else if (status.IsNotFound()) | 256 else if (status.IsNotFound()) |
220 return ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND; | 257 return ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND; |
258 else if (status.IsIOError()) | |
259 return ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR; | |
221 else if (status.IsCorruption()) | 260 else if (status.IsCorruption()) |
222 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; | 261 return ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED; |
223 else | 262 else |
224 return ServiceWorkerDatabase::STATUS_ERROR_FAILED; | 263 return ServiceWorkerDatabase::STATUS_ERROR_FAILED; |
225 } | 264 } |
226 | 265 |
266 const char* StatusToString(ServiceWorkerDatabase::Status status) { | |
267 switch (status) { | |
268 case ServiceWorkerDatabase::STATUS_OK: | |
269 return "Database OK"; | |
270 case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND: | |
271 return "Database not found"; | |
272 case ServiceWorkerDatabase::STATUS_ERROR_IO_ERROR: | |
273 return "Database IO error"; | |
274 case ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED: | |
275 return "Database corrupted"; | |
276 case ServiceWorkerDatabase::STATUS_ERROR_FAILED: | |
277 return "Database operation failed"; | |
278 case ServiceWorkerDatabase::STATUS_ERROR_MAX: | |
279 NOTREACHED(); | |
280 return "Database unknown error"; | |
281 } | |
282 NOTREACHED(); | |
283 return "Database unknown error"; | |
284 } | |
285 | |
286 void ReportOpenResult( | |
287 const tracked_objects::Location& from_here, | |
288 ServiceWorkerDatabase::Status status) { | |
289 if (status != ServiceWorkerDatabase::STATUS_OK) { | |
290 DLOG(ERROR) << "Failed at: " << from_here.ToString() | |
291 << " with error: " << StatusToString(status); | |
292 } | |
293 UMA_HISTOGRAM_ENUMERATION(kOpenResultHistogramLabel, | |
294 status, | |
295 ServiceWorkerDatabase::STATUS_ERROR_MAX); | |
296 } | |
297 | |
298 void ReportReadResult( | |
299 const tracked_objects::Location& from_here, | |
300 ServiceWorkerDatabase::Status status) { | |
301 if (status != ServiceWorkerDatabase::STATUS_OK) { | |
302 DLOG(ERROR) << "Failed at: " << from_here.ToString() | |
303 << " with error: " << StatusToString(status); | |
304 } | |
305 UMA_HISTOGRAM_ENUMERATION(kReadResultHistogramLabel, | |
306 status, | |
307 ServiceWorkerDatabase::STATUS_ERROR_MAX); | |
308 } | |
309 | |
310 void ReportWriteResult( | |
311 const tracked_objects::Location& from_here, | |
312 ServiceWorkerDatabase::Status status) { | |
313 if (status != ServiceWorkerDatabase::STATUS_OK) { | |
314 DLOG(ERROR) << "Failed at: " << from_here.ToString() | |
315 << " with error: " << StatusToString(status); | |
316 } | |
317 UMA_HISTOGRAM_ENUMERATION(kWriteResultHistogramLabel, | |
318 status, | |
319 ServiceWorkerDatabase::STATUS_ERROR_MAX); | |
320 } | |
321 | |
227 } // namespace | 322 } // namespace |
228 | 323 |
229 ServiceWorkerDatabase::RegistrationData::RegistrationData() | 324 ServiceWorkerDatabase::RegistrationData::RegistrationData() |
230 : registration_id(-1), | 325 : registration_id(-1), |
231 version_id(-1), | 326 version_id(-1), |
232 is_active(false), | 327 is_active(false), |
233 has_fetch_handler(false) { | 328 has_fetch_handler(false) { |
234 } | 329 } |
235 | 330 |
236 ServiceWorkerDatabase::RegistrationData::~RegistrationData() { | 331 ServiceWorkerDatabase::RegistrationData::~RegistrationData() { |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
288 ServiceWorkerDatabase::Status | 383 ServiceWorkerDatabase::Status |
289 ServiceWorkerDatabase::GetOriginsWithRegistrations(std::set<GURL>* origins) { | 384 ServiceWorkerDatabase::GetOriginsWithRegistrations(std::set<GURL>* origins) { |
290 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | 385 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
291 DCHECK(origins->empty()); | 386 DCHECK(origins->empty()); |
292 | 387 |
293 Status status = LazyOpen(false); | 388 Status status = LazyOpen(false); |
294 if (IsNewOrNonexistentDatabase(status)) | 389 if (IsNewOrNonexistentDatabase(status)) |
295 return STATUS_OK; | 390 return STATUS_OK; |
296 if (status != STATUS_OK) | 391 if (status != STATUS_OK) |
297 return status; | 392 return status; |
393 DCHECK_EQ(STATUS_OK, status); | |
298 | 394 |
299 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); | 395 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
300 for (itr->Seek(kUniqueOriginKey); itr->Valid(); itr->Next()) { | 396 for (itr->Seek(kUniqueOriginKey); itr->Valid(); itr->Next()) { |
301 if (!itr->status().ok()) { | 397 status = LevelDBStatusToStatus(itr->status()); |
302 HandleError(FROM_HERE, itr->status()); | 398 if (status != STATUS_OK) |
303 origins->clear(); | 399 break; |
304 return LevelDBStatusToStatus(itr->status()); | |
305 } | |
306 | |
307 std::string origin; | 400 std::string origin; |
308 if (!RemovePrefix(itr->key().ToString(), kUniqueOriginKey, &origin)) | 401 if (!RemovePrefix(itr->key().ToString(), kUniqueOriginKey, &origin)) |
309 break; | 402 break; |
310 origins->insert(GURL(origin)); | 403 origins->insert(GURL(origin)); |
311 } | 404 } |
312 return STATUS_OK; | 405 |
406 if (status != STATUS_OK) { | |
407 Disable(); | |
408 origins->clear(); | |
409 } | |
410 | |
411 ReportReadResult(FROM_HERE, status); | |
412 return status; | |
313 } | 413 } |
314 | 414 |
315 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetRegistrationsForOrigin( | 415 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetRegistrationsForOrigin( |
316 const GURL& origin, | 416 const GURL& origin, |
317 std::vector<RegistrationData>* registrations) { | 417 std::vector<RegistrationData>* registrations) { |
318 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | 418 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
319 DCHECK(registrations->empty()); | 419 DCHECK(registrations->empty()); |
320 | 420 |
321 Status status = LazyOpen(false); | 421 Status status = LazyOpen(false); |
322 if (IsNewOrNonexistentDatabase(status)) | 422 if (IsNewOrNonexistentDatabase(status)) |
323 return STATUS_OK; | 423 return STATUS_OK; |
324 if (status != STATUS_OK) | 424 if (status != STATUS_OK) |
325 return status; | 425 return status; |
426 DCHECK_EQ(STATUS_OK, status); | |
michaeln
2014/06/03 02:09:09
given line 424, not sure this dcheck is needed
nhiroki
2014/06/03 06:59:32
Removed them.
| |
326 | 427 |
327 // Create a key prefix for registrations. | 428 // Create a key prefix for registrations. |
328 std::string prefix = base::StringPrintf( | 429 std::string prefix = base::StringPrintf( |
329 "%s%s%c", kRegKeyPrefix, origin.spec().c_str(), kKeySeparator); | 430 "%s%s%c", kRegKeyPrefix, origin.spec().c_str(), kKeySeparator); |
330 | 431 |
331 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); | 432 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
332 for (itr->Seek(prefix); itr->Valid(); itr->Next()) { | 433 for (itr->Seek(prefix); itr->Valid(); itr->Next()) { |
333 if (!itr->status().ok()) { | 434 status = LevelDBStatusToStatus(itr->status()); |
334 HandleError(FROM_HERE, itr->status()); | 435 if (status != STATUS_OK || |
335 registrations->clear(); | 436 !RemovePrefix(itr->key().ToString(), prefix, NULL)) { |
336 return LevelDBStatusToStatus(itr->status()); | 437 break; |
337 } | 438 } |
338 | 439 RegistrationData registration; |
339 if (!RemovePrefix(itr->key().ToString(), prefix, NULL)) | 440 status = ParseRegistrationData(itr->value().ToString(), ®istration); |
441 if (status != STATUS_OK) | |
340 break; | 442 break; |
341 | |
342 RegistrationData registration; | |
343 if (!ParseRegistrationData(itr->value().ToString(), ®istration)) { | |
344 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | |
345 registrations->clear(); | |
346 return STATUS_ERROR_CORRUPTED; | |
347 } | |
348 registrations->push_back(registration); | 443 registrations->push_back(registration); |
349 } | 444 } |
350 return STATUS_OK; | 445 |
446 if (status != STATUS_OK) { | |
447 Disable(); | |
448 registrations->clear(); | |
449 } | |
450 | |
451 ReportReadResult(FROM_HERE, status); | |
452 return status; | |
351 } | 453 } |
352 | 454 |
353 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetAllRegistrations( | 455 ServiceWorkerDatabase::Status ServiceWorkerDatabase::GetAllRegistrations( |
354 std::vector<RegistrationData>* registrations) { | 456 std::vector<RegistrationData>* registrations) { |
355 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | 457 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
356 DCHECK(registrations->empty()); | 458 DCHECK(registrations->empty()); |
357 | 459 |
358 Status status = LazyOpen(false); | 460 Status status = LazyOpen(false); |
359 if (IsNewOrNonexistentDatabase(status)) | 461 if (IsNewOrNonexistentDatabase(status)) |
360 return STATUS_OK; | 462 return STATUS_OK; |
361 if (status != STATUS_OK) | 463 if (status != STATUS_OK) |
362 return status; | 464 return status; |
465 DCHECK_EQ(STATUS_OK, status); | |
363 | 466 |
364 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); | 467 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
365 for (itr->Seek(kRegKeyPrefix); itr->Valid(); itr->Next()) { | 468 for (itr->Seek(kRegKeyPrefix); itr->Valid(); itr->Next()) { |
366 if (!itr->status().ok()) { | 469 status = LevelDBStatusToStatus(itr->status()); |
367 HandleError(FROM_HERE, itr->status()); | 470 if (status != STATUS_OK || |
368 registrations->clear(); | 471 !RemovePrefix(itr->key().ToString(), kRegKeyPrefix, NULL)) { |
369 return LevelDBStatusToStatus(itr->status()); | 472 break; |
370 } | 473 } |
371 | 474 RegistrationData registration; |
372 if (!RemovePrefix(itr->key().ToString(), kRegKeyPrefix, NULL)) | 475 status = ParseRegistrationData(itr->value().ToString(), ®istration); |
476 if (status != STATUS_OK) | |
373 break; | 477 break; |
374 | |
375 RegistrationData registration; | |
376 if (!ParseRegistrationData(itr->value().ToString(), ®istration)) { | |
377 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | |
378 registrations->clear(); | |
379 return STATUS_ERROR_CORRUPTED; | |
380 } | |
381 registrations->push_back(registration); | 478 registrations->push_back(registration); |
382 } | 479 } |
383 return STATUS_OK; | 480 |
481 if (status != STATUS_OK) { | |
482 Disable(); | |
483 registrations->clear(); | |
484 } | |
485 | |
486 ReportReadResult(FROM_HERE, status); | |
487 return status; | |
384 } | 488 } |
385 | 489 |
386 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistration( | 490 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistration( |
387 int64 registration_id, | 491 int64 registration_id, |
388 const GURL& origin, | 492 const GURL& origin, |
389 RegistrationData* registration, | 493 RegistrationData* registration, |
390 std::vector<ResourceRecord>* resources) { | 494 std::vector<ResourceRecord>* resources) { |
391 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | 495 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
392 DCHECK(registration); | 496 DCHECK(registration); |
393 DCHECK(resources); | 497 DCHECK(resources); |
(...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
667 } | 771 } |
668 | 772 |
669 leveldb::Options options; | 773 leveldb::Options options; |
670 options.create_if_missing = create_if_missing; | 774 options.create_if_missing = create_if_missing; |
671 if (use_in_memory_db) { | 775 if (use_in_memory_db) { |
672 env_.reset(leveldb::NewMemEnv(leveldb::Env::Default())); | 776 env_.reset(leveldb::NewMemEnv(leveldb::Env::Default())); |
673 options.env = env_.get(); | 777 options.env = env_.get(); |
674 } | 778 } |
675 | 779 |
676 leveldb::DB* db = NULL; | 780 leveldb::DB* db = NULL; |
677 leveldb::Status db_status = | 781 Status status = LevelDBStatusToStatus( |
678 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db); | 782 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db)); |
679 if (!db_status.ok()) { | 783 ReportOpenResult(FROM_HERE, status); |
784 if (status != STATUS_OK) { | |
680 DCHECK(!db); | 785 DCHECK(!db); |
681 // TODO(nhiroki): Should we retry to open the database? | 786 // TODO(nhiroki): Should we retry to open the database? |
682 HandleError(FROM_HERE, db_status); | 787 Disable(); |
683 return LevelDBStatusToStatus(db_status); | 788 return status; |
684 } | 789 } |
685 db_.reset(db); | 790 db_.reset(db); |
686 | 791 |
687 int64 db_version; | 792 int64 db_version; |
688 Status status = ReadDatabaseVersion(&db_version); | 793 status = ReadDatabaseVersion(&db_version); |
689 if (status != STATUS_OK) | 794 if (status != STATUS_OK) |
690 return status; | 795 return status; |
691 DCHECK_LE(0, db_version); | 796 DCHECK_LE(0, db_version); |
692 if (db_version > 0) | 797 if (db_version > 0) |
693 state_ = INITIALIZED; | 798 state_ = INITIALIZED; |
694 return STATUS_OK; | 799 return STATUS_OK; |
695 } | 800 } |
696 | 801 |
697 bool ServiceWorkerDatabase::IsNewOrNonexistentDatabase( | 802 bool ServiceWorkerDatabase::IsNewOrNonexistentDatabase( |
698 ServiceWorkerDatabase::Status status) { | 803 ServiceWorkerDatabase::Status status) { |
699 if (status == STATUS_ERROR_NOT_FOUND) | 804 if (status == STATUS_ERROR_NOT_FOUND) |
700 return true; | 805 return true; |
701 if (status == STATUS_OK && state_ == UNINITIALIZED) | 806 if (status == STATUS_OK && state_ == UNINITIALIZED) |
702 return true; | 807 return true; |
703 return false; | 808 return false; |
704 } | 809 } |
705 | 810 |
706 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadNextAvailableId( | 811 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadNextAvailableId( |
707 const char* id_key, | 812 const char* id_key, |
708 int64* next_avail_id) { | 813 int64* next_avail_id) { |
709 DCHECK(id_key); | 814 DCHECK(id_key); |
710 DCHECK(next_avail_id); | 815 DCHECK(next_avail_id); |
711 | 816 |
712 std::string value; | 817 std::string value; |
713 leveldb::Status status = db_->Get(leveldb::ReadOptions(), id_key, &value); | 818 Status status = LevelDBStatusToStatus( |
714 if (status.IsNotFound()) { | 819 db_->Get(leveldb::ReadOptions(), id_key, &value)); |
820 if (status == STATUS_ERROR_NOT_FOUND) { | |
715 // Nobody has gotten the next resource id for |id_key|. | 821 // Nobody has gotten the next resource id for |id_key|. |
716 *next_avail_id = 0; | 822 *next_avail_id = 0; |
823 ReportReadResult(FROM_HERE, STATUS_OK); | |
717 return STATUS_OK; | 824 return STATUS_OK; |
825 } else if (status != STATUS_OK) { | |
826 Disable(); | |
827 ReportReadResult(FROM_HERE, status); | |
828 return status; | |
718 } | 829 } |
719 | 830 |
720 if (!status.ok()) { | 831 status = ParseId(value, next_avail_id); |
721 HandleError(FROM_HERE, status); | 832 if (status != STATUS_OK) |
722 return LevelDBStatusToStatus(status); | 833 Disable(); |
723 } | |
724 | 834 |
725 int64 parsed; | 835 ReportReadResult(FROM_HERE, status); |
726 if (!base::StringToInt64(value, &parsed)) { | 836 return status; |
727 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | |
728 return STATUS_ERROR_CORRUPTED; | |
729 } | |
730 | |
731 *next_avail_id = parsed; | |
732 return STATUS_OK; | |
733 } | 837 } |
734 | 838 |
735 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistrationData( | 839 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadRegistrationData( |
736 int64 registration_id, | 840 int64 registration_id, |
737 const GURL& origin, | 841 const GURL& origin, |
738 RegistrationData* registration) { | 842 RegistrationData* registration) { |
739 DCHECK(registration); | 843 DCHECK(registration); |
740 | 844 |
741 std::string key = CreateRegistrationKey(registration_id, origin); | 845 const std::string key = CreateRegistrationKey(registration_id, origin); |
742 | |
743 std::string value; | 846 std::string value; |
744 leveldb::Status status = db_->Get(leveldb::ReadOptions(), key, &value); | 847 Status status = LevelDBStatusToStatus( |
745 if (!status.ok()) { | 848 db_->Get(leveldb::ReadOptions(), key, &value)); |
746 if (!status.IsNotFound()) | 849 if (status == STATUS_ERROR_NOT_FOUND) { |
747 HandleError(FROM_HERE, status); | 850 ReportReadResult(FROM_HERE, STATUS_OK); |
748 return LevelDBStatusToStatus(status); | 851 return status; |
852 } else if (status != STATUS_OK) { | |
853 Disable(); | |
854 ReportReadResult(FROM_HERE, status); | |
855 return status; | |
749 } | 856 } |
750 | 857 |
751 RegistrationData parsed; | 858 status = ParseRegistrationData(value, registration); |
752 if (!ParseRegistrationData(value, &parsed)) { | 859 if (status != STATUS_OK) |
753 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | 860 Disable(); |
754 return STATUS_ERROR_CORRUPTED; | |
755 } | |
756 | 861 |
757 *registration = parsed; | 862 ReportReadResult(FROM_HERE, status); |
758 return STATUS_OK; | 863 return status; |
759 } | 864 } |
760 | 865 |
761 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceRecords( | 866 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceRecords( |
762 int64 version_id, | 867 int64 version_id, |
763 std::vector<ResourceRecord>* resources) { | 868 std::vector<ResourceRecord>* resources) { |
764 DCHECK(resources); | 869 DCHECK(resources->empty()); |
765 | 870 |
766 std::string prefix = CreateResourceRecordKeyPrefix(version_id); | 871 Status status = STATUS_OK; |
872 const std::string prefix = CreateResourceRecordKeyPrefix(version_id); | |
873 | |
767 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); | 874 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
768 for (itr->Seek(prefix); itr->Valid(); itr->Next()) { | 875 for (itr->Seek(prefix); itr->Valid(); itr->Next()) { |
769 if (!itr->status().ok()) { | 876 status = LevelDBStatusToStatus(itr->status()); |
770 HandleError(FROM_HERE, itr->status()); | 877 if (status != STATUS_OK || |
771 resources->clear(); | 878 !RemovePrefix(itr->key().ToString(), prefix, NULL)) { |
772 return LevelDBStatusToStatus(itr->status()); | 879 break; |
773 } | 880 } |
774 | 881 ResourceRecord resource; |
775 if (!RemovePrefix(itr->key().ToString(), prefix, NULL)) | 882 status = ParseResourceRecord(itr->value().ToString(), &resource); |
883 if (status != STATUS_OK) | |
776 break; | 884 break; |
777 | |
778 ResourceRecord resource; | |
779 if (!ParseResourceRecord(itr->value().ToString(), &resource)) { | |
780 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | |
781 resources->clear(); | |
782 return STATUS_ERROR_CORRUPTED; | |
783 } | |
784 resources->push_back(resource); | 885 resources->push_back(resource); |
785 } | 886 } |
786 return STATUS_OK; | 887 |
888 if (status != STATUS_OK) { | |
889 Disable(); | |
890 resources->clear(); | |
891 } | |
892 | |
893 ReportReadResult(FROM_HERE, status); | |
894 return status; | |
787 } | 895 } |
788 | 896 |
789 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceRecords( | 897 ServiceWorkerDatabase::Status ServiceWorkerDatabase::DeleteResourceRecords( |
790 int64 version_id, | 898 int64 version_id, |
791 std::vector<int64>* newly_purgeable_resources, | 899 std::vector<int64>* newly_purgeable_resources, |
792 leveldb::WriteBatch* batch) { | 900 leveldb::WriteBatch* batch) { |
793 DCHECK(batch); | 901 DCHECK(batch); |
794 | 902 |
795 std::string prefix = CreateResourceRecordKeyPrefix(version_id); | 903 Status status = STATUS_OK; |
904 const std::string prefix = CreateResourceRecordKeyPrefix(version_id); | |
905 | |
796 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); | 906 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
797 for (itr->Seek(prefix); itr->Valid(); itr->Next()) { | 907 for (itr->Seek(prefix); itr->Valid(); itr->Next()) { |
798 if (!itr->status().ok()) { | 908 status = LevelDBStatusToStatus(itr->status()); |
799 HandleError(FROM_HERE, itr->status()); | 909 if (status != STATUS_OK) |
800 return LevelDBStatusToStatus(itr->status()); | 910 break; |
801 } | |
802 | 911 |
803 std::string key = itr->key().ToString(); | 912 const std::string key = itr->key().ToString(); |
804 std::string unprefixed; | 913 std::string unprefixed; |
805 if (!RemovePrefix(key, prefix, &unprefixed)) | 914 if (!RemovePrefix(key, prefix, &unprefixed)) |
806 break; | 915 break; |
807 | 916 |
808 int64 resource_id; | 917 int64 resource_id; |
809 if (!base::StringToInt64(unprefixed, &resource_id)) { | 918 status = ParseId(unprefixed, &resource_id); |
810 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | 919 if (status != STATUS_OK) |
811 return STATUS_ERROR_CORRUPTED; | 920 break; |
812 } | |
813 | 921 |
814 // Remove a resource record. | 922 // Remove a resource record. |
815 batch->Delete(key); | 923 batch->Delete(key); |
816 | 924 |
817 // Currently resource sharing across versions and registrations is not | 925 // Currently resource sharing across versions and registrations is not |
818 // supported, so we can purge this without caring about it. | 926 // supported, so we can purge this without caring about it. |
819 PutPurgeableResourceIdToBatch(resource_id, batch); | 927 PutPurgeableResourceIdToBatch(resource_id, batch); |
820 newly_purgeable_resources->push_back(resource_id); | 928 newly_purgeable_resources->push_back(resource_id); |
821 } | 929 } |
822 return STATUS_OK; | 930 |
931 if (status != STATUS_OK) | |
932 Disable(); | |
933 | |
934 ReportReadResult(FROM_HERE, status); | |
935 return status; | |
823 } | 936 } |
824 | 937 |
825 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceIds( | 938 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadResourceIds( |
826 const char* id_key_prefix, | 939 const char* id_key_prefix, |
827 std::set<int64>* ids) { | 940 std::set<int64>* ids) { |
828 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | 941 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
829 DCHECK(id_key_prefix); | 942 DCHECK(id_key_prefix); |
830 DCHECK(ids->empty()); | 943 DCHECK(ids->empty()); |
831 | 944 |
832 Status status = LazyOpen(false); | 945 Status status = LazyOpen(false); |
833 if (IsNewOrNonexistentDatabase(status)) | 946 if (IsNewOrNonexistentDatabase(status)) |
834 return STATUS_OK; | 947 return STATUS_OK; |
835 if (status != STATUS_OK) | 948 if (status != STATUS_OK) |
836 return status; | 949 return status; |
950 DCHECK_EQ(STATUS_OK, status); | |
837 | 951 |
838 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); | 952 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
839 for (itr->Seek(id_key_prefix); itr->Valid(); itr->Next()) { | 953 for (itr->Seek(id_key_prefix); itr->Valid(); itr->Next()) { |
840 if (!itr->status().ok()) { | 954 status = LevelDBStatusToStatus(itr->status()); |
841 HandleError(FROM_HERE, itr->status()); | 955 if (status != STATUS_OK) |
842 ids->clear(); | 956 break; |
843 return LevelDBStatusToStatus(itr->status()); | |
844 } | |
845 | 957 |
846 std::string unprefixed; | 958 std::string unprefixed; |
847 if (!RemovePrefix(itr->key().ToString(), id_key_prefix, &unprefixed)) | 959 if (!RemovePrefix(itr->key().ToString(), id_key_prefix, &unprefixed)) |
848 break; | 960 break; |
849 | 961 |
850 int64 resource_id; | 962 int64 resource_id; |
851 if (!base::StringToInt64(unprefixed, &resource_id)) { | 963 status = ParseId(unprefixed, &resource_id); |
852 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | 964 if (status != STATUS_OK) |
853 ids->clear(); | 965 break; |
854 return STATUS_ERROR_CORRUPTED; | |
855 } | |
856 ids->insert(resource_id); | 966 ids->insert(resource_id); |
857 } | 967 } |
858 return STATUS_OK; | 968 |
969 if (status != STATUS_OK) { | |
970 Disable(); | |
971 ids->clear(); | |
972 } | |
973 | |
974 ReportReadResult(FROM_HERE, status); | |
975 return status; | |
859 } | 976 } |
860 | 977 |
861 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIds( | 978 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteResourceIds( |
862 const char* id_key_prefix, | 979 const char* id_key_prefix, |
863 const std::set<int64>& ids) { | 980 const std::set<int64>& ids) { |
864 leveldb::WriteBatch batch; | 981 leveldb::WriteBatch batch; |
865 Status status = WriteResourceIdsInBatch(id_key_prefix, ids, &batch); | 982 Status status = WriteResourceIdsInBatch(id_key_prefix, ids, &batch); |
866 if (status != STATUS_OK) | 983 if (status != STATUS_OK) |
867 return status; | 984 return status; |
868 return WriteBatch(&batch); | 985 return WriteBatch(&batch); |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
917 for (std::set<int64>::const_iterator itr = ids.begin(); | 1034 for (std::set<int64>::const_iterator itr = ids.begin(); |
918 itr != ids.end(); ++itr) { | 1035 itr != ids.end(); ++itr) { |
919 batch->Delete(CreateResourceIdKey(id_key_prefix, *itr)); | 1036 batch->Delete(CreateResourceIdKey(id_key_prefix, *itr)); |
920 } | 1037 } |
921 return STATUS_OK; | 1038 return STATUS_OK; |
922 } | 1039 } |
923 | 1040 |
924 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadDatabaseVersion( | 1041 ServiceWorkerDatabase::Status ServiceWorkerDatabase::ReadDatabaseVersion( |
925 int64* db_version) { | 1042 int64* db_version) { |
926 std::string value; | 1043 std::string value; |
927 leveldb::Status status = | 1044 Status status = LevelDBStatusToStatus( |
928 db_->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value); | 1045 db_->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value)); |
929 if (status.IsNotFound()) { | 1046 if (status == STATUS_ERROR_NOT_FOUND) { |
930 // The database hasn't been initialized yet. | 1047 // The database hasn't been initialized yet. |
931 *db_version = 0; | 1048 *db_version = 0; |
1049 ReportReadResult(FROM_HERE, STATUS_OK); | |
932 return STATUS_OK; | 1050 return STATUS_OK; |
933 } | 1051 } else if (status != STATUS_OK) { |
934 if (!status.ok()) { | 1052 Disable(); |
935 HandleError(FROM_HERE, status); | 1053 ReportReadResult(FROM_HERE, status); |
936 return LevelDBStatusToStatus(status); | 1054 return status; |
937 } | 1055 } |
938 | 1056 |
939 int64 parsed; | 1057 status = ParseDatabaseVersion(value, db_version); |
940 if (!base::StringToInt64(value, &parsed)) { | 1058 if (status != STATUS_OK) |
941 HandleError(FROM_HERE, leveldb::Status::Corruption("failed to parse")); | 1059 Disable(); |
942 return STATUS_ERROR_CORRUPTED; | |
943 } | |
944 | 1060 |
945 const int kFirstValidVersion = 1; | 1061 ReportReadResult(FROM_HERE, status); |
946 if (parsed < kFirstValidVersion || kCurrentSchemaVersion < parsed) { | 1062 return status; |
947 HandleError(FROM_HERE, leveldb::Status::Corruption("invalid DB version")); | |
948 return STATUS_ERROR_CORRUPTED; | |
949 } | |
950 | |
951 *db_version = parsed; | |
952 return STATUS_OK; | |
953 } | 1063 } |
954 | 1064 |
955 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteBatch( | 1065 ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteBatch( |
956 leveldb::WriteBatch* batch) { | 1066 leveldb::WriteBatch* batch) { |
957 DCHECK(batch); | 1067 DCHECK(batch); |
958 DCHECK_NE(DISABLED, state_); | 1068 DCHECK_NE(DISABLED, state_); |
959 | 1069 |
960 if (state_ == UNINITIALIZED) { | 1070 if (state_ == UNINITIALIZED) { |
961 // Write the database schema version. | 1071 // Write the database schema version. |
962 batch->Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion)); | 1072 batch->Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion)); |
963 state_ = INITIALIZED; | 1073 state_ = INITIALIZED; |
964 } | 1074 } |
965 | 1075 |
966 leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch); | 1076 Status status = LevelDBStatusToStatus( |
967 if (!status.ok()) | 1077 db_->Write(leveldb::WriteOptions(), batch)); |
968 HandleError(FROM_HERE, status); | 1078 if (status != STATUS_OK) |
969 return LevelDBStatusToStatus(status); | 1079 Disable(); |
1080 | |
1081 ReportWriteResult(FROM_HERE, status); | |
1082 return status; | |
970 } | 1083 } |
971 | 1084 |
972 void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded( | 1085 void ServiceWorkerDatabase::BumpNextRegistrationIdIfNeeded( |
973 int64 used_id, leveldb::WriteBatch* batch) { | 1086 int64 used_id, leveldb::WriteBatch* batch) { |
974 DCHECK(batch); | 1087 DCHECK(batch); |
975 if (next_avail_registration_id_ <= used_id) { | 1088 if (next_avail_registration_id_ <= used_id) { |
976 next_avail_registration_id_ = used_id + 1; | 1089 next_avail_registration_id_ = used_id + 1; |
977 batch->Put(kNextRegIdKey, base::Int64ToString(next_avail_registration_id_)); | 1090 batch->Put(kNextRegIdKey, base::Int64ToString(next_avail_registration_id_)); |
978 } | 1091 } |
979 } | 1092 } |
980 | 1093 |
981 void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded( | 1094 void ServiceWorkerDatabase::BumpNextVersionIdIfNeeded( |
982 int64 used_id, leveldb::WriteBatch* batch) { | 1095 int64 used_id, leveldb::WriteBatch* batch) { |
983 DCHECK(batch); | 1096 DCHECK(batch); |
984 if (next_avail_version_id_ <= used_id) { | 1097 if (next_avail_version_id_ <= used_id) { |
985 next_avail_version_id_ = used_id + 1; | 1098 next_avail_version_id_ = used_id + 1; |
986 batch->Put(kNextVerIdKey, base::Int64ToString(next_avail_version_id_)); | 1099 batch->Put(kNextVerIdKey, base::Int64ToString(next_avail_version_id_)); |
987 } | 1100 } |
988 } | 1101 } |
989 | 1102 |
990 bool ServiceWorkerDatabase::IsOpen() { | 1103 bool ServiceWorkerDatabase::IsOpen() { |
991 return db_ != NULL; | 1104 return db_ != NULL; |
992 } | 1105 } |
993 | 1106 |
994 void ServiceWorkerDatabase::HandleError( | 1107 void ServiceWorkerDatabase::Disable() { |
michaeln
2014/06/03 02:09:09
The new code looks correct. It maybe could have be
nhiroki
2014/06/03 06:59:32
SGTM! I'd prefer to work with Handle{...}Result()
| |
995 const tracked_objects::Location& from_here, | 1108 DLOG(ERROR) << "ServiceWorkerDatabase is disabled."; |
996 const leveldb::Status& status) { | |
997 // TODO(nhiroki): Add an UMA histogram. | |
998 DLOG(ERROR) << "Failed at: " << from_here.ToString() | |
999 << " with error: " << status.ToString(); | |
1000 state_ = DISABLED; | 1109 state_ = DISABLED; |
1001 db_.reset(); | 1110 db_.reset(); |
1002 } | 1111 } |
1003 | 1112 |
1004 } // namespace content | 1113 } // namespace content |
OLD | NEW |