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

Side by Side Diff: sql/connection.cc

Issue 1349863003: [sql] Use memory-mapped I/O for sql::Connection. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 3 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
« 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/files/file_path.h" 10 #include "base/files/file_path.h"
(...skipping 448 matching lines...) Expand 10 before | Expand all | Expand 10 after
459 preload_size = file_size; 459 preload_size = file_size;
460 460
461 scoped_ptr<char[]> buf(new char[page_size]); 461 scoped_ptr<char[]> buf(new char[page_size]);
462 for (sqlite3_int64 pos = 0; pos < preload_size; pos += page_size) { 462 for (sqlite3_int64 pos = 0; pos < preload_size; pos += page_size) {
463 rc = file->pMethods->xRead(file, buf.get(), page_size, pos); 463 rc = file->pMethods->xRead(file, buf.get(), page_size, pos);
464 if (rc != SQLITE_OK) 464 if (rc != SQLITE_OK)
465 return; 465 return;
466 } 466 }
467 } 467 }
468 468
469 // SQLite keeps unused pages associated with a connection in a cache. It asks
470 // the cache for pages by an id, and if the page is present and the database is
471 // unchanged, it considers the content of the page valid and doesn't read it
472 // from disk. When memory-mapped I/O is enabled, on read SQLite uses page
473 // structures created from the memory map data before consulting the cache. On
474 // write SQLite creates a new in-memory page structure, copies the data from the
475 // memory map, and later writes it, releasing the updated page back to the
476 // cache.
477 //
478 // This means that in memory-mapped mode, the contents of the cached pages are
479 // not re-used for reads, but they are re-used for writes if the re-written page
480 // is still in the cache. The implementation of sqlite3_db_release_memory() as
481 // of SQLite 3.8.7.4 frees all pages from pcaches associated with the
482 // connection, so it should free these pages.
483 //
484 // Unfortunately, the zero page is also freed. That page is never accessed
485 // using memory-mapped I/O, and the cached copy can be re-used after verifying
486 // the file change counter on disk. Also, fresh pages from cache receive some
487 // pager-level initialization before they can be used. Since the information
488 // involved will immediately be accessed in various ways, it is unclear if the
489 // additional overhead is material, or just moving processor cache effects
490 // around.
Scott Hess - ex-Googler 2015/09/16 22:46:16 Apologies for the mega-comment. I'm not entirely
491 //
492 // TODO(shess): It would be better to release the pages immediately when they
493 // are no longer needed. This would basically happen after SQLite commits a
494 // transaction. I had implemented a pcache wrapper to do this, but it involved
495 // layering violations, and it had to be setup before any other sqlite call,
496 // which was brittle. Also, for large files it would actually make sense to
497 // maintain the existing pcache behavior for blocks past the memory-mapped
498 // segment. I think drh would accept a reasonable implementation of the overall
499 // concept for upstreaming to SQLite core.
500 //
501 // TODO(shess): Another possibility would be to set the cache size small, which
502 // would keep the zero page around, plus some pre-initialized pages, and SQLite
503 // can manage things. The downside is that updates larger than the cache would
504 // spill to the journal. That could be compensated by setting cache_spill to
505 // false. The downside then is that it allows open-ended use of memory for
506 // large transactions.
507 //
508 // TODO(shess): The TrimMemory() trick of bouncing the cache size would also
509 // work. There could be two prepared statements, one for cache_size=1 one for
510 // cache_size=goal.
511 void Connection::ReleaseCacheMemoryIfNeeded(bool assume_changed) {
512 // If memory-mapping is not enabled, the page cache helps performance.
513 if (!mmap_enabled_)
514 return;
515
516 // On caller request, force the change comparison to fail. Done before the
517 // transaction-nesting test so that the signal can carry to transaction
518 // commit.
519 if (assume_changed)
520 total_changes_--;
521
522 // Cached pages may be re-used within the same transaction.
523 if (transaction_nesting())
524 return;
525
526 // If no changes have been made, skip flushing. This allows the first page of
527 // the database to remain in cache across multiple reads.
528 int current_changes = sqlite3_total_changes(db_);
529 if (current_changes == total_changes_)
530 return;
531
532 total_changes_ = current_changes;
533 sqlite3_db_release_memory(db_);
534 }
535
536 // NOTE(shess): When memory-mapped mode is solid, this will be a noop.
469 void Connection::TrimMemory(bool aggressively) { 537 void Connection::TrimMemory(bool aggressively) {
470 if (!db_) 538 if (!db_)
471 return; 539 return;
472 540
473 // TODO(shess): investigate using sqlite3_db_release_memory() when possible. 541 // TODO(shess): investigate using sqlite3_db_release_memory() when possible.
474 int original_cache_size; 542 int original_cache_size;
475 { 543 {
476 Statement sql_get_original(GetUniqueStatement("PRAGMA cache_size")); 544 Statement sql_get_original(GetUniqueStatement("PRAGMA cache_size"));
477 if (!sql_get_original.Step()) { 545 if (!sql_get_original.Step()) {
478 DLOG(WARNING) << "Could not get cache size " << GetErrorMessage(); 546 DLOG(WARNING) << "Could not get cache size " << GetErrorMessage();
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after
669 bool Connection::Delete(const base::FilePath& path) { 737 bool Connection::Delete(const base::FilePath& path) {
670 base::ThreadRestrictions::AssertIOAllowed(); 738 base::ThreadRestrictions::AssertIOAllowed();
671 739
672 base::FilePath journal_path(path.value() + FILE_PATH_LITERAL("-journal")); 740 base::FilePath journal_path(path.value() + FILE_PATH_LITERAL("-journal"));
673 base::FilePath wal_path(path.value() + FILE_PATH_LITERAL("-wal")); 741 base::FilePath wal_path(path.value() + FILE_PATH_LITERAL("-wal"));
674 742
675 std::string journal_str = AsUTF8ForSQL(journal_path); 743 std::string journal_str = AsUTF8ForSQL(journal_path);
676 std::string wal_str = AsUTF8ForSQL(wal_path); 744 std::string wal_str = AsUTF8ForSQL(wal_path);
677 std::string path_str = AsUTF8ForSQL(path); 745 std::string path_str = AsUTF8ForSQL(path);
678 746
747 InitializeSqlite();
748
679 sqlite3_vfs* vfs = sqlite3_vfs_find(NULL); 749 sqlite3_vfs* vfs = sqlite3_vfs_find(NULL);
680 CHECK(vfs); 750 CHECK(vfs);
681 CHECK(vfs->xDelete); 751 CHECK(vfs->xDelete);
682 CHECK(vfs->xAccess); 752 CHECK(vfs->xAccess);
683 753
684 // We only work with unix, win32 and mojo filesystems. If you're trying to 754 // We only work with unix, win32 and mojo filesystems. If you're trying to
685 // use this code with any other VFS, you're not in a good place. 755 // use this code with any other VFS, you're not in a good place.
686 CHECK(strncmp(vfs->zName, "unix", 4) == 0 || 756 CHECK(strncmp(vfs->zName, "unix", 4) == 0 ||
687 strncmp(vfs->zName, "win32", 5) == 0 || 757 strncmp(vfs->zName, "win32", 5) == 0 ||
688 strcmp(vfs->zName, "mojo") == 0); 758 strcmp(vfs->zName, "mojo") == 0);
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
766 836
767 // Collect the commit time manually, sql::Statement would register it as query 837 // Collect the commit time manually, sql::Statement would register it as query
768 // time only. 838 // time only.
769 const base::TimeTicks before = Now(); 839 const base::TimeTicks before = Now();
770 bool ret = commit.RunWithoutTimers(); 840 bool ret = commit.RunWithoutTimers();
771 const base::TimeDelta delta = Now() - before; 841 const base::TimeDelta delta = Now() - before;
772 842
773 RecordCommitTime(delta); 843 RecordCommitTime(delta);
774 RecordOneEvent(EVENT_COMMIT); 844 RecordOneEvent(EVENT_COMMIT);
775 845
846 // Release dirty cache pages after the transaction closes.
847 ReleaseCacheMemoryIfNeeded(false);
848
776 return ret; 849 return ret;
777 } 850 }
778 851
779 void Connection::RollbackAllTransactions() { 852 void Connection::RollbackAllTransactions() {
780 if (transaction_nesting_ > 0) { 853 if (transaction_nesting_ > 0) {
781 transaction_nesting_ = 0; 854 transaction_nesting_ = 0;
782 DoRollback(); 855 DoRollback();
783 } 856 }
784 } 857 }
785 858
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
856 // sqlite3_exec() does this, presumably to avoid spinning the parser for 929 // sqlite3_exec() does this, presumably to avoid spinning the parser for
857 // trailing whitespace. 930 // trailing whitespace.
858 // TODO(shess): Audit to see if this can become a DCHECK. 931 // TODO(shess): Audit to see if this can become a DCHECK.
859 while (base::IsAsciiWhitespace(*sql)) { 932 while (base::IsAsciiWhitespace(*sql)) {
860 sql++; 933 sql++;
861 } 934 }
862 935
863 const base::TimeDelta delta = Now() - before; 936 const base::TimeDelta delta = Now() - before;
864 RecordTimeAndChanges(delta, read_only); 937 RecordTimeAndChanges(delta, read_only);
865 } 938 }
939
940 // Most calls to Execute() modify the database. The main exceptions would be
941 // calls such as CREATE TABLE IF NOT EXISTS which could modify the database
942 // but sometimes don't.
943 ReleaseCacheMemoryIfNeeded(true);
944
866 return rc; 945 return rc;
867 } 946 }
868 947
869 bool Connection::Execute(const char* sql) { 948 bool Connection::Execute(const char* sql) {
870 if (!db_) { 949 if (!db_) {
871 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db"; 950 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db";
872 return false; 951 return false;
873 } 952 }
874 953
875 int error = ExecuteAndReturnErrorCode(sql); 954 int error = ExecuteAndReturnErrorCode(sql);
(...skipping 344 matching lines...) Expand 10 before | Expand all | Expand 10 after
1220 1299
1221 // http://www.sqlite.org/pragma.html#pragma_journal_mode 1300 // http://www.sqlite.org/pragma.html#pragma_journal_mode
1222 // DELETE (default) - delete -journal file to commit. 1301 // DELETE (default) - delete -journal file to commit.
1223 // TRUNCATE - truncate -journal file to commit. 1302 // TRUNCATE - truncate -journal file to commit.
1224 // PERSIST - zero out header of -journal file to commit. 1303 // PERSIST - zero out header of -journal file to commit.
1225 // TRUNCATE should be faster than DELETE because it won't need directory 1304 // TRUNCATE should be faster than DELETE because it won't need directory
1226 // changes for each transaction. PERSIST may break the spirit of using 1305 // changes for each transaction. PERSIST may break the spirit of using
1227 // secure_delete. 1306 // secure_delete.
1228 ignore_result(Execute("PRAGMA journal_mode = TRUNCATE")); 1307 ignore_result(Execute("PRAGMA journal_mode = TRUNCATE"));
1229 1308
1309 // Enable memory-mapped access. This value will be capped by
1310 // SQLITE_MAX_MMAP_SIZE, which could be different between 32-bit and 64-bit
1311 // platforms.
1312 mmap_enabled_ = false;
1313 ignore_result(Execute("PRAGMA mmap_size = 2147483648")); // 2GB.
1314 {
1315 Statement s(GetUniqueStatement("PRAGMA mmap_size"));
1316 if (s.Step() && s.ColumnInt64(0) > 0)
1317 mmap_enabled_ = true;
1318 }
1319
1230 const base::TimeDelta kBusyTimeout = 1320 const base::TimeDelta kBusyTimeout =
1231 base::TimeDelta::FromSeconds(kBusyTimeoutSeconds); 1321 base::TimeDelta::FromSeconds(kBusyTimeoutSeconds);
1232 1322
1233 if (page_size_ != 0) { 1323 if (page_size_ != 0) {
1234 // Enforce SQLite restrictions on |page_size_|. 1324 // Enforce SQLite restrictions on |page_size_|.
1235 DCHECK(!(page_size_ & (page_size_ - 1))) 1325 DCHECK(!(page_size_ & (page_size_ - 1)))
1236 << " page_size_ " << page_size_ << " is not a power of two."; 1326 << " page_size_ " << page_size_ << " is not a power of two.";
1237 const int kSqliteMaxPageSize = 32768; // from sqliteLimit.h 1327 const int kSqliteMaxPageSize = 32768; // from sqliteLimit.h
1238 DCHECK_LE(page_size_, kSqliteMaxPageSize); 1328 DCHECK_LE(page_size_, kSqliteMaxPageSize);
1239 const std::string sql = 1329 const std::string sql =
(...skipping 23 matching lines...) Expand all
1263 1353
1264 // Collect the rollback time manually, sql::Statement would register it as 1354 // Collect the rollback time manually, sql::Statement would register it as
1265 // query time only. 1355 // query time only.
1266 const base::TimeTicks before = Now(); 1356 const base::TimeTicks before = Now();
1267 rollback.RunWithoutTimers(); 1357 rollback.RunWithoutTimers();
1268 const base::TimeDelta delta = Now() - before; 1358 const base::TimeDelta delta = Now() - before;
1269 1359
1270 RecordUpdateTime(delta); 1360 RecordUpdateTime(delta);
1271 RecordOneEvent(EVENT_ROLLBACK); 1361 RecordOneEvent(EVENT_ROLLBACK);
1272 1362
1363 // The cache may have been accumulating dirty pages for commit.
1364 ReleaseCacheMemoryIfNeeded(false);
1365
1273 needs_rollback_ = false; 1366 needs_rollback_ = false;
1274 } 1367 }
1275 1368
1276 void Connection::StatementRefCreated(StatementRef* ref) { 1369 void Connection::StatementRefCreated(StatementRef* ref) {
1277 DCHECK(open_statements_.find(ref) == open_statements_.end()); 1370 DCHECK(open_statements_.find(ref) == open_statements_.end());
1278 open_statements_.insert(ref); 1371 open_statements_.insert(ref);
1279 } 1372 }
1280 1373
1281 void Connection::StatementRefDeleted(StatementRef* ref) { 1374 void Connection::StatementRefDeleted(StatementRef* ref) {
1282 StatementRefSet::iterator i = open_statements_.find(ref); 1375 StatementRefSet::iterator i = open_statements_.find(ref);
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
1382 ignore_result(Execute(kNoWritableSchema)); 1475 ignore_result(Execute(kNoWritableSchema));
1383 1476
1384 return ret; 1477 return ret;
1385 } 1478 }
1386 1479
1387 base::TimeTicks TimeSource::Now() { 1480 base::TimeTicks TimeSource::Now() {
1388 return base::TimeTicks::Now(); 1481 return base::TimeTicks::Now();
1389 } 1482 }
1390 1483
1391 } // namespace sql 1484 } // 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