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

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

Powered by Google App Engine
This is Rietveld 408576698