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

Side by Side Diff: sql/connection.cc

Issue 1145833002: [sql] Stats gathering for sql/ APIs. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: in-memory for timing, fix int mismatch for windows Created 5 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
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/files/file_path.h" 9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h" 10 #include "base/files/file_util.h"
(...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after
212 212
213 Connection::Connection() 213 Connection::Connection()
214 : db_(NULL), 214 : db_(NULL),
215 page_size_(0), 215 page_size_(0),
216 cache_size_(0), 216 cache_size_(0),
217 exclusive_locking_(false), 217 exclusive_locking_(false),
218 restrict_to_user_(false), 218 restrict_to_user_(false),
219 transaction_nesting_(0), 219 transaction_nesting_(0),
220 needs_rollback_(false), 220 needs_rollback_(false),
221 in_memory_(false), 221 in_memory_(false),
222 poisoned_(false) { 222 poisoned_(false),
223 stats_histogram_(NULL),
224 commit_time_histogram_(NULL),
225 autocommit_time_histogram_(NULL),
226 update_time_histogram_(NULL),
227 query_time_histogram_(NULL) {
223 } 228 }
224 229
225 Connection::~Connection() { 230 Connection::~Connection() {
226 Close(); 231 Close();
227 } 232 }
228 233
234 void Connection::RecordEvent(Events event, size_t count) {
235 for (size_t i = 0; i < count; ++i) {
236 UMA_HISTOGRAM_ENUMERATION("Sqlite.Stats", event, EVENT_MX);
237 }
238
239 if (stats_histogram_) {
240 for (size_t i = 0; i < count; ++i) {
241 stats_histogram_->Add(event);
242 }
243 }
244 }
245
246 void Connection::CommitTime(const base::TimeDelta& delta) {
247 UpdateTime(delta);
248 UMA_HISTOGRAM_MEDIUM_TIMES("Sqlite.CommitTime", delta);
249 if (commit_time_histogram_)
250 commit_time_histogram_->AddTime(delta);
251 }
252
253 void Connection::AutoCommitTime(const base::TimeDelta& delta) {
254 UpdateTime(delta);
255 UMA_HISTOGRAM_MEDIUM_TIMES("Sqlite.AutoCommitTime", delta);
256 if (autocommit_time_histogram_)
257 autocommit_time_histogram_->AddTime(delta);
258 }
259
260 void Connection::UpdateTime(const base::TimeDelta& delta) {
261 QueryTime(delta);
262 UMA_HISTOGRAM_MEDIUM_TIMES("Sqlite.UpdateTime", delta);
263 if (update_time_histogram_)
264 update_time_histogram_->AddTime(delta);
265 }
266
267 void Connection::QueryTime(const base::TimeDelta& delta) {
268 UMA_HISTOGRAM_MEDIUM_TIMES("Sqlite.QueryTime", delta);
269 if (query_time_histogram_)
270 query_time_histogram_->AddTime(delta);
271 }
272
273 void Connection::ChangeHelper(const base::TimeDelta& delta, bool read_only) {
274 if (read_only) {
275 QueryTime(delta);
276 } else {
277 const int changes = sqlite3_changes(db_);
278 if (sqlite3_get_autocommit(db_)) {
279 AutoCommitTime(delta);
280 RecordEvent(EVENT_CHANGES_AUTOCOMMIT, changes);
281 } else {
282 UpdateTime(delta);
283 RecordEvent(EVENT_CHANGES, changes);
284 }
285 }
286 }
287
229 bool Connection::Open(const base::FilePath& path) { 288 bool Connection::Open(const base::FilePath& path) {
230 if (!histogram_tag_.empty()) { 289 if (!histogram_tag_.empty()) {
231 int64_t size_64 = 0; 290 int64_t size_64 = 0;
232 if (base::GetFileSize(path, &size_64)) { 291 if (base::GetFileSize(path, &size_64)) {
233 size_t sample = static_cast<size_t>(size_64 / 1024); 292 size_t sample = static_cast<size_t>(size_64 / 1024);
234 std::string full_histogram_name = "Sqlite.SizeKB." + histogram_tag_; 293 std::string full_histogram_name = "Sqlite.SizeKB." + histogram_tag_;
235 base::HistogramBase* histogram = 294 base::HistogramBase* histogram =
236 base::Histogram::FactoryGet( 295 base::Histogram::FactoryGet(
237 full_histogram_name, 1, 1000000, 50, 296 full_histogram_name, 1, 1000000, 50,
238 base::HistogramBase::kUmaTargetedHistogramFlag); 297 base::HistogramBase::kUmaTargetedHistogramFlag);
(...skipping 330 matching lines...) Expand 10 before | Expand all | Expand 10 after
569 // When we're going to rollback, fail on this begin and don't actually 628 // When we're going to rollback, fail on this begin and don't actually
570 // mark us as entering the nested transaction. 629 // mark us as entering the nested transaction.
571 return false; 630 return false;
572 } 631 }
573 632
574 bool success = true; 633 bool success = true;
575 if (!transaction_nesting_) { 634 if (!transaction_nesting_) {
576 needs_rollback_ = false; 635 needs_rollback_ = false;
577 636
578 Statement begin(GetCachedStatement(SQL_FROM_HERE, "BEGIN TRANSACTION")); 637 Statement begin(GetCachedStatement(SQL_FROM_HERE, "BEGIN TRANSACTION"));
638 RecordOneEvent(EVENT_BEGIN);
579 if (!begin.Run()) 639 if (!begin.Run())
580 return false; 640 return false;
581 } 641 }
582 transaction_nesting_++; 642 transaction_nesting_++;
583 return success; 643 return success;
584 } 644 }
585 645
586 void Connection::RollbackTransaction() { 646 void Connection::RollbackTransaction() {
587 if (!transaction_nesting_) { 647 if (!transaction_nesting_) {
588 DLOG_IF(FATAL, !poisoned_) << "Rolling back a nonexistent transaction"; 648 DLOG_IF(FATAL, !poisoned_) << "Rolling back a nonexistent transaction";
(...skipping 22 matching lines...) Expand all
611 // Mark any nested transactions as failing after we've already got one. 671 // Mark any nested transactions as failing after we've already got one.
612 return !needs_rollback_; 672 return !needs_rollback_;
613 } 673 }
614 674
615 if (needs_rollback_) { 675 if (needs_rollback_) {
616 DoRollback(); 676 DoRollback();
617 return false; 677 return false;
618 } 678 }
619 679
620 Statement commit(GetCachedStatement(SQL_FROM_HERE, "COMMIT")); 680 Statement commit(GetCachedStatement(SQL_FROM_HERE, "COMMIT"));
621 return commit.Run(); 681
682 // Collect the commit time manually, sql::Statement would register it as query
683 // time only.
684 const base::TimeTicks before = base::TimeTicks::Now();
685 bool ret = commit.RunWithoutTimers();
686 const base::TimeDelta delta = base::TimeTicks::Now() - before;
687
688 CommitTime(delta);
689 RecordOneEvent(EVENT_COMMIT);
690
691 return ret;
622 } 692 }
623 693
624 void Connection::RollbackAllTransactions() { 694 void Connection::RollbackAllTransactions() {
625 if (transaction_nesting_ > 0) { 695 if (transaction_nesting_ > 0) {
626 transaction_nesting_ = 0; 696 transaction_nesting_ = 0;
627 DoRollback(); 697 DoRollback();
628 } 698 }
629 } 699 }
630 700
631 bool Connection::AttachDatabase(const base::FilePath& other_db_path, 701 bool Connection::AttachDatabase(const base::FilePath& other_db_path,
(...skipping 11 matching lines...) Expand all
643 } 713 }
644 714
645 bool Connection::DetachDatabase(const char* attachment_point) { 715 bool Connection::DetachDatabase(const char* attachment_point) {
646 DCHECK(ValidAttachmentPoint(attachment_point)); 716 DCHECK(ValidAttachmentPoint(attachment_point));
647 717
648 Statement s(GetUniqueStatement("DETACH DATABASE ?")); 718 Statement s(GetUniqueStatement("DETACH DATABASE ?"));
649 s.BindString(0, attachment_point); 719 s.BindString(0, attachment_point);
650 return s.Run(); 720 return s.Run();
651 } 721 }
652 722
723 // TODO(shess): Consider changing this to execute exactly one statement. If a
724 // caller wishes to execute multiple statements, that should be explicit, and
725 // perhaps tucked into an explicit transaction with rollback in case of error.
653 int Connection::ExecuteAndReturnErrorCode(const char* sql) { 726 int Connection::ExecuteAndReturnErrorCode(const char* sql) {
654 AssertIOAllowed(); 727 AssertIOAllowed();
655 if (!db_) { 728 if (!db_) {
656 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db"; 729 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db";
657 return SQLITE_ERROR; 730 return SQLITE_ERROR;
658 } 731 }
659 return sqlite3_exec(db_, sql, NULL, NULL, NULL); 732 DCHECK(sql);
733
734 RecordOneEvent(EVENT_EXECUTE);
735 int rc = SQLITE_OK;
736 while ((rc == SQLITE_OK) && *sql) {
737 sqlite3_stmt *stmt = NULL;
738 const char *leftover_sql;
739
740 const base::TimeTicks before = base::TimeTicks::Now();
741 rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, &leftover_sql);
742 sql = leftover_sql;
743
744 // Stop if an error is encountered.
745 if (rc != SQLITE_OK)
746 break;
747
748 // This happens if |sql| originally only contained comments or whitespace.
749 // TODO(shess): Audit to see if this can become a DCHECK(). Having
750 // extraneous comments and whitespace in the SQL statements increases
751 // runtime cost and can easily be shifted out to the C++ layer.
752 if (!stmt)
753 continue;
754
755 // Save for use after statement is finalized.
756 const bool read_only = !!sqlite3_stmt_readonly(stmt);
757
758 RecordOneEvent(Connection::EVENT_STATEMENT_RUN);
759 while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
760 // TODO(shess): Audit to see if this can become a DCHECK. I think PRAGMA
761 // is the only legitimate case for this.
762 RecordOneEvent(Connection::EVENT_STATEMENT_ROWS);
763 }
764
765 // sqlite3_finalize() returns SQLITE_OK if the most recent sqlite3_step()
766 // returned SQLITE_DONE or SQLITE_ROW, otherwise the error code.
767 rc = sqlite3_finalize(stmt);
768 if (rc == SQLITE_OK)
769 RecordOneEvent(Connection::EVENT_STATEMENT_SUCCESS);
770
771 // sqlite3_exec() does this, presumably to avoid spinning the parser for
772 // trailing whitespace.
773 // TODO(shess): Audit to see if this can become a DCHECK.
774 while (IsAsciiWhitespace(*sql)) {
775 sql++;
776 }
777
778 const base::TimeDelta delta = base::TimeTicks::Now() - before;
779 ChangeHelper(delta, read_only);
780 }
781 return rc;
660 } 782 }
661 783
662 bool Connection::Execute(const char* sql) { 784 bool Connection::Execute(const char* sql) {
663 if (!db_) { 785 if (!db_) {
664 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db"; 786 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db";
665 return false; 787 return false;
666 } 788 }
667 789
668 int error = ExecuteAndReturnErrorCode(sql); 790 int error = ExecuteAndReturnErrorCode(sql);
669 if (error != SQLITE_OK) 791 if (error != SQLITE_OK)
(...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after
879 AssertIOAllowed(); 1001 AssertIOAllowed();
880 1002
881 if (db_) { 1003 if (db_) {
882 DLOG(FATAL) << "sql::Connection is already open."; 1004 DLOG(FATAL) << "sql::Connection is already open.";
883 return false; 1005 return false;
884 } 1006 }
885 1007
886 // Make sure sqlite3_initialize() is called before anything else. 1008 // Make sure sqlite3_initialize() is called before anything else.
887 InitializeSqlite(); 1009 InitializeSqlite();
888 1010
1011 // Setup the stats histograms immediately rather than allocating lazily.
1012 // Connections which won't exercise all of these probably shouldn't exist.
1013 if (!histogram_tag_.empty()) {
1014 stats_histogram_ =
1015 base::LinearHistogram::FactoryGet(
1016 "Sqlite.Stats." + histogram_tag_, 1, EVENT_MX, EVENT_MX + 1,
1017 base::HistogramBase::kUmaTargetedHistogramFlag);
1018
1019 // The timer setup matches UMA_HISTOGRAM_MEDIUM_TIMES(). 3 minutes is an
1020 // unreasonable time for any single operation, so there is not much value to
1021 // knowing if it was 3 minutes or 5 minutes. In reality at that point
1022 // things are entirely busted.
1023 commit_time_histogram_ =
1024 base::Histogram::FactoryTimeGet(
1025 "Sqlite.CommitTime." + histogram_tag_,
1026 base::TimeDelta::FromMilliseconds(10),
1027 base::TimeDelta::FromMinutes(3),
1028 50,
1029 base::HistogramBase::kUmaTargetedHistogramFlag);
1030
1031 autocommit_time_histogram_ =
1032 base::Histogram::FactoryTimeGet(
1033 "Sqlite.AutoCommitTime." + histogram_tag_,
1034 base::TimeDelta::FromMilliseconds(10),
1035 base::TimeDelta::FromMinutes(3),
1036 50,
1037 base::HistogramBase::kUmaTargetedHistogramFlag);
1038
1039 update_time_histogram_ =
1040 base::Histogram::FactoryTimeGet(
1041 "Sqlite.UpdateTime." + histogram_tag_,
1042 base::TimeDelta::FromMilliseconds(10),
1043 base::TimeDelta::FromMinutes(3),
1044 50,
1045 base::HistogramBase::kUmaTargetedHistogramFlag);
1046
1047 query_time_histogram_ =
1048 base::Histogram::FactoryTimeGet(
1049 "Sqlite.QueryTime." + histogram_tag_,
1050 base::TimeDelta::FromMilliseconds(10),
1051 base::TimeDelta::FromMinutes(3),
1052 50,
1053 base::HistogramBase::kUmaTargetedHistogramFlag);
1054 }
1055
889 // If |poisoned_| is set, it means an error handler called 1056 // If |poisoned_| is set, it means an error handler called
890 // RazeAndClose(). Until regular Close() is called, the caller 1057 // RazeAndClose(). Until regular Close() is called, the caller
891 // should be treating the database as open, but is_open() currently 1058 // should be treating the database as open, but is_open() currently
892 // only considers the sqlite3 handle's state. 1059 // only considers the sqlite3 handle's state.
893 // TODO(shess): Revise is_open() to consider poisoned_, and review 1060 // TODO(shess): Revise is_open() to consider poisoned_, and review
894 // to see if any non-testing code even depends on it. 1061 // to see if any non-testing code even depends on it.
895 DLOG_IF(FATAL, poisoned_) << "sql::Connection is already open."; 1062 DLOG_IF(FATAL, poisoned_) << "sql::Connection is already open.";
896 poisoned_ = false; 1063 poisoned_ = false;
897 1064
898 int err = sqlite3_open(file_name.c_str(), &db_); 1065 int err = sqlite3_open(file_name.c_str(), &db_);
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
1021 if (was_poisoned && retry_flag == RETRY_ON_POISON) 1188 if (was_poisoned && retry_flag == RETRY_ON_POISON)
1022 return OpenInternal(file_name, NO_RETRY); 1189 return OpenInternal(file_name, NO_RETRY);
1023 return false; 1190 return false;
1024 } 1191 }
1025 1192
1026 return true; 1193 return true;
1027 } 1194 }
1028 1195
1029 void Connection::DoRollback() { 1196 void Connection::DoRollback() {
1030 Statement rollback(GetCachedStatement(SQL_FROM_HERE, "ROLLBACK")); 1197 Statement rollback(GetCachedStatement(SQL_FROM_HERE, "ROLLBACK"));
1031 rollback.Run(); 1198
1199 // Collect the rollback time manually, sql::Statement would register it as
1200 // query time only.
1201 const base::TimeTicks before = base::TimeTicks::Now();
1202 rollback.RunWithoutTimers();
1203 const base::TimeDelta delta = base::TimeTicks::Now() - before;
1204
1205 UpdateTime(delta);
1206 RecordOneEvent(EVENT_ROLLBACK);
1207
1032 needs_rollback_ = false; 1208 needs_rollback_ = false;
1033 } 1209 }
1034 1210
1035 void Connection::StatementRefCreated(StatementRef* ref) { 1211 void Connection::StatementRefCreated(StatementRef* ref) {
1036 DCHECK(open_statements_.find(ref) == open_statements_.end()); 1212 DCHECK(open_statements_.find(ref) == open_statements_.end());
1037 open_statements_.insert(ref); 1213 open_statements_.insert(ref);
1038 } 1214 }
1039 1215
1040 void Connection::StatementRefDeleted(StatementRef* ref) { 1216 void Connection::StatementRefDeleted(StatementRef* ref) {
1041 StatementRefSet::iterator i = open_statements_.find(ref); 1217 StatementRefSet::iterator i = open_statements_.find(ref);
1042 if (i == open_statements_.end()) 1218 if (i == open_statements_.end())
1043 DLOG(FATAL) << "Could not find statement"; 1219 DLOG(FATAL) << "Could not find statement";
1044 else 1220 else
1045 open_statements_.erase(i); 1221 open_statements_.erase(i);
1046 } 1222 }
1047 1223
1224 void Connection::set_histogram_tag(const std::string& tag) {
1225 DCHECK(!is_open());
1226 histogram_tag_ = tag;
1227 }
1228
1048 void Connection::AddTaggedHistogram(const std::string& name, 1229 void Connection::AddTaggedHistogram(const std::string& name,
1049 size_t sample) const { 1230 size_t sample) const {
1050 if (histogram_tag_.empty()) 1231 if (histogram_tag_.empty())
1051 return; 1232 return;
1052 1233
1053 // TODO(shess): The histogram macros create a bit of static storage 1234 // TODO(shess): The histogram macros create a bit of static storage
1054 // for caching the histogram object. This code shouldn't execute 1235 // for caching the histogram object. This code shouldn't execute
1055 // often enough for such caching to be crucial. If it becomes an 1236 // often enough for such caching to be crucial. If it becomes an
1056 // issue, the object could be cached alongside histogram_prefix_. 1237 // issue, the object could be cached alongside histogram_prefix_.
1057 std::string full_histogram_name = name + "." + histogram_tag_; 1238 std::string full_histogram_name = name + "." + histogram_tag_;
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
1131 } 1312 }
1132 1313
1133 // Best effort to put things back as they were before. 1314 // Best effort to put things back as they were before.
1134 const char kNoWritableSchema[] = "PRAGMA writable_schema = OFF"; 1315 const char kNoWritableSchema[] = "PRAGMA writable_schema = OFF";
1135 ignore_result(Execute(kNoWritableSchema)); 1316 ignore_result(Execute(kNoWritableSchema));
1136 1317
1137 return ret; 1318 return ret;
1138 } 1319 }
1139 1320
1140 } // namespace sql 1321 } // namespace sql
OLDNEW
« no previous file with comments | « sql/connection.h ('k') | sql/connection_unittest.cc » ('j') | sql/connection_unittest.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698