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

Side by Side Diff: sql/connection.cc

Issue 1533703002: [sql] Consider fresh databases suitable for memory-mapped I/O. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: eyeroll and 0UL Created 5 years 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
« no previous file with comments | « sql/connection.h ('k') | sql/connection_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "sql/connection.h" 5 #include "sql/connection.h"
6 6
7 #include <string.h> 7 #include <string.h>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/debug/dump_without_crashing.h" 10 #include "base/debug/dump_without_crashing.h"
11 #include "base/files/file_path.h" 11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h" 12 #include "base/files/file_util.h"
13 #include "base/format_macros.h" 13 #include "base/format_macros.h"
14 #include "base/json/json_file_value_serializer.h" 14 #include "base/json/json_file_value_serializer.h"
15 #include "base/lazy_instance.h" 15 #include "base/lazy_instance.h"
16 #include "base/logging.h" 16 #include "base/logging.h"
17 #include "base/message_loop/message_loop.h" 17 #include "base/message_loop/message_loop.h"
18 #include "base/metrics/histogram.h" 18 #include "base/metrics/histogram.h"
19 #include "base/metrics/sparse_histogram.h" 19 #include "base/metrics/sparse_histogram.h"
20 #include "base/strings/string_split.h" 20 #include "base/strings/string_split.h"
21 #include "base/strings/string_util.h" 21 #include "base/strings/string_util.h"
22 #include "base/strings/stringprintf.h" 22 #include "base/strings/stringprintf.h"
23 #include "base/strings/utf_string_conversions.h" 23 #include "base/strings/utf_string_conversions.h"
24 #include "base/synchronization/lock.h" 24 #include "base/synchronization/lock.h"
25 #include "base/trace_event/memory_dump_manager.h" 25 #include "base/trace_event/memory_dump_manager.h"
26 #include "base/trace_event/process_memory_dump.h" 26 #include "base/trace_event/process_memory_dump.h"
27 #include "sql/meta_table.h"
27 #include "sql/statement.h" 28 #include "sql/statement.h"
28 #include "third_party/sqlite/sqlite3.h" 29 #include "third_party/sqlite/sqlite3.h"
29 30
30 #if defined(OS_IOS) && defined(USE_SYSTEM_SQLITE) 31 #if defined(OS_IOS) && defined(USE_SYSTEM_SQLITE)
31 #include "third_party/sqlite/src/ext/icu/sqliteicu.h" 32 #include "third_party/sqlite/src/ext/icu/sqliteicu.h"
32 #endif 33 #endif
33 34
34 namespace { 35 namespace {
35 36
36 // Spin for up to a second waiting for the lock to clear when setting 37 // Spin for up to a second waiting for the lock to clear when setting
(...skipping 414 matching lines...) Expand 10 before | Expand all | Expand 10 after
451 int64_t size_64 = 0; 452 int64_t size_64 = 0;
452 if (base::GetFileSize(path, &size_64)) { 453 if (base::GetFileSize(path, &size_64)) {
453 size_t sample = static_cast<size_t>(size_64 / 1024); 454 size_t sample = static_cast<size_t>(size_64 / 1024);
454 std::string full_histogram_name = "Sqlite.SizeKB." + histogram_tag_; 455 std::string full_histogram_name = "Sqlite.SizeKB." + histogram_tag_;
455 base::HistogramBase* histogram = 456 base::HistogramBase* histogram =
456 base::Histogram::FactoryGet( 457 base::Histogram::FactoryGet(
457 full_histogram_name, 1, 1000000, 50, 458 full_histogram_name, 1, 1000000, 50,
458 base::HistogramBase::kUmaTargetedHistogramFlag); 459 base::HistogramBase::kUmaTargetedHistogramFlag);
459 if (histogram) 460 if (histogram)
460 histogram->Add(sample); 461 histogram->Add(sample);
462 UMA_HISTOGRAM_COUNTS("Sqlite.SizeKB", sample);
461 } 463 }
462 } 464 }
463 465
464 return OpenInternal(AsUTF8ForSQL(path), RETRY_ON_POISON); 466 return OpenInternal(AsUTF8ForSQL(path), RETRY_ON_POISON);
465 } 467 }
466 468
467 bool Connection::OpenInMemory() { 469 bool Connection::OpenInMemory() {
468 in_memory_ = true; 470 in_memory_ = true;
469 return OpenInternal(":memory:", NO_RETRY); 471 return OpenInternal(":memory:", NO_RETRY);
470 } 472 }
(...skipping 405 matching lines...) Expand 10 before | Expand all | Expand 10 after
876 base::StringAppendF(&debug_info, "%s\n", messages[i].c_str()); 878 base::StringAppendF(&debug_info, "%s\n", messages[i].c_str());
877 } 879 }
878 } 880 }
879 881
880 return debug_info; 882 return debug_info;
881 } 883 }
882 884
883 size_t Connection::GetAppropriateMmapSize() { 885 size_t Connection::GetAppropriateMmapSize() {
884 AssertIOAllowed(); 886 AssertIOAllowed();
885 887
886 // TODO(shess): Using sql::MetaTable seems indicated, but mixing
887 // sql::MetaTable and direct access seems error-prone. It might make sense to
888 // simply integrate sql::MetaTable functionality into sql::Connection.
889
890 #if defined(OS_IOS) 888 #if defined(OS_IOS)
891 // iOS SQLite does not support memory mapping. 889 // iOS SQLite does not support memory mapping.
892 return 0; 890 return 0;
893 #endif 891 #endif
894 892
895 // If the database doesn't have a place to track progress, assume the worst. 893 // How much to map if no errors are found. 50MB encompasses the 99th
896 // This will happen when new databases are created. 894 // percentile of Chrome databases in the wild, so this should be good.
897 if (!DoesTableExist("meta")) { 895 const size_t kMmapEverything = 256 * 1024 * 1024;
896
897 // If the database doesn't have a place to track progress, assume the best.
898 // This will happen when new databases are created, or if a database doesn't
899 // use a meta table. sql::MetaTable::Init() will preload kMmapSuccess.
900 // TODO(shess): Databases not using meta include:
901 // DOMStorageDatabase (localstorage)
902 // ActivityDatabase (extensions activity log)
903 // PredictorDatabase (prefetch and autocomplete predictor data)
904 // SyncDirectory (sync metadata storage)
905 // For now, these all have mmap disabled to allow other databases to get the
906 // default-enable path. sqlite-diag could be an alternative for all but
907 // DOMStorageDatabase, which creates many small databases.
908 // http://crbug.com/537742
909 if (!MetaTable::DoesTableExist(this)) {
898 RecordOneEvent(EVENT_MMAP_META_MISSING); 910 RecordOneEvent(EVENT_MMAP_META_MISSING);
911 return kMmapEverything;
912 }
913
914 int64_t mmap_ofs = 0;
915 if (!MetaTable::GetMmapStatus(this, &mmap_ofs)) {
916 RecordOneEvent(EVENT_MMAP_META_FAILURE_READ);
899 return 0; 917 return 0;
900 } 918 }
901 919
902 // Key into meta table to get status from a previous run. The value
903 // represents how much data in bytes has successfully been read from the
904 // database. |kMmapFailure| indicates that there was a read error and the
905 // database should not be memory-mapped, while |kMmapSuccess| indicates that
906 // the entire file was read at some point and can be memory-mapped without
907 // constraint.
908 const char* kMmapStatusKey = "mmap_status";
909 static const sqlite3_int64 kMmapFailure = -2;
910 static const sqlite3_int64 kMmapSuccess = -1;
911
912 // Start reading from 0 unless status is found in meta table.
913 sqlite3_int64 mmap_ofs = 0;
914
915 // Retrieve the current status. It is fine for the status to be missing
916 // entirely, but any error prevents memory-mapping.
917 {
918 const char* kMmapStatusSql = "SELECT value FROM meta WHERE key = ?";
919 Statement s(GetUniqueStatement(kMmapStatusSql));
920 s.BindString(0, kMmapStatusKey);
921 if (s.Step()) {
922 mmap_ofs = s.ColumnInt64(0);
923 } else if (!s.Succeeded()) {
924 RecordOneEvent(EVENT_MMAP_META_FAILURE_READ);
925 return 0;
926 }
927 }
928
929 // Database read failed in the past, don't memory map. 920 // Database read failed in the past, don't memory map.
930 if (mmap_ofs == kMmapFailure) { 921 if (mmap_ofs == MetaTable::kMmapFailure) {
931 RecordOneEvent(EVENT_MMAP_FAILED); 922 RecordOneEvent(EVENT_MMAP_FAILED);
932 return 0; 923 return 0;
933 } else if (mmap_ofs != kMmapSuccess) { 924 } else if (mmap_ofs != MetaTable::kMmapSuccess) {
934 // Continue reading from previous offset. 925 // Continue reading from previous offset.
935 DCHECK_GE(mmap_ofs, 0); 926 DCHECK_GE(mmap_ofs, 0);
936 927
937 // TODO(shess): Could this reading code be shared with Preload()? It would 928 // TODO(shess): Could this reading code be shared with Preload()? It would
938 // require locking twice (this code wouldn't be able to access |db_size| so 929 // require locking twice (this code wouldn't be able to access |db_size| so
939 // the helper would have to return amount read). 930 // the helper would have to return amount read).
940 931
941 // Read more of the database looking for errors. The VFS interface is used 932 // Read more of the database looking for errors. The VFS interface is used
942 // to assure that the reads are valid for SQLite. |g_reads_allowed| is used 933 // to assure that the reads are valid for SQLite. |g_reads_allowed| is used
943 // to limit checking to 20MB per run of Chromium. 934 // to limit checking to 20MB per run of Chromium.
(...skipping 30 matching lines...) Expand all
974 int rc = file->pMethods->xRead(file, buf, sizeof(buf), mmap_ofs); 965 int rc = file->pMethods->xRead(file, buf, sizeof(buf), mmap_ofs);
975 if (rc == SQLITE_OK) { 966 if (rc == SQLITE_OK) {
976 mmap_ofs += sizeof(buf); 967 mmap_ofs += sizeof(buf);
977 amount -= sizeof(buf); 968 amount -= sizeof(buf);
978 } else if (rc == SQLITE_IOERR_SHORT_READ) { 969 } else if (rc == SQLITE_IOERR_SHORT_READ) {
979 // Reached EOF for a database with page size < |kPageSize|. 970 // Reached EOF for a database with page size < |kPageSize|.
980 mmap_ofs = db_size; 971 mmap_ofs = db_size;
981 break; 972 break;
982 } else { 973 } else {
983 // TODO(shess): Consider calling OnSqliteError(). 974 // TODO(shess): Consider calling OnSqliteError().
984 mmap_ofs = kMmapFailure; 975 mmap_ofs = MetaTable::kMmapFailure;
985 break; 976 break;
986 } 977 }
987 } 978 }
988 979
989 // Log these events after update to distinguish meta update failure. 980 // Log these events after update to distinguish meta update failure.
990 Events event; 981 Events event;
991 if (mmap_ofs >= db_size) { 982 if (mmap_ofs >= db_size) {
992 mmap_ofs = kMmapSuccess; 983 mmap_ofs = MetaTable::kMmapSuccess;
993 event = EVENT_MMAP_SUCCESS_NEW; 984 event = EVENT_MMAP_SUCCESS_NEW;
994 } else if (mmap_ofs > 0) { 985 } else if (mmap_ofs > 0) {
995 event = EVENT_MMAP_SUCCESS_PARTIAL; 986 event = EVENT_MMAP_SUCCESS_PARTIAL;
996 } else { 987 } else {
997 DCHECK_EQ(kMmapFailure, mmap_ofs); 988 DCHECK_EQ(MetaTable::kMmapFailure, mmap_ofs);
998 event = EVENT_MMAP_FAILED_NEW; 989 event = EVENT_MMAP_FAILED_NEW;
999 } 990 }
1000 991
1001 const char* kMmapUpdateStatusSql = "REPLACE INTO meta VALUES (?, ?)"; 992 if (!MetaTable::SetMmapStatus(this, mmap_ofs)) {
1002 Statement s(GetUniqueStatement(kMmapUpdateStatusSql));
1003 s.BindString(0, kMmapStatusKey);
1004 s.BindInt64(1, mmap_ofs);
1005 if (!s.Run()) {
1006 RecordOneEvent(EVENT_MMAP_META_FAILURE_UPDATE); 993 RecordOneEvent(EVENT_MMAP_META_FAILURE_UPDATE);
1007 return 0; 994 return 0;
1008 } 995 }
1009 996
1010 RecordOneEvent(event); 997 RecordOneEvent(event);
1011 } 998 }
1012 } 999 }
1013 1000
1014 if (mmap_ofs == kMmapFailure) 1001 if (mmap_ofs == MetaTable::kMmapFailure)
1015 return 0; 1002 return 0;
1016 if (mmap_ofs == kMmapSuccess) 1003 if (mmap_ofs == MetaTable::kMmapSuccess)
1017 return 256 * 1024 * 1024; 1004 return kMmapEverything;
1018 return mmap_ofs; 1005 return mmap_ofs;
1019 } 1006 }
1020 1007
1021 void Connection::TrimMemory(bool aggressively) { 1008 void Connection::TrimMemory(bool aggressively) {
1022 if (!db_) 1009 if (!db_)
1023 return; 1010 return;
1024 1011
1025 // TODO(shess): investigate using sqlite3_db_release_memory() when possible. 1012 // TODO(shess): investigate using sqlite3_db_release_memory() when possible.
1026 int original_cache_size; 1013 int original_cache_size;
1027 { 1014 {
(...skipping 965 matching lines...) Expand 10 before | Expand all | Expand 10 after
1993 ignore_result(Execute(kNoWritableSchema)); 1980 ignore_result(Execute(kNoWritableSchema));
1994 1981
1995 return ret; 1982 return ret;
1996 } 1983 }
1997 1984
1998 base::TimeTicks TimeSource::Now() { 1985 base::TimeTicks TimeSource::Now() {
1999 return base::TimeTicks::Now(); 1986 return base::TimeTicks::Now();
2000 } 1987 }
2001 1988
2002 } // namespace sql 1989 } // namespace sql
OLDNEW
« no previous file with comments | « sql/connection.h ('k') | sql/connection_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698