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

Side by Side Diff: sql/connection.cc

Issue 1382283003: [sql] Use memory-mapped I/O for sql::Connection. (Closed) Base URL: http://chromium.googlesource.com/chromium/src.git@master
Patch Set: Check for open db before releasing cache. Created 5 years, 2 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 264 matching lines...) Expand 10 before | Expand all | Expand 10 after
275 Connection::Connection() 275 Connection::Connection()
276 : db_(NULL), 276 : db_(NULL),
277 page_size_(0), 277 page_size_(0),
278 cache_size_(0), 278 cache_size_(0),
279 exclusive_locking_(false), 279 exclusive_locking_(false),
280 restrict_to_user_(false), 280 restrict_to_user_(false),
281 transaction_nesting_(0), 281 transaction_nesting_(0),
282 needs_rollback_(false), 282 needs_rollback_(false),
283 in_memory_(false), 283 in_memory_(false),
284 poisoned_(false), 284 poisoned_(false),
285 mmap_disabled_(false),
286 mmap_enabled_(false),
287 total_changes_at_last_release_(0),
285 stats_histogram_(NULL), 288 stats_histogram_(NULL),
286 commit_time_histogram_(NULL), 289 commit_time_histogram_(NULL),
287 autocommit_time_histogram_(NULL), 290 autocommit_time_histogram_(NULL),
288 update_time_histogram_(NULL), 291 update_time_histogram_(NULL),
289 query_time_histogram_(NULL), 292 query_time_histogram_(NULL),
290 clock_(new TimeSource()) { 293 clock_(new TimeSource()) {
291 } 294 }
292 295
293 Connection::~Connection() { 296 Connection::~Connection() {
294 Close(); 297 Close();
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
459 preload_size = file_size; 462 preload_size = file_size;
460 463
461 scoped_ptr<char[]> buf(new char[page_size]); 464 scoped_ptr<char[]> buf(new char[page_size]);
462 for (sqlite3_int64 pos = 0; pos < preload_size; pos += page_size) { 465 for (sqlite3_int64 pos = 0; pos < preload_size; pos += page_size) {
463 rc = file->pMethods->xRead(file, buf.get(), page_size, pos); 466 rc = file->pMethods->xRead(file, buf.get(), page_size, pos);
464 if (rc != SQLITE_OK) 467 if (rc != SQLITE_OK)
465 return; 468 return;
466 } 469 }
467 } 470 }
468 471
472 // SQLite keeps unused pages associated with a connection in a cache. It asks
473 // the cache for pages by an id, and if the page is present and the database is
474 // unchanged, it considers the content of the page valid and doesn't read it
475 // from disk. When memory-mapped I/O is enabled, on read SQLite uses page
476 // structures created from the memory map data before consulting the cache. On
477 // write SQLite creates a new in-memory page structure, copies the data from the
478 // memory map, and later writes it, releasing the updated page back to the
479 // cache.
480 //
481 // This means that in memory-mapped mode, the contents of the cached pages are
482 // not re-used for reads, but they are re-used for writes if the re-written page
483 // is still in the cache. The implementation of sqlite3_db_release_memory() as
484 // of SQLite 3.8.7.4 frees all pages from pcaches associated with the
485 // connection, so it should free these pages.
486 //
487 // Unfortunately, the zero page is also freed. That page is never accessed
488 // using memory-mapped I/O, and the cached copy can be re-used after verifying
489 // the file change counter on disk. Also, fresh pages from cache receive some
490 // pager-level initialization before they can be used. Since the information
491 // involved will immediately be accessed in various ways, it is unclear if the
492 // additional overhead is material, or just moving processor cache effects
493 // around.
494 //
495 // TODO(shess): It would be better to release the pages immediately when they
496 // are no longer needed. This would basically happen after SQLite commits a
497 // transaction. I had implemented a pcache wrapper to do this, but it involved
498 // layering violations, and it had to be setup before any other sqlite call,
499 // which was brittle. Also, for large files it would actually make sense to
500 // maintain the existing pcache behavior for blocks past the memory-mapped
501 // segment. I think drh would accept a reasonable implementation of the overall
502 // concept for upstreaming to SQLite core.
503 //
504 // TODO(shess): Another possibility would be to set the cache size small, which
505 // would keep the zero page around, plus some pre-initialized pages, and SQLite
506 // can manage things. The downside is that updates larger than the cache would
507 // spill to the journal. That could be compensated by setting cache_spill to
508 // false. The downside then is that it allows open-ended use of memory for
509 // large transactions.
510 //
511 // TODO(shess): The TrimMemory() trick of bouncing the cache size would also
512 // work. There could be two prepared statements, one for cache_size=1 one for
513 // cache_size=goal.
514 void Connection::ReleaseCacheMemoryIfNeeded(bool implicit_change_performed) {
515 DCHECK(is_open());
516
517 // If memory-mapping is not enabled, the page cache helps performance.
518 if (!mmap_enabled_)
519 return;
520
521 // On caller request, force the change comparison to fail. Done before the
522 // transaction-nesting test so that the signal can carry to transaction
523 // commit.
524 if (implicit_change_performed)
525 --total_changes_at_last_release_;
526
527 // Cached pages may be re-used within the same transaction.
528 if (transaction_nesting())
529 return;
530
531 // If no changes have been made, skip flushing. This allows the first page of
532 // the database to remain in cache across multiple reads.
533 const int total_changes = sqlite3_total_changes(db_);
534 if (total_changes == total_changes_at_last_release_)
535 return;
536
537 total_changes_at_last_release_ = total_changes;
538 sqlite3_db_release_memory(db_);
539 }
540
469 void Connection::TrimMemory(bool aggressively) { 541 void Connection::TrimMemory(bool aggressively) {
470 if (!db_) 542 if (!db_)
471 return; 543 return;
472 544
473 // TODO(shess): investigate using sqlite3_db_release_memory() when possible. 545 // TODO(shess): investigate using sqlite3_db_release_memory() when possible.
474 int original_cache_size; 546 int original_cache_size;
475 { 547 {
476 Statement sql_get_original(GetUniqueStatement("PRAGMA cache_size")); 548 Statement sql_get_original(GetUniqueStatement("PRAGMA cache_size"));
477 if (!sql_get_original.Step()) { 549 if (!sql_get_original.Step()) {
478 DLOG(WARNING) << "Could not get cache size " << GetErrorMessage(); 550 DLOG(WARNING) << "Could not get cache size " << GetErrorMessage();
(...skipping 290 matching lines...) Expand 10 before | Expand all | Expand 10 after
769 841
770 // Collect the commit time manually, sql::Statement would register it as query 842 // Collect the commit time manually, sql::Statement would register it as query
771 // time only. 843 // time only.
772 const base::TimeTicks before = Now(); 844 const base::TimeTicks before = Now();
773 bool ret = commit.RunWithoutTimers(); 845 bool ret = commit.RunWithoutTimers();
774 const base::TimeDelta delta = Now() - before; 846 const base::TimeDelta delta = Now() - before;
775 847
776 RecordCommitTime(delta); 848 RecordCommitTime(delta);
777 RecordOneEvent(EVENT_COMMIT); 849 RecordOneEvent(EVENT_COMMIT);
778 850
851 // Release dirty cache pages after the transaction closes.
852 ReleaseCacheMemoryIfNeeded(false);
853
779 return ret; 854 return ret;
780 } 855 }
781 856
782 void Connection::RollbackAllTransactions() { 857 void Connection::RollbackAllTransactions() {
783 if (transaction_nesting_ > 0) { 858 if (transaction_nesting_ > 0) {
784 transaction_nesting_ = 0; 859 transaction_nesting_ = 0;
785 DoRollback(); 860 DoRollback();
786 } 861 }
787 } 862 }
788 863
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
859 // sqlite3_exec() does this, presumably to avoid spinning the parser for 934 // sqlite3_exec() does this, presumably to avoid spinning the parser for
860 // trailing whitespace. 935 // trailing whitespace.
861 // TODO(shess): Audit to see if this can become a DCHECK. 936 // TODO(shess): Audit to see if this can become a DCHECK.
862 while (base::IsAsciiWhitespace(*sql)) { 937 while (base::IsAsciiWhitespace(*sql)) {
863 sql++; 938 sql++;
864 } 939 }
865 940
866 const base::TimeDelta delta = Now() - before; 941 const base::TimeDelta delta = Now() - before;
867 RecordTimeAndChanges(delta, read_only); 942 RecordTimeAndChanges(delta, read_only);
868 } 943 }
944
945 // Most calls to Execute() modify the database. The main exceptions would be
946 // calls such as CREATE TABLE IF NOT EXISTS which could modify the database
947 // but sometimes don't.
948 ReleaseCacheMemoryIfNeeded(true);
949
869 return rc; 950 return rc;
870 } 951 }
871 952
872 bool Connection::Execute(const char* sql) { 953 bool Connection::Execute(const char* sql) {
873 if (!db_) { 954 if (!db_) {
874 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db"; 955 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db";
875 return false; 956 return false;
876 } 957 }
877 958
878 int error = ExecuteAndReturnErrorCode(sql); 959 int error = ExecuteAndReturnErrorCode(sql);
(...skipping 344 matching lines...) Expand 10 before | Expand all | Expand 10 after
1223 1304
1224 // http://www.sqlite.org/pragma.html#pragma_journal_mode 1305 // http://www.sqlite.org/pragma.html#pragma_journal_mode
1225 // DELETE (default) - delete -journal file to commit. 1306 // DELETE (default) - delete -journal file to commit.
1226 // TRUNCATE - truncate -journal file to commit. 1307 // TRUNCATE - truncate -journal file to commit.
1227 // PERSIST - zero out header of -journal file to commit. 1308 // PERSIST - zero out header of -journal file to commit.
1228 // TRUNCATE should be faster than DELETE because it won't need directory 1309 // TRUNCATE should be faster than DELETE because it won't need directory
1229 // changes for each transaction. PERSIST may break the spirit of using 1310 // changes for each transaction. PERSIST may break the spirit of using
1230 // secure_delete. 1311 // secure_delete.
1231 ignore_result(Execute("PRAGMA journal_mode = TRUNCATE")); 1312 ignore_result(Execute("PRAGMA journal_mode = TRUNCATE"));
1232 1313
1314 // Enable memory-mapped access. This value will be capped by
1315 // SQLITE_MAX_MMAP_SIZE, which could be different between 32-bit and 64-bit
1316 // platforms.
1317 mmap_enabled_ = false;
1318 if (!mmap_disabled_)
1319 ignore_result(Execute("PRAGMA mmap_size = 268435456")); // 256MB.
1320 {
1321 Statement s(GetUniqueStatement("PRAGMA mmap_size"));
1322 if (s.Step() && s.ColumnInt64(0) > 0)
1323 mmap_enabled_ = true;
1324 }
1325
1233 const base::TimeDelta kBusyTimeout = 1326 const base::TimeDelta kBusyTimeout =
1234 base::TimeDelta::FromSeconds(kBusyTimeoutSeconds); 1327 base::TimeDelta::FromSeconds(kBusyTimeoutSeconds);
1235 1328
1236 if (page_size_ != 0) { 1329 if (page_size_ != 0) {
1237 // Enforce SQLite restrictions on |page_size_|. 1330 // Enforce SQLite restrictions on |page_size_|.
1238 DCHECK(!(page_size_ & (page_size_ - 1))) 1331 DCHECK(!(page_size_ & (page_size_ - 1)))
1239 << " page_size_ " << page_size_ << " is not a power of two."; 1332 << " page_size_ " << page_size_ << " is not a power of two.";
1240 const int kSqliteMaxPageSize = 32768; // from sqliteLimit.h 1333 const int kSqliteMaxPageSize = 32768; // from sqliteLimit.h
1241 DCHECK_LE(page_size_, kSqliteMaxPageSize); 1334 DCHECK_LE(page_size_, kSqliteMaxPageSize);
1242 const std::string sql = 1335 const std::string sql =
(...skipping 23 matching lines...) Expand all
1266 1359
1267 // Collect the rollback time manually, sql::Statement would register it as 1360 // Collect the rollback time manually, sql::Statement would register it as
1268 // query time only. 1361 // query time only.
1269 const base::TimeTicks before = Now(); 1362 const base::TimeTicks before = Now();
1270 rollback.RunWithoutTimers(); 1363 rollback.RunWithoutTimers();
1271 const base::TimeDelta delta = Now() - before; 1364 const base::TimeDelta delta = Now() - before;
1272 1365
1273 RecordUpdateTime(delta); 1366 RecordUpdateTime(delta);
1274 RecordOneEvent(EVENT_ROLLBACK); 1367 RecordOneEvent(EVENT_ROLLBACK);
1275 1368
1369 // The cache may have been accumulating dirty pages for commit. Note that in
1370 // some cases sql::Transaction can fire rollback after a database is closed.
Scott Hess - ex-Googler 2015/10/02 23:42:44 This test _could_ be at the top of this method, bu
1371 if (is_open())
1372 ReleaseCacheMemoryIfNeeded(false);
1373
1276 needs_rollback_ = false; 1374 needs_rollback_ = false;
1277 } 1375 }
1278 1376
1279 void Connection::StatementRefCreated(StatementRef* ref) { 1377 void Connection::StatementRefCreated(StatementRef* ref) {
1280 DCHECK(open_statements_.find(ref) == open_statements_.end()); 1378 DCHECK(open_statements_.find(ref) == open_statements_.end());
1281 open_statements_.insert(ref); 1379 open_statements_.insert(ref);
1282 } 1380 }
1283 1381
1284 void Connection::StatementRefDeleted(StatementRef* ref) { 1382 void Connection::StatementRefDeleted(StatementRef* ref) {
1285 StatementRefSet::iterator i = open_statements_.find(ref); 1383 StatementRefSet::iterator i = open_statements_.find(ref);
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after
1385 ignore_result(Execute(kNoWritableSchema)); 1483 ignore_result(Execute(kNoWritableSchema));
1386 1484
1387 return ret; 1485 return ret;
1388 } 1486 }
1389 1487
1390 base::TimeTicks TimeSource::Now() { 1488 base::TimeTicks TimeSource::Now() {
1391 return base::TimeTicks::Now(); 1489 return base::TimeTicks::Now();
1392 } 1490 }
1393 1491
1394 } // namespace sql 1492 } // 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