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

Side by Side Diff: content/browser/indexed_db/leveldb/leveldb_database.cc

Issue 15564008: Migrate the IndexedDB backend from Blink to Chromium (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Update to ToT again Created 7 years, 6 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) 2013 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 "content/browser/indexed_db/leveldb/leveldb_database.h"
6
7 #include <string>
8
9 #include "base/basictypes.h"
10 #include "base/logging.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/metrics/histogram.h"
13 #include "base/string16.h"
14 #include "base/sys_info.h"
15 #include "base/utf_string_conversions.h"
16 #include "content/browser/indexed_db/leveldb/leveldb_comparator.h"
17 #include "content/browser/indexed_db/leveldb/leveldb_iterator.h"
18 #include "content/browser/indexed_db/leveldb/leveldb_slice.h"
19 #include "content/browser/indexed_db/leveldb/leveldb_write_batch.h"
20 #include "third_party/leveldatabase/env_idb.h"
21 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
22 #include "third_party/leveldatabase/src/include/leveldb/comparator.h"
23 #include "third_party/leveldatabase/src/include/leveldb/db.h"
24 #include "third_party/leveldatabase/src/include/leveldb/env.h"
25 #include "third_party/leveldatabase/src/include/leveldb/slice.h"
26
27 namespace content {
28
29 static leveldb::Slice MakeSlice(const std::vector<char>& value) {
30 DCHECK_GT(value.size(), static_cast<size_t>(0));
31 return leveldb::Slice(&*value.begin(), value.size());
32 }
33
34 static leveldb::Slice MakeSlice(const LevelDBSlice& s) {
35 return leveldb::Slice(s.begin(), s.end() - s.begin());
36 }
37
38 static LevelDBSlice MakeLevelDBSlice(const leveldb::Slice& s) {
39 return LevelDBSlice(s.data(), s.data() + s.size());
40 }
41
42 class ComparatorAdapter : public leveldb::Comparator {
43 public:
44 explicit ComparatorAdapter(const LevelDBComparator* comparator)
45 : comparator_(comparator) {}
46
47 virtual int Compare(const leveldb::Slice& a, const leveldb::Slice& b) const
48 OVERRIDE {
49 return comparator_->Compare(MakeLevelDBSlice(a), MakeLevelDBSlice(b));
50 }
51
52 virtual const char* Name() const OVERRIDE { return comparator_->Name(); }
53
54 // TODO(jsbell): Support the methods below in the future.
55 virtual void FindShortestSeparator(std::string* start,
56 const leveldb::Slice& limit) const
57 OVERRIDE {}
58 virtual void FindShortSuccessor(std::string* key) const OVERRIDE {}
59
60 private:
61 const LevelDBComparator* comparator_;
62 };
63
64 LevelDBSnapshot::LevelDBSnapshot(LevelDBDatabase* db)
65 : db_(db->db_.get()), snapshot_(db_->GetSnapshot()) {}
66
67 LevelDBSnapshot::~LevelDBSnapshot() { db_->ReleaseSnapshot(snapshot_); }
68
69 LevelDBDatabase::LevelDBDatabase() {}
70
71 LevelDBDatabase::~LevelDBDatabase() {
72 // db_'s destructor uses comparator_adapter_; order of deletion is important.
73 db_.reset();
74 comparator_adapter_.reset();
75 env_.reset();
76 }
77
78 static leveldb::Status OpenDB(leveldb::Comparator* comparator,
79 leveldb::Env* env,
80 const base::FilePath& path,
81 leveldb::DB** db) {
82 leveldb::Options options;
83 options.comparator = comparator;
84 options.create_if_missing = true;
85 options.paranoid_checks = true;
86
87 // Marking compression as explicitly off so snappy support can be
88 // compiled in for other leveldb clients without implicitly enabling
89 // it for IndexedDB. http://crbug.com/81384
90 options.compression = leveldb::kNoCompression;
91
92 // 20 max_open_files is the minimum LevelDB allows but its cache behaves
93 // poorly with less than 4 files per shard. As of this writing the latest
94 // leveldb (1.9) hardcodes 16 shards. See
95 // https://code.google.com/p/chromium/issues/detail?id=227313#c11
96 options.max_open_files = 80;
97 options.env = env;
98
99 // ChromiumEnv assumes UTF8, converts back to FilePath before using.
100 return leveldb::DB::Open(options, path.AsUTF8Unsafe(), db);
101 }
102
103 bool LevelDBDatabase::Destroy(const base::FilePath& file_name) {
104 leveldb::Options options;
105 options.env = leveldb::IDBEnv();
106 // ChromiumEnv assumes UTF8, converts back to FilePath before using.
107 const leveldb::Status s =
108 leveldb::DestroyDB(file_name.AsUTF8Unsafe(), options);
109 return s.ok();
110 }
111
112 static void HistogramFreeSpace(const char* type,
113 const base::FilePath& file_name) {
114 string16 name = ASCIIToUTF16("WebCore.IndexedDB.LevelDB.Open") +
115 ASCIIToUTF16(type) + ASCIIToUTF16("FreeDiskSpace");
116 int64 free_disk_space_in_k_bytes =
117 base::SysInfo::AmountOfFreeDiskSpace(file_name) / 1024;
118 if (free_disk_space_in_k_bytes < 0) {
119 base::Histogram::FactoryGet(
120 "WebCore.IndexedDB.LevelDB.FreeDiskSpaceFailure",
121 1,
122 2 /*boundary*/,
123 2 /*boundary*/ + 1,
124 base::HistogramBase::kUmaTargetedHistogramFlag)->Add(1 /*sample*/);
125 return;
126 }
127 int clamped_disk_space_k_bytes =
128 free_disk_space_in_k_bytes > INT_MAX ? INT_MAX
129 : free_disk_space_in_k_bytes;
130 const uint64 histogram_max = static_cast<uint64>(1e9);
131 COMPILE_ASSERT(histogram_max <= INT_MAX, histogram_max_too_big);
132 base::Histogram::FactoryGet(UTF16ToUTF8(name),
133 1,
134 histogram_max,
135 11 /*buckets*/,
136 base::HistogramBase::kUmaTargetedHistogramFlag)
137 ->Add(clamped_disk_space_k_bytes);
138 }
139
140 static void HistogramLevelDBError(const char* histogram_name,
141 const leveldb::Status& s) {
142 DCHECK(!s.ok());
143 enum {
144 LEVEL_DB_NOT_FOUND,
145 LEVEL_DB_CORRUPTION,
146 LEVEL_DB_IO_ERROR,
147 LEVEL_DB_OTHER,
148 LEVEL_DB_MAX_ERROR
149 };
150 int leveldb_error = LEVEL_DB_OTHER;
151 if (s.IsNotFound())
152 leveldb_error = LEVEL_DB_NOT_FOUND;
153 else if (s.IsCorruption())
154 leveldb_error = LEVEL_DB_CORRUPTION;
155 else if (s.IsIOError())
156 leveldb_error = LEVEL_DB_IO_ERROR;
157 base::Histogram::FactoryGet(histogram_name,
158 1,
159 LEVEL_DB_MAX_ERROR,
160 LEVEL_DB_MAX_ERROR + 1,
161 base::HistogramBase::kUmaTargetedHistogramFlag)
162 ->Add(leveldb_error);
163 }
164
165 scoped_ptr<LevelDBDatabase> LevelDBDatabase::Open(
166 const base::FilePath& file_name,
167 const LevelDBComparator* comparator) {
168 scoped_ptr<ComparatorAdapter> comparator_adapter(
169 new ComparatorAdapter(comparator));
170
171 leveldb::DB* db;
172 const leveldb::Status s =
173 OpenDB(comparator_adapter.get(), leveldb::IDBEnv(), file_name, &db);
174
175 if (!s.ok()) {
176 HistogramLevelDBError("WebCore.IndexedDB.LevelDBOpenErrors", s);
177 HistogramFreeSpace("Failure", file_name);
178
179 LOG(ERROR) << "Failed to open LevelDB database from "
180 << file_name.AsUTF8Unsafe() << "," << s.ToString();
181 return scoped_ptr<LevelDBDatabase>();
182 }
183
184 HistogramFreeSpace("Success", file_name);
185
186 scoped_ptr<LevelDBDatabase> result(new LevelDBDatabase);
187 result->db_ = make_scoped_ptr(db);
188 result->comparator_adapter_ = comparator_adapter.Pass();
189 result->comparator_ = comparator;
190
191 return result.Pass();
192 }
193
194 scoped_ptr<LevelDBDatabase> LevelDBDatabase::OpenInMemory(
195 const LevelDBComparator* comparator) {
196 scoped_ptr<ComparatorAdapter> comparator_adapter(
197 new ComparatorAdapter(comparator));
198 scoped_ptr<leveldb::Env> in_memory_env(leveldb::NewMemEnv(leveldb::IDBEnv()));
199
200 leveldb::DB* db;
201 const leveldb::Status s = OpenDB(
202 comparator_adapter.get(), in_memory_env.get(), base::FilePath(), &db);
203
204 if (!s.ok()) {
205 LOG(ERROR) << "Failed to open in-memory LevelDB database: " << s.ToString();
206 return scoped_ptr<LevelDBDatabase>();
207 }
208
209 scoped_ptr<LevelDBDatabase> result(new LevelDBDatabase);
210 result->env_ = in_memory_env.Pass();
211 result->db_ = make_scoped_ptr(db);
212 result->comparator_adapter_ = comparator_adapter.Pass();
213 result->comparator_ = comparator;
214
215 return result.Pass();
216 }
217
218 bool LevelDBDatabase::Put(const LevelDBSlice& key,
219 const std::vector<char>& value) {
220 leveldb::WriteOptions write_options;
221 write_options.sync = true;
222
223 const leveldb::Status s =
224 db_->Put(write_options, MakeSlice(key), MakeSlice(value));
225 if (s.ok())
226 return true;
227 LOG(ERROR) << "LevelDB put failed: " << s.ToString();
228 return false;
229 }
230
231 bool LevelDBDatabase::Remove(const LevelDBSlice& key) {
232 leveldb::WriteOptions write_options;
233 write_options.sync = true;
234
235 const leveldb::Status s = db_->Delete(write_options, MakeSlice(key));
236 if (s.ok())
237 return true;
238 if (s.IsNotFound())
239 return false;
240 LOG(ERROR) << "LevelDB remove failed: " << s.ToString();
241 return false;
242 }
243
244 bool LevelDBDatabase::Get(const LevelDBSlice& key,
245 std::vector<char>& value,
246 bool& found,
247 const LevelDBSnapshot* snapshot) {
248 found = false;
249 std::string result;
250 leveldb::ReadOptions read_options;
251 read_options.verify_checksums = true; // TODO(jsbell): Disable this if the
252 // performance impact is too great.
253 read_options.snapshot = snapshot ? snapshot->snapshot_ : 0;
254
255 const leveldb::Status s = db_->Get(read_options, MakeSlice(key), &result);
256 if (s.ok()) {
257 found = true;
258 value.clear();
259 value.insert(value.end(), result.begin(), result.end());
260 return true;
261 }
262 if (s.IsNotFound())
263 return true;
264 LOG(ERROR) << "LevelDB get failed: " << s.ToString();
265 return false;
266 }
267
268 bool LevelDBDatabase::Write(LevelDBWriteBatch& write_batch) {
269 leveldb::WriteOptions write_options;
270 write_options.sync = true;
271
272 const leveldb::Status s =
273 db_->Write(write_options, write_batch.write_batch_.get());
274 if (s.ok())
275 return true;
276 HistogramLevelDBError("WebCore.IndexedDB.LevelDBWriteErrors", s);
277 LOG(ERROR) << "LevelDB write failed: " << s.ToString();
278 return false;
279 }
280
281 namespace {
282 class IteratorImpl : public LevelDBIterator {
283 public:
284 virtual ~IteratorImpl() {}
285
286 virtual bool IsValid() const OVERRIDE;
287 virtual void SeekToLast() OVERRIDE;
288 virtual void Seek(const LevelDBSlice& target) OVERRIDE;
289 virtual void Next() OVERRIDE;
290 virtual void Prev() OVERRIDE;
291 virtual LevelDBSlice Key() const OVERRIDE;
292 virtual LevelDBSlice Value() const OVERRIDE;
293
294 private:
295 friend class content::LevelDBDatabase;
296 IteratorImpl(scoped_ptr<leveldb::Iterator> iterator);
297 void CheckStatus();
298
299 scoped_ptr<leveldb::Iterator> iterator_;
300 };
301 }
302
303 IteratorImpl::IteratorImpl(scoped_ptr<leveldb::Iterator> it)
304 : iterator_(it.Pass()) {}
305
306 void IteratorImpl::CheckStatus() {
307 const leveldb::Status s = iterator_->status();
308 if (!s.ok())
309 LOG(ERROR) << "LevelDB iterator error: " << s.ToString();
310 }
311
312 bool IteratorImpl::IsValid() const { return iterator_->Valid(); }
313
314 void IteratorImpl::SeekToLast() {
315 iterator_->SeekToLast();
316 CheckStatus();
317 }
318
319 void IteratorImpl::Seek(const LevelDBSlice& target) {
320 iterator_->Seek(MakeSlice(target));
321 CheckStatus();
322 }
323
324 void IteratorImpl::Next() {
325 DCHECK(IsValid());
326 iterator_->Next();
327 CheckStatus();
328 }
329
330 void IteratorImpl::Prev() {
331 DCHECK(IsValid());
332 iterator_->Prev();
333 CheckStatus();
334 }
335
336 LevelDBSlice IteratorImpl::Key() const {
337 DCHECK(IsValid());
338 return MakeLevelDBSlice(iterator_->key());
339 }
340
341 LevelDBSlice IteratorImpl::Value() const {
342 DCHECK(IsValid());
343 return MakeLevelDBSlice(iterator_->value());
344 }
345
346 scoped_ptr<LevelDBIterator> LevelDBDatabase::CreateIterator(
347 const LevelDBSnapshot* snapshot) {
348 leveldb::ReadOptions read_options;
349 read_options.verify_checksums = true; // TODO(jsbell): Disable this if the
350 // performance impact is too great.
351 read_options.snapshot = snapshot ? snapshot->snapshot_ : 0;
352 scoped_ptr<leveldb::Iterator> i(db_->NewIterator(read_options));
353 if (!i) // TODO(jsbell): Double check if we actually need to check this.
354 return scoped_ptr<LevelDBIterator>();
355 return scoped_ptr<LevelDBIterator>(new IteratorImpl(i.Pass()));
356 }
357
358 const LevelDBComparator* LevelDBDatabase::Comparator() const {
359 return comparator_;
360 }
361
362 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/indexed_db/leveldb/leveldb_database.h ('k') | content/browser/indexed_db/leveldb/leveldb_iterator.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698