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

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 std::map<std::string, std::string>* keys_to_set,
35 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)
107 reading_results->error = PREF_READ_ERROR_LEVELDB_DESTROYED_REOPENED;
108 else
109 reading_results->error = PREF_READ_ERROR_LEVELDB_REPAIRED_REOPENED;
110 return;
111 }
112 if (destroy_succeeded) {
113 reading_results->error =
114 PREF_READ_ERROR_LEVELDB_DESTROYED_REOPEN_FAILED;
115 return;
116 }
117 status = leveldb::DestroyDB(path.AsUTF8Unsafe(), options);
118 if (!status.ok()) {
119 reading_results->error =
120 PREF_READ_ERROR_LEVELDB_REPAIRED_REOPEN_FAILED_DESTROY_FAILED;
121 return;
122 }
123 status = leveldb::DB::Open(options, path.AsUTF8Unsafe(), &db);
124 if (!status.ok()) {
125 reading_results->error =
126 PREF_READ_ERROR_LEVELDB_REPAIRED_REOPEN_FAILED_DESTROYED_REOPEN_FAILED;
127 return;
128 }
129 reading_results->error =
130 PREF_READ_ERROR_LEVELDB_REPAIRED_REOPEN_FAILED_DESTROYED_REOPENED;
131 reading_results->db.reset(db);
132 }
133
134 /* static */
135 scoped_ptr<LevelDBPrefStore::ReadingResults> LevelDBPrefStore::DoReading(
136 const base::FilePath& path) {
137 base::ThreadRestrictions::AssertIOAllowed();
138
139 scoped_ptr<ReadingResults> reading_results(new ReadingResults);
140 reading_results->error = PREF_READ_ERROR_NONE;
141
142 reading_results->no_dir = !base::PathExists(path.DirName());
143 OpenDB(path, reading_results.get());
144 if (!reading_results->db) {
145 DCHECK_NE(PREF_READ_ERROR_NONE, reading_results->error);
146 return reading_results.Pass();
147 }
148
149 reading_results->value_map.reset(new PrefValueMap);
150 scoped_ptr<leveldb::Iterator> it(
151 reading_results->db->NewIterator(leveldb::ReadOptions()));
152 // TODO(dgrogan): Is it really necessary to check it->status() each iteration?
153 for (it->SeekToFirst(); it->Valid() && it->status().ok(); it->Next()) {
154 const std::string value_string = it->value().ToString();
155 JSONStringValueSerializer deserializer(value_string);
156 std::string error_message;
157 int error_code;
158 base::Value* json_value =
159 deserializer.Deserialize(&error_code, &error_message);
160 if (json_value) {
161 reading_results->value_map->SetValue(it->key().ToString(), json_value);
162 } else {
163 DLOG(ERROR) << "Invalid json for key " << it->key().ToString()
164 << ": " << error_message;
165 if (reading_results->error == PREF_READ_ERROR_LEVELDB_REPAIRED_REOPENED ||
166 reading_results->error ==
167 PREF_READ_ERROR_LEVELDB_SOME_DATA_LOST_AFTER_REPAIR) {
168 reading_results->error =
169 PREF_READ_ERROR_LEVELDB_SOME_DATA_LOST_AFTER_REPAIR;
170 } else {
171 reading_results->error = PREF_READ_ERROR_LEVELDB_SOME_DATA_LOST;
172 }
173 }
174 }
175
176 if (!it->status().ok()) {
177 reading_results->error = PREF_READ_ERROR_LEVELDB_ITERATOR_STATUS_NOT_OK;
178 return reading_results.Pass();
179 }
180 return reading_results.Pass();
181 }
182
183 LevelDBPrefStore::LevelDBPrefStore(
184 const base::FilePath& filename,
185 base::SequencedTaskRunner* sequenced_task_runner)
186 : path_(filename),
187 sequenced_task_runner_(sequenced_task_runner),
188 original_task_runner_(base::MessageLoopProxy::current()),
189 read_only_(false),
190 initialized_(false),
191 read_error_(PREF_READ_ERROR_OTHER),
192 weak_ptr_factory_(this) {}
193
194 LevelDBPrefStore::~LevelDBPrefStore() {
195 CommitPendingWrite();
196 sequenced_task_runner_->DeleteSoon(FROM_HERE, serializer_.release());
197 }
198
199 bool LevelDBPrefStore::GetValue(const std::string& key,
200 const base::Value** result) const {
201 DCHECK(initialized_);
202 const base::Value* tmp = NULL;
203 if (!prefs_.GetValue(key, &tmp)) {
204 return false;
205 }
206
207 if (result)
208 *result = tmp;
209 return true;
210 }
211
212 // Callers of GetMutableValue have to also call ReportValueChanged.
213 bool LevelDBPrefStore::GetMutableValue(const std::string& key,
214 base::Value** result) {
215 DCHECK(initialized_);
216 return prefs_.GetValue(key, result);
217 }
218
219 void LevelDBPrefStore::AddObserver(PrefStore::Observer* observer) {
220 observers_.AddObserver(observer);
221 }
222
223 void LevelDBPrefStore::RemoveObserver(PrefStore::Observer* observer) {
224 observers_.RemoveObserver(observer);
225 }
226
227 bool LevelDBPrefStore::HasObservers() const {
228 return observers_.might_have_observers();
229 }
230
231 bool LevelDBPrefStore::IsInitializationComplete() const { return initialized_; }
232
233 void LevelDBPrefStore::PersistFromUIThread() {
234 if (read_only_)
235 return;
236 DCHECK(serializer_);
237
238 scoped_ptr<std::set<std::string> > keys_to_delete(new std::set<std::string>);
239 keys_to_delete->swap(keys_to_delete_);
240
241 scoped_ptr<std::map<std::string, std::string> > keys_to_set(
242 new std::map<std::string, std::string>);
243 keys_to_set->swap(keys_to_set_);
244
245 sequenced_task_runner_->PostTask(
246 FROM_HERE,
247 base::Bind(&LevelDBPrefStore::FileThreadSerializer::WriteToDatabase,
248 base::Unretained(serializer_.get()),
249 base::Owned(keys_to_set.release()),
250 base::Owned(keys_to_delete.release())));
251 }
252
253 void LevelDBPrefStore::ScheduleWrite() {
254 if (!timer_.IsRunning()) {
255 timer_.Start(FROM_HERE,
256 base::TimeDelta::FromSeconds(10),
257 this,
258 &LevelDBPrefStore::PersistFromUIThread);
259 }
260 }
261
262 void LevelDBPrefStore::SetValue(const std::string& key, base::Value* value) {
263 SetValueInternal(key, value, true /*notify*/);
264 }
265
266 void LevelDBPrefStore::SetValueSilently(const std::string& key,
267 base::Value* value) {
268 SetValueInternal(key, value, false /*notify*/);
269 }
270
271 static std::string Serialize(base::Value* value) {
272 std::string value_string;
273 JSONStringValueSerializer serializer(&value_string);
274 bool serialized_ok = serializer.Serialize(*value);
275 DCHECK(serialized_ok);
276 return value_string;
277 }
278
279 void LevelDBPrefStore::SetValueInternal(const std::string& key,
280 base::Value* value,
281 bool notify) {
282 DCHECK(initialized_);
283 DCHECK(value);
284 scoped_ptr<base::Value> new_value(value);
285 base::Value* old_value = NULL;
286 prefs_.GetValue(key, &old_value);
287 if (!old_value || !value->Equals(old_value)) {
288 std::string value_string = Serialize(value);
289 prefs_.SetValue(key, new_value.release());
290 MarkForInsertion(key, value_string);
291 if (notify)
292 NotifyObservers(key);
293 }
294 }
295
296 void LevelDBPrefStore::RemoveValue(const std::string& key) {
297 DCHECK(initialized_);
298 if (prefs_.RemoveValue(key)) {
299 MarkForDeletion(key);
300 NotifyObservers(key);
301 }
302 }
303
304 bool LevelDBPrefStore::ReadOnly() const { return read_only_; }
305
306 PersistentPrefStore::PrefReadError LevelDBPrefStore::GetReadError() const {
307 return read_error_;
308 }
309
310 PersistentPrefStore::PrefReadError LevelDBPrefStore::ReadPrefs() {
311 DCHECK(!initialized_);
312 if (path_.empty()) {
313 scoped_ptr<LevelDBPrefStore::ReadingResults> reading_results(
314 new LevelDBPrefStore::ReadingResults);
315 reading_results->error = PREF_READ_ERROR_FILE_NOT_SPECIFIED;
316 OnStorageRead(reading_results.Pass());
317 return PREF_READ_ERROR_FILE_NOT_SPECIFIED;
318 }
319
320 scoped_ptr<LevelDBPrefStore::ReadingResults> reading_results =
321 DoReading(path_);
322 PersistentPrefStore::PrefReadError error = reading_results->error;
323 OnStorageRead(reading_results.Pass());
324 return error;
325 }
326
327 void LevelDBPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
328 DCHECK_EQ(false, initialized_);
329 error_delegate_.reset(error_delegate);
330 if (path_.empty()) {
331 scoped_ptr<LevelDBPrefStore::ReadingResults> reading_results(
332 new LevelDBPrefStore::ReadingResults);
333 reading_results->error = PREF_READ_ERROR_FILE_NOT_SPECIFIED;
334 OnStorageRead(reading_results.Pass());
335 }
336 PostTaskAndReplyWithResult(sequenced_task_runner_,
337 FROM_HERE,
338 base::Bind(&LevelDBPrefStore::DoReading, path_),
339 base::Bind(&LevelDBPrefStore::OnStorageRead,
340 weak_ptr_factory_.GetWeakPtr()));
341 }
342
343 void LevelDBPrefStore::CommitPendingWrite() {
344 if (timer_.IsRunning()) {
345 timer_.Stop();
346 PersistFromUIThread();
347 }
348 }
349
350 void LevelDBPrefStore::MarkForDeletion(const std::string& key) {
351 if (read_only_)
352 return;
353 keys_to_delete_.insert(key);
354 // Need to erase in case there's a set operation in the same batch that would
355 // clobber this delete.
356 keys_to_set_.erase(key);
357 ScheduleWrite();
358 }
359
360 void LevelDBPrefStore::MarkForInsertion(const std::string& key,
361 const std::string& value) {
362 if (read_only_)
363 return;
364 keys_to_set_[key] = value;
365 // Need to erase in case there's a delete operation in the same batch that
366 // would clobber this set.
367 keys_to_delete_.erase(key);
368 ScheduleWrite();
369 }
370
371 void LevelDBPrefStore::ReportValueChanged(const std::string& key) {
372 base::Value* new_value = NULL;
373 DCHECK(prefs_.GetValue(key, &new_value));
374 std::string value_string = Serialize(new_value);
375 MarkForInsertion(key, value_string);
376 NotifyObservers(key);
377 }
378
379 void LevelDBPrefStore::NotifyObservers(const std::string& key) {
380 FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
381 }
382
383 void LevelDBPrefStore::OnStorageRead(
384 scoped_ptr<LevelDBPrefStore::ReadingResults> reading_results) {
385 read_error_ = reading_results->error;
386
387 if (reading_results->no_dir) {
388 FOR_EACH_OBSERVER(
389 PrefStore::Observer, observers_, OnInitializationCompleted(false));
390 return;
391 }
392
393 initialized_ = true;
394
395 switch (read_error_) {
396 case PREF_READ_ERROR_FILE_NOT_SPECIFIED:
397 case PREF_READ_ERROR_LEVELDB_IO_ERROR:
398 case PREF_READ_ERROR_LEVELDB_DESTROY_FAILED:
399 case PREF_READ_ERROR_LEVELDB_DESTROYED_REOPEN_FAILED:
400 case PREF_READ_ERROR_LEVELDB_REPAIRED_REOPEN_FAILED_DESTROY_FAILED:
401 case PREF_READ_ERROR_LEVELDB_REPAIRED_REOPEN_FAILED_DESTROYED_REOPEN_FAILED:
402 DCHECK(reading_results->value_map.get() == NULL);
403 DCHECK(reading_results->db.get() == NULL);
404 read_only_ = true;
405 break;
406 case PREF_READ_ERROR_NONE:
407 case PREF_READ_ERROR_LEVELDB_DESTROYED_REOPENED:
408 case PREF_READ_ERROR_LEVELDB_REPAIRED_REOPENED:
409 case PREF_READ_ERROR_LEVELDB_REPAIRED_REOPEN_FAILED_DESTROYED_REOPENED:
410 case PREF_READ_ERROR_LEVELDB_SOME_DATA_LOST:
411 case PREF_READ_ERROR_LEVELDB_SOME_DATA_LOST_AFTER_REPAIR:
412 case PREF_READ_ERROR_LEVELDB_ITERATOR_STATUS_NOT_OK:
413 serializer_.reset(new FileThreadSerializer(reading_results->db.Pass()));
414 prefs_.Swap(reading_results->value_map.get());
415 break;
416 default:
417 NOTREACHED() << "Unknown error: " << read_error_;
418 }
419
420 // TODO(dgrogan): Call pref_filter_->FilterOnLoad
421
422 if (error_delegate_.get() && read_error_ != PREF_READ_ERROR_NONE)
423 error_delegate_->OnError(read_error_);
424
425 FOR_EACH_OBSERVER(
426 PrefStore::Observer, observers_, OnInitializationCompleted(true));
427 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698