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 |