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

Side by Side Diff: base/prefs/leveldb_pref_store.cc

Issue 169323003: Implementation of leveldb-backed PrefStore (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: respond to comments 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
(Empty)
1 // Copyright (c) 2012 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.
4
5 #include "base/prefs/leveldb_pref_store.h"
6
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/file_util.h"
10 #include "base/json/json_string_value_serializer.h"
11 #include "base/location.h"
12 #include "base/sequenced_task_runner.h"
13 #include "base/task_runner_util.h"
14 #include "base/threading/thread_restrictions.h"
15 #include "base/time/time.h"
16 #include "base/values.h"
17 #include "third_party/leveldatabase/env_chromium.h"
18 #include "third_party/leveldatabase/src/include/leveldb/db.h"
19 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
20
21 struct LevelDBPrefStore::ReadingResults {
22 bool no_dir;
23 scoped_ptr<leveldb::DB> db;
24 scoped_ptr<PrefValueMap> value_map;
25 PersistentPrefStore::PrefReadError error;
26 };
27
28 // An instance of this class is created on the UI thread but is used
29 // exclusively on the FILE thread.
30 class LevelDBPrefStore::FileThreadSerializer {
31 public:
32 FileThreadSerializer(scoped_ptr<leveldb::DB> db) : db_(db.Pass()) {}
33 void WriteToDatabase(
34 scoped_ptr<std::map<std::string, std::string> > keys_to_set,
35 scoped_ptr<std::set<std::string> > keys_to_delete) {
36 DCHECK(keys_to_set->size() > 0 || keys_to_delete->size() > 0);
37 leveldb::WriteBatch batch;
38 for (std::map<std::string, std::string>::iterator iter =
39 keys_to_set->begin();
40 iter != keys_to_set->end();
41 iter++) {
42 batch.Put(iter->first, iter->second);
43 }
44
45 for (std::set<std::string>::iterator iter = keys_to_delete->begin();
46 iter != keys_to_delete->end();
47 iter++) {
48 batch.Delete(*iter);
49 }
50
51 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
52
53 // DCHECK is fine; the corresponding error is ignored in JsonPrefStore.
54 // There's also no API available to surface the error back up to the caller.
55 // TODO(dgrogan): UMA?
56 DCHECK(status.ok()) << status.ToString();
57 }
58
59 private:
60 scoped_ptr<leveldb::DB> db_;
61 DISALLOW_COPY_AND_ASSIGN(FileThreadSerializer);
62 };
63
64 bool MoveDirectoryAside(const base::FilePath& path) {
65 base::FilePath bad_path = path.AppendASCII(".bad");
66 if (!base::Move(path, bad_path)) {
67 base::DeleteFile(bad_path, true);
68 return false;
69 }
70 return true;
71 }
72
73 /* static */
74 void LevelDBPrefStore::OpenDB(const base::FilePath& path,
75 ReadingResults* reading_results) {
76 leveldb::Options options;
77 options.create_if_missing = true;
78 leveldb::DB* db;
79 leveldb::Status status = leveldb::DB::Open(options, path.AsUTF8Unsafe(), &db);
80 if (status.ok()) {
81 reading_results->db.reset(db);
82 reading_results->error = PREF_READ_ERROR_NONE;
83 return;
84 }
85 if (leveldb_env::IsIOError(status)) {
86 reading_results->error = PREF_READ_ERROR_LEVELDB_IO_ERROR;
87 return;
88 }
89 // If it's not an IO error, it's corruption that we can try to repair.
90 status = leveldb::RepairDB(path.AsUTF8Unsafe(), options);
91 bool destroy_succeeded = false;
92 if (!status.ok()) {
93 if (!MoveDirectoryAside(path)) {
94 status = leveldb::DestroyDB(path.AsUTF8Unsafe(), options);
95 if (!status.ok()) {
96 reading_results->error = PREF_READ_ERROR_LEVELDB_DESTROY_FAILED;
97 return;
98 }
99 }
100 destroy_succeeded = true;
101 }
102 // Either repair or destroy succeeded, now try to open again.
103 status = leveldb::DB::Open(options, path.AsUTF8Unsafe(), &db);
104 if (status.ok()) {
105 reading_results->db.reset(db);
106 if (destroy_succeeded) {
Mattias Nissler (ping if slow) 2014/04/14 10:11:41 nit: curlies not needed or you could convert lines
dgrogan 2014/04/17 01:10:27 Done.
107 reading_results->error = PREF_READ_ERROR_LEVELDB_DESTROYED_REOPENED;
108 } else {
109 reading_results->error = PREF_READ_ERROR_LEVELDB_REPAIRED_REOPENED;
110 }
111 return;
112 }
113 if (destroy_succeeded) {
114 reading_results->error =
115 PREF_READ_ERROR_LEVELDB_DESTROYED_REOPEN_FAILED;
116 return;
117 }
118 status = leveldb::DestroyDB(path.AsUTF8Unsafe(), options);
119 if (!status.ok()) {
120 reading_results->error =
121 PREF_READ_ERROR_LEVELDB_REPAIRED_REOPEN_FAILED_DESTROY_FAILED;
122 return;
123 }
124 status = leveldb::DB::Open(options, path.AsUTF8Unsafe(), &db);
125 if (!status.ok()) {
126 reading_results->error =
127 PREF_READ_ERROR_LEVELDB_REPAIRED_REOPEN_FAILED_DESTROYED_REOPEN_FAILED;
128 return;
129 }
130 reading_results->error =
131 PREF_READ_ERROR_LEVELDB_REPAIRED_REOPEN_FAILED_DESTROYED_REOPENED;
132 reading_results->db.reset(db);
Mattias Nissler (ping if slow) 2014/04/14 10:11:41 Quite some error code proliferation here now, see
dgrogan 2014/04/17 01:10:27 Yeah, I was dismayed at the explosion of errors...
133 }
134
135 /* static */
136 scoped_ptr<LevelDBPrefStore::ReadingResults> LevelDBPrefStore::DoReading(
137 const base::FilePath& path) {
138 base::ThreadRestrictions::AssertIOAllowed();
139
140 scoped_ptr<ReadingResults> reading_results(new ReadingResults);
141 reading_results->error = PREF_READ_ERROR_NONE;
142
143 reading_results->no_dir = !base::PathExists(path.DirName());
144 OpenDB(path, reading_results.get());
145 if (!reading_results->db.get()) {
Mattias Nissler (ping if slow) 2014/04/14 10:11:41 nit: no need for .get() here, scoped_ptr works fin
dgrogan 2014/04/17 01:10:27 Done.
146 DCHECK_NE(PREF_READ_ERROR_NONE, reading_results->error);
147 return reading_results.Pass();
148 }
149
150 reading_results->value_map.reset(new PrefValueMap);
151 scoped_ptr<leveldb::Iterator> it(
152 reading_results->db->NewIterator(leveldb::ReadOptions()));
153 // TODO(dgrogan): Is it really necessary to check it->status() each iteration?
154 for (it->SeekToFirst(); it->Valid() && it->status().ok(); it->Next()) {
155 const std::string value_string = it->value().ToString();
156 JSONStringValueSerializer deserializer(value_string);
157 std::string error_message;
158 int error_code;
159 base::Value* json_value =
160 deserializer.Deserialize(&error_code, &error_message);
161 if (json_value) {
162 reading_results->value_map->SetValue(it->key().ToString(), json_value);
163 } else {
164 DLOG(ERROR) << "Invalid json for key " << it->key().ToString()
165 << ": " << error_message;
166 if (reading_results->error == PREF_READ_ERROR_LEVELDB_REPAIRED_REOPENED ||
167 reading_results->error ==
168 PREF_READ_ERROR_LEVELDB_SOME_DATA_LOST_AFTER_REPAIR) {
169 reading_results->error =
170 PREF_READ_ERROR_LEVELDB_SOME_DATA_LOST_AFTER_REPAIR;
171 } else {
172 reading_results->error = PREF_READ_ERROR_LEVELDB_SOME_DATA_LOST;
173 }
174 }
175 }
176
177 if (!it->status().ok()) {
178 reading_results->error = PREF_READ_ERROR_LEVELDB_ITERATOR_STATUS_NOT_OK;
179 return reading_results.Pass();
180 }
181 return reading_results.Pass();
182 }
183
184 LevelDBPrefStore::LevelDBPrefStore(
185 const base::FilePath& filename,
186 base::SequencedTaskRunner* sequenced_task_runner)
187 : path_(filename),
188 sequenced_task_runner_(sequenced_task_runner),
189 original_task_runner_(base::MessageLoopProxy::current()),
190 read_only_(false),
191 initialized_(false),
192 read_error_(PREF_READ_ERROR_OTHER),
193 weak_ptr_factory_(this) {}
194
195 LevelDBPrefStore::~LevelDBPrefStore() {
196 CommitPendingWrite();
197 sequenced_task_runner_->DeleteSoon(FROM_HERE, serializer_.release());
198 }
199
200 bool LevelDBPrefStore::GetValue(const std::string& key,
201 const base::Value** result) const {
202 DCHECK(initialized_);
203 const base::Value* tmp = NULL;
204 if (!prefs_.GetValue(key, &tmp)) {
205 return false;
206 }
207
208 if (result)
209 *result = tmp;
210 return true;
211 }
212
213 // Callers of GetMutableValue have to also call ReportValueChanged.
214 bool LevelDBPrefStore::GetMutableValue(const std::string& key,
215 base::Value** result) {
216 DCHECK(initialized_);
217 return prefs_.GetValue(key, result);
218 }
219
220 void LevelDBPrefStore::AddObserver(PrefStore::Observer* observer) {
221 observers_.AddObserver(observer);
222 }
223
224 void LevelDBPrefStore::RemoveObserver(PrefStore::Observer* observer) {
225 observers_.RemoveObserver(observer);
226 }
227
228 bool LevelDBPrefStore::HasObservers() const {
229 return observers_.might_have_observers();
230 }
231
232 bool LevelDBPrefStore::IsInitializationComplete() const { return initialized_; }
233
234 void LevelDBPrefStore::PersistFromUIThread() {
235 if (read_only_)
236 return;
237 DCHECK(serializer_);
238
239 scoped_ptr<std::set<std::string>> keys_to_delete(new std::set<std::string>);
Mattias Nissler (ping if slow) 2014/04/14 10:11:41 nit: >> is C++11, which I don't think is allowed i
dgrogan 2014/04/17 01:10:27 Done.
240 keys_to_delete->swap(keys_to_delete_);
241
242 scoped_ptr<std::map<std::string, std::string> > keys_to_set(
243 new std::map<std::string, std::string>);
244 keys_to_set->swap(keys_to_set_);
245
246 sequenced_task_runner_->PostTask(
247 FROM_HERE,
248 base::Bind(&LevelDBPrefStore::FileThreadSerializer::WriteToDatabase,
249 base::Unretained(serializer_.get()),
250 base::Passed(&keys_to_set),
Mattias Nissler (ping if slow) 2014/04/14 10:11:41 nit: Could use base::Owned() here and avoid the sc
dgrogan 2014/04/17 01:10:27 Done.
251 base::Passed(&keys_to_delete)));
252 }
253
254 void LevelDBPrefStore::ScheduleWrite() {
255 if (!timer_.IsRunning()) {
256 timer_.Start(FROM_HERE,
257 base::TimeDelta::FromSeconds(10),
258 this,
259 &LevelDBPrefStore::PersistFromUIThread);
260 }
261 }
262
263 void LevelDBPrefStore::SetValue(const std::string& key, base::Value* value) {
264 SetValueInternal(key, value, true /*notify*/);
265 }
266
267 void LevelDBPrefStore::SetValueSilently(const std::string& key,
268 base::Value* value) {
269 SetValueInternal(key, value, false /*notify*/);
270 }
271
272 static std::string Serialize(base::Value* value) {
273 std::string value_string;
274 JSONStringValueSerializer serializer(&value_string);
275 bool serialized_ok = serializer.Serialize(*value);
276 DCHECK(serialized_ok);
277 return value_string;
278 }
279
280 void LevelDBPrefStore::SetValueInternal(const std::string& key,
281 base::Value* value,
282 bool notify) {
283 DCHECK(initialized_);
284 DCHECK(value);
285 scoped_ptr<base::Value> new_value(value);
286 base::Value* old_value = NULL;
287 prefs_.GetValue(key, &old_value);
288 if (!old_value || !value->Equals(old_value)) {
289 std::string value_string = Serialize(value);
290 prefs_.SetValue(key, new_value.release());
291 MarkForInsertion(key, value_string);
292 if (notify)
293 NotifyObservers(key);
294 }
295 }
296
297 void LevelDBPrefStore::RemoveValue(const std::string& key) {
298 DCHECK(initialized_);
299 if (prefs_.RemoveValue(key)) {
300 MarkForDeletion(key);
301 NotifyObservers(key);
302 }
303 }
304
305 bool LevelDBPrefStore::ReadOnly() const { return read_only_; }
306
307 PersistentPrefStore::PrefReadError LevelDBPrefStore::GetReadError() const {
308 return read_error_;
309 }
310
311 PersistentPrefStore::PrefReadError LevelDBPrefStore::ReadPrefs() {
312 DCHECK(!initialized_);
313 if (path_.empty()) {
314 scoped_ptr<LevelDBPrefStore::ReadingResults> reading_results(
315 new LevelDBPrefStore::ReadingResults);
316 reading_results->error = PREF_READ_ERROR_FILE_NOT_SPECIFIED;
317 OnStorageRead(reading_results.Pass());
318 return PREF_READ_ERROR_FILE_NOT_SPECIFIED;
319 }
320
321 scoped_ptr<LevelDBPrefStore::ReadingResults> reading_results =
322 DoReading(path_);
323 PersistentPrefStore::PrefReadError error = reading_results->error;
324 OnStorageRead(reading_results.Pass());
325 return error;
326 }
327
328 void LevelDBPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
329 DCHECK_EQ(false, initialized_);
330 error_delegate_.reset(error_delegate);
331 if (path_.empty()) {
332 scoped_ptr<LevelDBPrefStore::ReadingResults> reading_results(
333 new LevelDBPrefStore::ReadingResults);
334 reading_results->error = PREF_READ_ERROR_FILE_NOT_SPECIFIED;
335 OnStorageRead(reading_results.Pass());
336 }
337 PostTaskAndReplyWithResult(sequenced_task_runner_,
338 FROM_HERE,
339 base::Bind(&LevelDBPrefStore::DoReading, path_),
340 base::Bind(&LevelDBPrefStore::OnStorageRead,
341 weak_ptr_factory_.GetWeakPtr()));
342 }
343
344 void LevelDBPrefStore::CommitPendingWrite() {
345 if (timer_.IsRunning()) {
346 timer_.Stop();
347 PersistFromUIThread();
348 }
349 }
350
351 void LevelDBPrefStore::MarkForDeletion(const std::string& key) {
352 if (read_only_)
353 return;
354 keys_to_delete_.insert(key);
355 // Need to erase in case there's a set operation in the same batch that would
356 // clobber this delete.
357 keys_to_set_.erase(key);
358 ScheduleWrite();
359 }
360
361 void LevelDBPrefStore::MarkForInsertion(const std::string& key,
362 const std::string& value) {
363 if (read_only_)
364 return;
365 keys_to_set_[key] = value;
366 // Need to erase in case there's a delete operation in the same batch that
367 // would clobber this set.
368 keys_to_delete_.erase(key);
369 ScheduleWrite();
370 }
371
372 void LevelDBPrefStore::ReportValueChanged(const std::string& key) {
373 base::Value* new_value = NULL;
374 DCHECK(prefs_.GetValue(key, &new_value));
375 std::string value_string = Serialize(new_value);
376 MarkForInsertion(key, value_string);
377 NotifyObservers(key);
378 }
379
380 void LevelDBPrefStore::NotifyObservers(const std::string& key) {
381 FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
382 }
383
384 void LevelDBPrefStore::OnStorageRead(
385 scoped_ptr<LevelDBPrefStore::ReadingResults> reading_results) {
386 read_error_ = reading_results->error;
387
388 if (reading_results->no_dir) {
389 FOR_EACH_OBSERVER(
390 PrefStore::Observer, observers_, OnInitializationCompleted(false));
391 return;
392 }
393
394 initialized_ = true;
395
396 switch (read_error_) {
397 case PREF_READ_ERROR_FILE_NOT_SPECIFIED:
398 case PREF_READ_ERROR_LEVELDB_IO_ERROR:
399 case PREF_READ_ERROR_LEVELDB_DESTROY_FAILED:
400 case PREF_READ_ERROR_LEVELDB_DESTROYED_REOPEN_FAILED:
401 case PREF_READ_ERROR_LEVELDB_REPAIRED_REOPEN_FAILED_DESTROY_FAILED:
402 case PREF_READ_ERROR_LEVELDB_REPAIRED_REOPEN_FAILED_DESTROYED_REOPEN_FAILED:
403 DCHECK(reading_results->value_map.get() == NULL);
404 DCHECK(reading_results->db.get() == NULL);
405 read_only_ = true;
406 break;
407 case PREF_READ_ERROR_NONE:
408 case PREF_READ_ERROR_LEVELDB_DESTROYED_REOPENED:
409 case PREF_READ_ERROR_LEVELDB_REPAIRED_REOPENED:
410 case PREF_READ_ERROR_LEVELDB_REPAIRED_REOPEN_FAILED_DESTROYED_REOPENED:
411 case PREF_READ_ERROR_LEVELDB_SOME_DATA_LOST:
412 case PREF_READ_ERROR_LEVELDB_SOME_DATA_LOST_AFTER_REPAIR:
Mattias Nissler (ping if slow) 2014/04/14 10:11:41 The error codes here seem like they'd be better se
dgrogan 2014/04/17 01:10:27 I'd be worried about losing information about whic
Mattias Nissler (ping if slow) 2014/04/17 08:50:50 If you just binary-or flags and send the result to
413 case PREF_READ_ERROR_LEVELDB_ITERATOR_STATUS_NOT_OK:
414 serializer_.reset(new FileThreadSerializer(reading_results->db.Pass()));
415 prefs_.Swap(reading_results->value_map.get());
416 break;
417 default:
418 NOTREACHED() << "Unknown error: " << read_error_;
419 }
420
421 // TODO(dgrogan): Call pref_filter_->FilterOnLoad
422
423 if (error_delegate_.get() && read_error_ != PREF_READ_ERROR_NONE)
424 error_delegate_->OnError(read_error_);
425
426 FOR_EACH_OBSERVER(
427 PrefStore::Observer, observers_, OnInitializationCompleted(true));
428 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698