Chromium Code Reviews| 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/logging.h" | 11 #include "base/logging.h" |
| 11 #include "base/strings/string_number_conversions.h" | 12 #include "base/strings/string_number_conversions.h" |
| 12 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h" | 13 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h" |
| 13 #include "third_party/leveldatabase/src/include/leveldb/db.h" | 14 #include "third_party/leveldatabase/src/include/leveldb/db.h" |
| 14 #include "third_party/leveldatabase/src/include/leveldb/env.h" | 15 #include "third_party/leveldatabase/src/include/leveldb/env.h" |
| 15 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" | 16 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" |
| 16 | 17 |
| 17 // LevelDB database schema | 18 // LevelDB database schema |
| 18 // ======================= | 19 // ======================= |
| 19 // | 20 // |
| 21 // NOTE | |
| 22 // - int64 value is serialized as a string by base::Int64ToString(). | |
| 23 // | |
| 20 // Version 1 (in sorted order) | 24 // Version 1 (in sorted order) |
| 21 // key: "DB_VERSION" | 25 // key: "DB_VERSION" |
| 22 // value: <int64 serialized as a string> | 26 // value: "1" |
| 23 // | 27 // |
| 24 // key: "NEXT_REGISTRATION_ID" | 28 // key: "NEXT_REGISTRATION_ID" |
| 25 // value: <int64 serialized as a string> | 29 // value: <int64 'next_available_registration_id'> |
| 26 // | 30 // |
| 27 // key: "NEXT_RESOURCE_ID" | 31 // key: "NEXT_RESOURCE_ID" |
| 28 // value: <int64 serialized as a string> | 32 // value: <int64 'next_available_resource_id'> |
| 29 // | 33 // |
| 30 // key: "NEXT_VERSION_ID" | 34 // key: "NEXT_VERSION_ID" |
| 31 // value: <int64 serialized as a string> | 35 // value: <int64 'next_available_version_id'> |
| 32 | 36 |
| 33 namespace content { | 37 namespace content { |
| 34 | 38 |
| 35 namespace { | 39 namespace { |
| 36 | 40 |
| 37 const char kDatabaseVersionKey[] = "DB_VERSION"; | 41 const char kDatabaseVersionKey[] = "DB_VERSION"; |
| 38 const char kNextRegIdKey[] = "NEXT_REGISTRATION_ID"; | 42 const char kNextRegIdKey[] = "NEXT_REGISTRATION_ID"; |
| 39 const char kNextResIdKey[] = "NEXT_RESOURCE_ID"; | 43 const char kNextResIdKey[] = "NEXT_RESOURCE_ID"; |
| 40 const char kNextVerIdKey[] = "NEXT_VERSION_ID"; | 44 const char kNextVerIdKey[] = "NEXT_VERSION_ID"; |
| 41 | 45 |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 73 DCHECK(next_avail_version_id); | 77 DCHECK(next_avail_version_id); |
| 74 DCHECK(next_avail_resource_id); | 78 DCHECK(next_avail_resource_id); |
| 75 | 79 |
| 76 if (!LazyOpen(false) || is_disabled_) | 80 if (!LazyOpen(false) || is_disabled_) |
| 77 return false; | 81 return false; |
| 78 | 82 |
| 79 int64 reg_id = -1; | 83 int64 reg_id = -1; |
| 80 int64 ver_id = -1; | 84 int64 ver_id = -1; |
| 81 int64 res_id = -1; | 85 int64 res_id = -1; |
| 82 | 86 |
| 83 if (!ReadInt64(kNextRegIdKey, ®_id) || | 87 if (!ReadNextAvailableId(kNextRegIdKey, ®_id) || |
| 84 !ReadInt64(kNextVerIdKey, &ver_id) || | 88 !ReadNextAvailableId(kNextVerIdKey, &ver_id) || |
| 85 !ReadInt64(kNextResIdKey, &res_id)) | 89 !ReadNextAvailableId(kNextResIdKey, &res_id)) |
| 86 return false; | 90 return false; |
| 87 | 91 |
| 88 *next_avail_registration_id = reg_id; | 92 *next_avail_registration_id = reg_id; |
| 89 *next_avail_version_id = ver_id; | 93 *next_avail_version_id = ver_id; |
| 90 *next_avail_resource_id = res_id; | 94 *next_avail_resource_id = res_id; |
| 91 return true; | 95 return true; |
| 92 } | 96 } |
| 93 | 97 |
| 94 bool ServiceWorkerDatabase::LazyOpen(bool create_if_needed) { | 98 bool ServiceWorkerDatabase::LazyOpen(bool create_if_needed) { |
| 95 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); | 99 DCHECK(sequence_checker_.CalledOnValidSequencedThread()); |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 118 env_.reset(leveldb::NewMemEnv(leveldb::Env::Default())); | 122 env_.reset(leveldb::NewMemEnv(leveldb::Env::Default())); |
| 119 options.env = env_.get(); | 123 options.env = env_.get(); |
| 120 } | 124 } |
| 121 | 125 |
| 122 leveldb::DB* db = NULL; | 126 leveldb::DB* db = NULL; |
| 123 leveldb::Status status = | 127 leveldb::Status status = |
| 124 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db); | 128 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db); |
| 125 if (!status.ok()) { | 129 if (!status.ok()) { |
| 126 DCHECK(!db); | 130 DCHECK(!db); |
| 127 // TODO(nhiroki): Should we retry to open the database? | 131 // TODO(nhiroki): Should we retry to open the database? |
| 128 DLOG(ERROR) << "Failed to open LevelDB database: " << status.ToString(); | 132 HandleError(FROM_HERE, status); |
| 129 is_disabled_ = true; | |
| 130 return false; | 133 return false; |
| 131 } | 134 } |
| 132 db_.reset(db); | 135 db_.reset(db); |
| 133 | 136 |
| 134 if (IsEmpty() && !PopulateInitialData()) { | 137 if (IsEmpty() && !PopulateInitialData()) { |
| 135 DLOG(ERROR) << "Failed to populate the database."; | 138 DLOG(ERROR) << "Failed to populate the database."; |
| 136 is_disabled_ = true; | |
| 137 db_.reset(); | 139 db_.reset(); |
| 138 return false; | 140 return false; |
| 139 } | 141 } |
| 140 return true; | 142 return true; |
| 141 } | 143 } |
| 142 | 144 |
| 143 bool ServiceWorkerDatabase::PopulateInitialData() { | 145 bool ServiceWorkerDatabase::PopulateInitialData() { |
| 144 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch); | 146 leveldb::WriteBatch batch; |
| 145 batch->Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion)); | 147 batch.Put(kDatabaseVersionKey, base::Int64ToString(kCurrentSchemaVersion)); |
| 146 batch->Put(kNextRegIdKey, "0"); | 148 batch.Put(kNextRegIdKey, base::Int64ToString(0)); |
| 147 batch->Put(kNextResIdKey, "0"); | 149 batch.Put(kNextResIdKey, base::Int64ToString(0)); |
| 148 batch->Put(kNextVerIdKey, "0"); | 150 batch.Put(kNextVerIdKey, base::Int64ToString(0)); |
| 149 return WriteBatch(batch.Pass()); | 151 return WriteBatch(&batch); |
| 150 } | 152 } |
| 151 | 153 |
| 152 bool ServiceWorkerDatabase::ReadInt64( | 154 bool ServiceWorkerDatabase::ReadNextAvailableId( |
| 153 const leveldb::Slice& key, | 155 const char* id_key, int64* next_avail_id) { |
| 154 int64* value_out) { | 156 DCHECK(id_key); |
| 155 DCHECK(value_out); | 157 DCHECK(next_avail_id); |
| 156 | 158 |
| 157 std::string value; | 159 std::string value; |
| 158 leveldb::Status status = db_->Get(leveldb::ReadOptions(), key, &value); | 160 leveldb::Status status = db_->Get(leveldb::ReadOptions(), id_key, &value); |
| 159 if (!status.ok()) { | 161 if (!status.ok()) { |
| 160 DLOG(ERROR) << "Failed to read data keyed by " | 162 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; | 163 return false; |
| 166 } | 164 } |
| 167 | 165 |
| 168 int64 parsed = -1; | 166 int64 parsed; |
| 169 if (!base::StringToInt64(value, &parsed)) { | 167 if (!base::StringToInt64(value, &parsed)) { |
| 170 DLOG(ERROR) << "Database might be corrupted: " | 168 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; | 169 return false; |
| 175 } | 170 } |
| 176 | 171 |
| 177 *value_out = parsed; | 172 *next_avail_id = parsed; |
| 178 return true; | 173 return true; |
| 179 } | 174 } |
| 180 | 175 |
| 181 bool ServiceWorkerDatabase::WriteBatch(scoped_ptr<leveldb::WriteBatch> batch) { | 176 bool ServiceWorkerDatabase::WriteBatch(leveldb::WriteBatch* batch) { |
| 182 if (!batch) | 177 DCHECK(batch); |
| 183 return true; | 178 DCHECK(!is_disabled_); |
| 184 | 179 leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch); |
| 185 leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch.get()); | 180 if (!status.ok()) { |
| 186 if (status.ok()) | 181 HandleError(FROM_HERE, status); |
| 187 return true; | 182 return false; |
| 188 | 183 } |
| 189 DLOG(ERROR) << "Failed to write the batch: " << status.ToString(); | 184 return true; |
| 190 is_disabled_ = true; | |
| 191 if (status.IsCorruption()) | |
| 192 was_corruption_detected_ = true; | |
| 193 return false; | |
| 194 } | 185 } |
| 195 | 186 |
| 196 bool ServiceWorkerDatabase::IsOpen() { | 187 bool ServiceWorkerDatabase::IsOpen() { |
| 197 return db_.get() != NULL; | 188 return db_.get() != NULL; |
| 198 } | 189 } |
| 199 | 190 |
| 200 bool ServiceWorkerDatabase::IsEmpty() { | 191 bool ServiceWorkerDatabase::IsEmpty() { |
| 201 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); | 192 scoped_ptr<leveldb::Iterator> itr(db_->NewIterator(leveldb::ReadOptions())); |
| 202 itr->SeekToFirst(); | 193 itr->SeekToFirst(); |
| 194 // TODO(nhiroki): Handle an error. | |
| 195 DCHECK(itr->status().ok()); | |
| 203 return !itr->Valid(); | 196 return !itr->Valid(); |
| 204 } | 197 } |
| 205 | 198 |
| 199 void ServiceWorkerDatabase::HandleError( | |
| 200 const tracked_objects::Location& from_here, | |
| 201 const leveldb::Status& status) { | |
| 202 // TODO(nhiroki): Add an UMA histogram. | |
| 203 DLOG(ERROR) << "Failed at: " << from_here.ToString() | |
| 204 << " with error: " << status.ToString(); | |
| 205 is_disabled_ = true; | |
| 206 if (status.IsCorruption()) | |
| 207 was_corruption_detected_ = true; | |
| 208 db_.reset(); | |
|
michaeln
2014/04/25 00:01:36
ok, db is closed when anything goes wrong
| |
| 209 } | |
| 210 | |
| 206 } // namespace content | 211 } // namespace content |
| OLD | NEW |