Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / content / browser / indexed_db / leveldb / leveldb_database.cc
blob2c18f1f5170f34448b4d01f43f28bb82913ed6ab
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.
5 #include "content/browser/indexed_db/leveldb/leveldb_database.h"
7 #include <cerrno>
9 #include "base/basictypes.h"
10 #include "base/files/file.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/metrics/histogram.h"
14 #include "base/strings/string16.h"
15 #include "base/strings/string_piece.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/sys_info.h"
19 #include "content/browser/indexed_db/indexed_db_class_factory.h"
20 #include "content/browser/indexed_db/leveldb/leveldb_comparator.h"
21 #include "content/browser/indexed_db/leveldb/leveldb_env.h"
22 #include "content/browser/indexed_db/leveldb/leveldb_iterator_impl.h"
23 #include "content/browser/indexed_db/leveldb/leveldb_write_batch.h"
24 #include "third_party/leveldatabase/env_chromium.h"
25 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
26 #include "third_party/leveldatabase/src/include/leveldb/db.h"
27 #include "third_party/leveldatabase/src/include/leveldb/env.h"
28 #include "third_party/leveldatabase/src/include/leveldb/filter_policy.h"
29 #include "third_party/leveldatabase/src/include/leveldb/slice.h"
31 using base::StringPiece;
33 namespace content {
35 // Forcing flushes to disk at the end of a transaction guarantees that the
36 // data hit disk, but drastically impacts throughput when the filesystem is
37 // busy with background compactions. Not syncing trades off reliability for
38 // performance. Note that background compactions which move data from the
39 // log to SSTs are always done with reliable writes.
41 // Sync writes are necessary on Windows for quota calculations; POSIX
42 // calculates file sizes correctly even when not synced to disk.
43 #if defined(OS_WIN)
44 static const bool kSyncWrites = true;
45 #else
46 // TODO(dgrogan): Either remove the #if block or change this back to false.
47 // See http://crbug.com/338385.
48 static const bool kSyncWrites = true;
49 #endif
51 static leveldb::Slice MakeSlice(const StringPiece& s) {
52 return leveldb::Slice(s.begin(), s.size());
55 static StringPiece MakeStringPiece(const leveldb::Slice& s) {
56 return StringPiece(s.data(), s.size());
59 LevelDBDatabase::ComparatorAdapter::ComparatorAdapter(
60 const LevelDBComparator* comparator)
61 : comparator_(comparator) {}
63 int LevelDBDatabase::ComparatorAdapter::Compare(const leveldb::Slice& a,
64 const leveldb::Slice& b) const {
65 return comparator_->Compare(MakeStringPiece(a), MakeStringPiece(b));
68 const char* LevelDBDatabase::ComparatorAdapter::Name() const {
69 return comparator_->Name();
72 // TODO(jsbell): Support the methods below in the future.
73 void LevelDBDatabase::ComparatorAdapter::FindShortestSeparator(
74 std::string* start,
75 const leveldb::Slice& limit) const {}
77 void LevelDBDatabase::ComparatorAdapter::FindShortSuccessor(
78 std::string* key) const {}
80 LevelDBSnapshot::LevelDBSnapshot(LevelDBDatabase* db)
81 : db_(db->db_.get()), snapshot_(db_->GetSnapshot()) {}
83 LevelDBSnapshot::~LevelDBSnapshot() { db_->ReleaseSnapshot(snapshot_); }
85 LevelDBDatabase::LevelDBDatabase() {}
87 LevelDBDatabase::~LevelDBDatabase() {
88 // db_'s destructor uses comparator_adapter_; order of deletion is important.
89 CloseDatabase();
90 comparator_adapter_.reset();
91 env_.reset();
94 void LevelDBDatabase::CloseDatabase() {
95 if (db_) {
96 base::TimeTicks begin_time = base::TimeTicks::Now();
97 db_.reset();
98 UMA_HISTOGRAM_MEDIUM_TIMES("WebCore.IndexedDB.LevelDB.CloseTime",
99 base::TimeTicks::Now() - begin_time);
103 static leveldb::Status OpenDB(
104 leveldb::Comparator* comparator,
105 leveldb::Env* env,
106 const base::FilePath& path,
107 leveldb::DB** db,
108 scoped_ptr<const leveldb::FilterPolicy>* filter_policy) {
109 filter_policy->reset(leveldb::NewBloomFilterPolicy(10));
110 leveldb::Options options;
111 options.comparator = comparator;
112 options.create_if_missing = true;
113 options.paranoid_checks = true;
114 options.filter_policy = filter_policy->get();
115 options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
116 options.compression = leveldb::kSnappyCompression;
118 // For info about the troubles we've run into with this parameter, see:
119 // https://code.google.com/p/chromium/issues/detail?id=227313#c11
120 options.max_open_files = 80;
121 options.env = env;
123 // ChromiumEnv assumes UTF8, converts back to FilePath before using.
124 leveldb::Status s = leveldb::DB::Open(options, path.AsUTF8Unsafe(), db);
126 return s;
129 leveldb::Status LevelDBDatabase::Destroy(const base::FilePath& file_name) {
130 leveldb::Options options;
131 options.env = LevelDBEnv::Get();
132 // ChromiumEnv assumes UTF8, converts back to FilePath before using.
133 return leveldb::DestroyDB(file_name.AsUTF8Unsafe(), options);
136 namespace {
137 class LockImpl : public LevelDBLock {
138 public:
139 explicit LockImpl(leveldb::Env* env, leveldb::FileLock* lock)
140 : env_(env), lock_(lock) {}
141 ~LockImpl() override { env_->UnlockFile(lock_); }
143 private:
144 leveldb::Env* env_;
145 leveldb::FileLock* lock_;
147 DISALLOW_COPY_AND_ASSIGN(LockImpl);
149 } // namespace
151 scoped_ptr<LevelDBLock> LevelDBDatabase::LockForTesting(
152 const base::FilePath& file_name) {
153 leveldb::Env* env = LevelDBEnv::Get();
154 base::FilePath lock_path = file_name.AppendASCII("LOCK");
155 leveldb::FileLock* lock = NULL;
156 leveldb::Status status = env->LockFile(lock_path.AsUTF8Unsafe(), &lock);
157 if (!status.ok())
158 return scoped_ptr<LevelDBLock>();
159 DCHECK(lock);
160 return scoped_ptr<LevelDBLock>(new LockImpl(env, lock));
163 static int CheckFreeSpace(const char* const type,
164 const base::FilePath& file_name) {
165 std::string name =
166 std::string("WebCore.IndexedDB.LevelDB.Open") + type + "FreeDiskSpace";
167 int64 free_disk_space_in_k_bytes =
168 base::SysInfo::AmountOfFreeDiskSpace(file_name) / 1024;
169 if (free_disk_space_in_k_bytes < 0) {
170 base::Histogram::FactoryGet(
171 "WebCore.IndexedDB.LevelDB.FreeDiskSpaceFailure",
173 2 /*boundary*/,
174 2 /*boundary*/ + 1,
175 base::HistogramBase::kUmaTargetedHistogramFlag)->Add(1 /*sample*/);
176 return -1;
178 int clamped_disk_space_k_bytes = free_disk_space_in_k_bytes > INT_MAX
179 ? INT_MAX
180 : free_disk_space_in_k_bytes;
181 const uint64 histogram_max = static_cast<uint64>(1e9);
182 static_assert(histogram_max <= INT_MAX, "histogram_max too big");
183 base::Histogram::FactoryGet(name,
185 histogram_max,
186 11 /*buckets*/,
187 base::HistogramBase::kUmaTargetedHistogramFlag)
188 ->Add(clamped_disk_space_k_bytes);
189 return clamped_disk_space_k_bytes;
192 static void ParseAndHistogramIOErrorDetails(const std::string& histogram_name,
193 const leveldb::Status& s) {
194 leveldb_env::MethodID method;
195 base::File::Error error = base::File::FILE_OK;
196 leveldb_env::ErrorParsingResult result =
197 leveldb_env::ParseMethodAndError(s, &method, &error);
198 if (result == leveldb_env::NONE)
199 return;
200 std::string method_histogram_name(histogram_name);
201 method_histogram_name.append(".EnvMethod");
202 base::LinearHistogram::FactoryGet(
203 method_histogram_name,
205 leveldb_env::kNumEntries,
206 leveldb_env::kNumEntries + 1,
207 base::HistogramBase::kUmaTargetedHistogramFlag)->Add(method);
209 std::string error_histogram_name(histogram_name);
211 if (result == leveldb_env::METHOD_AND_BFE) {
212 DCHECK_LT(error, 0);
213 error_histogram_name.append(std::string(".BFE.") +
214 leveldb_env::MethodIDToString(method));
215 base::LinearHistogram::FactoryGet(
216 error_histogram_name,
218 -base::File::FILE_ERROR_MAX,
219 -base::File::FILE_ERROR_MAX + 1,
220 base::HistogramBase::kUmaTargetedHistogramFlag)->Add(-error);
224 static void ParseAndHistogramCorruptionDetails(
225 const std::string& histogram_name,
226 const leveldb::Status& status) {
227 int error = leveldb_env::GetCorruptionCode(status);
228 DCHECK_GE(error, 0);
229 std::string corruption_histogram_name(histogram_name);
230 corruption_histogram_name.append(".Corruption");
231 const int kNumPatterns = leveldb_env::GetNumCorruptionCodes();
232 base::LinearHistogram::FactoryGet(
233 corruption_histogram_name,
235 kNumPatterns,
236 kNumPatterns + 1,
237 base::HistogramBase::kUmaTargetedHistogramFlag)->Add(error);
240 static void HistogramLevelDBError(const std::string& histogram_name,
241 const leveldb::Status& s) {
242 if (s.ok()) {
243 NOTREACHED();
244 return;
246 enum {
247 LEVEL_DB_NOT_FOUND,
248 LEVEL_DB_CORRUPTION,
249 LEVEL_DB_IO_ERROR,
250 LEVEL_DB_OTHER,
251 LEVEL_DB_MAX_ERROR
253 int leveldb_error = LEVEL_DB_OTHER;
254 if (s.IsNotFound())
255 leveldb_error = LEVEL_DB_NOT_FOUND;
256 else if (s.IsCorruption())
257 leveldb_error = LEVEL_DB_CORRUPTION;
258 else if (s.IsIOError())
259 leveldb_error = LEVEL_DB_IO_ERROR;
260 base::Histogram::FactoryGet(histogram_name,
262 LEVEL_DB_MAX_ERROR,
263 LEVEL_DB_MAX_ERROR + 1,
264 base::HistogramBase::kUmaTargetedHistogramFlag)
265 ->Add(leveldb_error);
266 if (s.IsIOError())
267 ParseAndHistogramIOErrorDetails(histogram_name, s);
268 else
269 ParseAndHistogramCorruptionDetails(histogram_name, s);
272 leveldb::Status LevelDBDatabase::Open(const base::FilePath& file_name,
273 const LevelDBComparator* comparator,
274 scoped_ptr<LevelDBDatabase>* result,
275 bool* is_disk_full) {
276 base::TimeTicks begin_time = base::TimeTicks::Now();
278 scoped_ptr<ComparatorAdapter> comparator_adapter(
279 new ComparatorAdapter(comparator));
281 leveldb::DB* db;
282 scoped_ptr<const leveldb::FilterPolicy> filter_policy;
283 const leveldb::Status s = OpenDB(comparator_adapter.get(), LevelDBEnv::Get(),
284 file_name, &db, &filter_policy);
286 if (!s.ok()) {
287 HistogramLevelDBError("WebCore.IndexedDB.LevelDBOpenErrors", s);
288 int free_space_k_bytes = CheckFreeSpace("Failure", file_name);
289 // Disks with <100k of free space almost never succeed in opening a
290 // leveldb database.
291 if (is_disk_full)
292 *is_disk_full = free_space_k_bytes >= 0 && free_space_k_bytes < 100;
294 LOG(ERROR) << "Failed to open LevelDB database from "
295 << file_name.AsUTF8Unsafe() << "," << s.ToString();
296 return s;
299 UMA_HISTOGRAM_MEDIUM_TIMES("WebCore.IndexedDB.LevelDB.OpenTime",
300 base::TimeTicks::Now() - begin_time);
302 CheckFreeSpace("Success", file_name);
304 (*result).reset(new LevelDBDatabase);
305 (*result)->db_ = make_scoped_ptr(db);
306 (*result)->comparator_adapter_ = comparator_adapter.Pass();
307 (*result)->comparator_ = comparator;
308 (*result)->filter_policy_ = filter_policy.Pass();
310 return s;
313 scoped_ptr<LevelDBDatabase> LevelDBDatabase::OpenInMemory(
314 const LevelDBComparator* comparator) {
315 scoped_ptr<ComparatorAdapter> comparator_adapter(
316 new ComparatorAdapter(comparator));
317 scoped_ptr<leveldb::Env> in_memory_env(leveldb::NewMemEnv(LevelDBEnv::Get()));
319 leveldb::DB* db;
320 scoped_ptr<const leveldb::FilterPolicy> filter_policy;
321 const leveldb::Status s = OpenDB(comparator_adapter.get(),
322 in_memory_env.get(),
323 base::FilePath(),
324 &db,
325 &filter_policy);
327 if (!s.ok()) {
328 LOG(ERROR) << "Failed to open in-memory LevelDB database: " << s.ToString();
329 return scoped_ptr<LevelDBDatabase>();
332 scoped_ptr<LevelDBDatabase> result(new LevelDBDatabase);
333 result->env_ = in_memory_env.Pass();
334 result->db_ = make_scoped_ptr(db);
335 result->comparator_adapter_ = comparator_adapter.Pass();
336 result->comparator_ = comparator;
337 result->filter_policy_ = filter_policy.Pass();
339 return result.Pass();
342 leveldb::Status LevelDBDatabase::Put(const StringPiece& key,
343 std::string* value) {
344 base::TimeTicks begin_time = base::TimeTicks::Now();
346 leveldb::WriteOptions write_options;
347 write_options.sync = kSyncWrites;
349 const leveldb::Status s =
350 db_->Put(write_options, MakeSlice(key), MakeSlice(*value));
351 if (!s.ok())
352 LOG(ERROR) << "LevelDB put failed: " << s.ToString();
353 else
354 UMA_HISTOGRAM_TIMES("WebCore.IndexedDB.LevelDB.PutTime",
355 base::TimeTicks::Now() - begin_time);
356 return s;
359 leveldb::Status LevelDBDatabase::Remove(const StringPiece& key) {
360 leveldb::WriteOptions write_options;
361 write_options.sync = kSyncWrites;
363 const leveldb::Status s = db_->Delete(write_options, MakeSlice(key));
364 if (!s.IsNotFound())
365 LOG(ERROR) << "LevelDB remove failed: " << s.ToString();
366 return s;
369 leveldb::Status LevelDBDatabase::Get(const StringPiece& key,
370 std::string* value,
371 bool* found,
372 const LevelDBSnapshot* snapshot) {
373 *found = false;
374 leveldb::ReadOptions read_options;
375 read_options.verify_checksums = true; // TODO(jsbell): Disable this if the
376 // performance impact is too great.
377 read_options.snapshot = snapshot ? snapshot->snapshot_ : 0;
379 const leveldb::Status s = db_->Get(read_options, MakeSlice(key), value);
380 if (s.ok()) {
381 *found = true;
382 return s;
384 if (s.IsNotFound())
385 return leveldb::Status::OK();
386 HistogramLevelDBError("WebCore.IndexedDB.LevelDBReadErrors", s);
387 LOG(ERROR) << "LevelDB get failed: " << s.ToString();
388 return s;
391 leveldb::Status LevelDBDatabase::Write(const LevelDBWriteBatch& write_batch) {
392 base::TimeTicks begin_time = base::TimeTicks::Now();
393 leveldb::WriteOptions write_options;
394 write_options.sync = kSyncWrites;
396 const leveldb::Status s =
397 db_->Write(write_options, write_batch.write_batch_.get());
398 if (!s.ok()) {
399 HistogramLevelDBError("WebCore.IndexedDB.LevelDBWriteErrors", s);
400 LOG(ERROR) << "LevelDB write failed: " << s.ToString();
401 } else {
402 UMA_HISTOGRAM_TIMES("WebCore.IndexedDB.LevelDB.WriteTime",
403 base::TimeTicks::Now() - begin_time);
405 return s;
408 scoped_ptr<LevelDBIterator> LevelDBDatabase::CreateIterator(
409 const LevelDBSnapshot* snapshot) {
410 leveldb::ReadOptions read_options;
411 read_options.verify_checksums = true; // TODO(jsbell): Disable this if the
412 // performance impact is too great.
413 read_options.snapshot = snapshot ? snapshot->snapshot_ : 0;
415 scoped_ptr<leveldb::Iterator> i(db_->NewIterator(read_options));
416 return scoped_ptr<LevelDBIterator>(
417 IndexedDBClassFactory::Get()->CreateIteratorImpl(i.Pass()));
420 const LevelDBComparator* LevelDBDatabase::Comparator() const {
421 return comparator_;
424 void LevelDBDatabase::Compact(const base::StringPiece& start,
425 const base::StringPiece& stop) {
426 const leveldb::Slice start_slice = MakeSlice(start);
427 const leveldb::Slice stop_slice = MakeSlice(stop);
428 // NULL batch means just wait for earlier writes to be done
429 db_->Write(leveldb::WriteOptions(), NULL);
430 db_->CompactRange(&start_slice, &stop_slice);
433 void LevelDBDatabase::CompactAll() { db_->CompactRange(NULL, NULL); }
435 } // namespace content