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

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: Add mockable time source. Then mock it mercilessly. 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 123 matching lines...) Expand 10 before | Expand all | Expand 10 after
134 134
135 // TODO(shess): NULL in file->pMethods has been observed on android_dbg 135 // TODO(shess): NULL in file->pMethods has been observed on android_dbg
136 // content_unittests, even though it should not be possible. 136 // content_unittests, even though it should not be possible.
137 // http://crbug.com/329982 137 // http://crbug.com/329982
138 if (!*file || !(*file)->pMethods) 138 if (!*file || !(*file)->pMethods)
139 return SQLITE_ERROR; 139 return SQLITE_ERROR;
140 140
141 return rc; 141 return rc;
142 } 142 }
143 143
144 // This should match UMA_HISTOGRAM_MEDIUM_TIMES().
145 base::HistogramBase* GetMediumTimeHistogram(const std::string& name) {
146 return base::Histogram::FactoryTimeGet(
147 name,
148 base::TimeDelta::FromMilliseconds(10),
149 base::TimeDelta::FromMinutes(3),
150 50,
151 base::HistogramBase::kUmaTargetedHistogramFlag);
152 }
153
144 } // namespace 154 } // namespace
145 155
146 namespace sql { 156 namespace sql {
147 157
148 // static 158 // static
149 Connection::ErrorIgnorerCallback* Connection::current_ignorer_cb_ = NULL; 159 Connection::ErrorIgnorerCallback* Connection::current_ignorer_cb_ = NULL;
150 160
151 // static 161 // static
152 bool Connection::ShouldIgnoreSqliteError(int error) { 162 bool Connection::ShouldIgnoreSqliteError(int error) {
153 if (!current_ignorer_cb_) 163 if (!current_ignorer_cb_)
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
212 222
213 Connection::Connection() 223 Connection::Connection()
214 : db_(NULL), 224 : db_(NULL),
215 page_size_(0), 225 page_size_(0),
216 cache_size_(0), 226 cache_size_(0),
217 exclusive_locking_(false), 227 exclusive_locking_(false),
218 restrict_to_user_(false), 228 restrict_to_user_(false),
219 transaction_nesting_(0), 229 transaction_nesting_(0),
220 needs_rollback_(false), 230 needs_rollback_(false),
221 in_memory_(false), 231 in_memory_(false),
222 poisoned_(false) { 232 poisoned_(false),
233 stats_histogram_(NULL),
234 commit_time_histogram_(NULL),
235 autocommit_time_histogram_(NULL),
236 update_time_histogram_(NULL),
237 query_time_histogram_(NULL),
238 clock_(new TimeSource()) {
223 } 239 }
224 240
225 Connection::~Connection() { 241 Connection::~Connection() {
226 Close(); 242 Close();
227 } 243 }
228 244
245 void Connection::RecordEvent(Events event, size_t count) {
246 for (size_t i = 0; i < count; ++i) {
247 UMA_HISTOGRAM_ENUMERATION("Sqlite.Stats", event, EVENT_MX);
248 }
249
250 if (stats_histogram_) {
251 for (size_t i = 0; i < count; ++i) {
252 stats_histogram_->Add(event);
253 }
254 }
255 }
256
257 void Connection::RecordCommitTime(const base::TimeDelta& delta) {
258 RecordUpdateTime(delta);
259 UMA_HISTOGRAM_MEDIUM_TIMES("Sqlite.CommitTime", delta);
260 if (commit_time_histogram_)
261 commit_time_histogram_->AddTime(delta);
262 }
263
264 void Connection::RecordAutoCommitTime(const base::TimeDelta& delta) {
265 RecordUpdateTime(delta);
266 UMA_HISTOGRAM_MEDIUM_TIMES("Sqlite.AutoCommitTime", delta);
267 if (autocommit_time_histogram_)
268 autocommit_time_histogram_->AddTime(delta);
269 }
270
271 void Connection::RecordUpdateTime(const base::TimeDelta& delta) {
272 RecordQueryTime(delta);
273 UMA_HISTOGRAM_MEDIUM_TIMES("Sqlite.UpdateTime", delta);
274 if (update_time_histogram_)
275 update_time_histogram_->AddTime(delta);
276 }
277
278 void Connection::RecordQueryTime(const base::TimeDelta& delta) {
279 UMA_HISTOGRAM_MEDIUM_TIMES("Sqlite.QueryTime", delta);
280 if (query_time_histogram_)
281 query_time_histogram_->AddTime(delta);
282 }
283
284 void Connection::RecordTimeAndChanges(
285 const base::TimeDelta& delta, bool read_only) {
286 if (read_only) {
287 RecordQueryTime(delta);
288 } else {
289 const int changes = sqlite3_changes(db_);
290 if (sqlite3_get_autocommit(db_)) {
291 RecordAutoCommitTime(delta);
292 RecordEvent(EVENT_CHANGES_AUTOCOMMIT, changes);
293 } else {
294 RecordUpdateTime(delta);
295 RecordEvent(EVENT_CHANGES, changes);
296 }
297 }
298 }
299
229 bool Connection::Open(const base::FilePath& path) { 300 bool Connection::Open(const base::FilePath& path) {
230 if (!histogram_tag_.empty()) { 301 if (!histogram_tag_.empty()) {
231 int64_t size_64 = 0; 302 int64_t size_64 = 0;
232 if (base::GetFileSize(path, &size_64)) { 303 if (base::GetFileSize(path, &size_64)) {
233 size_t sample = static_cast<size_t>(size_64 / 1024); 304 size_t sample = static_cast<size_t>(size_64 / 1024);
234 std::string full_histogram_name = "Sqlite.SizeKB." + histogram_tag_; 305 std::string full_histogram_name = "Sqlite.SizeKB." + histogram_tag_;
235 base::HistogramBase* histogram = 306 base::HistogramBase* histogram =
236 base::Histogram::FactoryGet( 307 base::Histogram::FactoryGet(
237 full_histogram_name, 1, 1000000, 50, 308 full_histogram_name, 1, 1000000, 50,
238 base::HistogramBase::kUmaTargetedHistogramFlag); 309 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 640 // When we're going to rollback, fail on this begin and don't actually
570 // mark us as entering the nested transaction. 641 // mark us as entering the nested transaction.
571 return false; 642 return false;
572 } 643 }
573 644
574 bool success = true; 645 bool success = true;
575 if (!transaction_nesting_) { 646 if (!transaction_nesting_) {
576 needs_rollback_ = false; 647 needs_rollback_ = false;
577 648
578 Statement begin(GetCachedStatement(SQL_FROM_HERE, "BEGIN TRANSACTION")); 649 Statement begin(GetCachedStatement(SQL_FROM_HERE, "BEGIN TRANSACTION"));
650 RecordOneEvent(EVENT_BEGIN);
579 if (!begin.Run()) 651 if (!begin.Run())
580 return false; 652 return false;
581 } 653 }
582 transaction_nesting_++; 654 transaction_nesting_++;
583 return success; 655 return success;
584 } 656 }
585 657
586 void Connection::RollbackTransaction() { 658 void Connection::RollbackTransaction() {
587 if (!transaction_nesting_) { 659 if (!transaction_nesting_) {
588 DLOG_IF(FATAL, !poisoned_) << "Rolling back a nonexistent transaction"; 660 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. 683 // Mark any nested transactions as failing after we've already got one.
612 return !needs_rollback_; 684 return !needs_rollback_;
613 } 685 }
614 686
615 if (needs_rollback_) { 687 if (needs_rollback_) {
616 DoRollback(); 688 DoRollback();
617 return false; 689 return false;
618 } 690 }
619 691
620 Statement commit(GetCachedStatement(SQL_FROM_HERE, "COMMIT")); 692 Statement commit(GetCachedStatement(SQL_FROM_HERE, "COMMIT"));
621 return commit.Run(); 693
694 // Collect the commit time manually, sql::Statement would register it as query
695 // time only.
696 const base::TimeTicks before = Now();
697 bool ret = commit.RunWithoutTimers();
698 const base::TimeDelta delta = Now() - before;
699
700 RecordCommitTime(delta);
701 RecordOneEvent(EVENT_COMMIT);
702
703 return ret;
622 } 704 }
623 705
624 void Connection::RollbackAllTransactions() { 706 void Connection::RollbackAllTransactions() {
625 if (transaction_nesting_ > 0) { 707 if (transaction_nesting_ > 0) {
626 transaction_nesting_ = 0; 708 transaction_nesting_ = 0;
627 DoRollback(); 709 DoRollback();
628 } 710 }
629 } 711 }
630 712
631 bool Connection::AttachDatabase(const base::FilePath& other_db_path, 713 bool Connection::AttachDatabase(const base::FilePath& other_db_path,
(...skipping 11 matching lines...) Expand all
643 } 725 }
644 726
645 bool Connection::DetachDatabase(const char* attachment_point) { 727 bool Connection::DetachDatabase(const char* attachment_point) {
646 DCHECK(ValidAttachmentPoint(attachment_point)); 728 DCHECK(ValidAttachmentPoint(attachment_point));
647 729
648 Statement s(GetUniqueStatement("DETACH DATABASE ?")); 730 Statement s(GetUniqueStatement("DETACH DATABASE ?"));
649 s.BindString(0, attachment_point); 731 s.BindString(0, attachment_point);
650 return s.Run(); 732 return s.Run();
651 } 733 }
652 734
735 // TODO(shess): Consider changing this to execute exactly one statement. If a
736 // caller wishes to execute multiple statements, that should be explicit, and
737 // perhaps tucked into an explicit transaction with rollback in case of error.
653 int Connection::ExecuteAndReturnErrorCode(const char* sql) { 738 int Connection::ExecuteAndReturnErrorCode(const char* sql) {
654 AssertIOAllowed(); 739 AssertIOAllowed();
655 if (!db_) { 740 if (!db_) {
656 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db"; 741 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db";
657 return SQLITE_ERROR; 742 return SQLITE_ERROR;
658 } 743 }
659 return sqlite3_exec(db_, sql, NULL, NULL, NULL); 744 DCHECK(sql);
745
746 RecordOneEvent(EVENT_EXECUTE);
747 int rc = SQLITE_OK;
748 while ((rc == SQLITE_OK) && *sql) {
749 sqlite3_stmt *stmt = NULL;
750 const char *leftover_sql;
751
752 const base::TimeTicks before = Now();
753 rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, &leftover_sql);
754 sql = leftover_sql;
755
756 // Stop if an error is encountered.
757 if (rc != SQLITE_OK)
758 break;
759
760 // This happens if |sql| originally only contained comments or whitespace.
761 // TODO(shess): Audit to see if this can become a DCHECK(). Having
762 // extraneous comments and whitespace in the SQL statements increases
763 // runtime cost and can easily be shifted out to the C++ layer.
764 if (!stmt)
765 continue;
766
767 // Save for use after statement is finalized.
768 const bool read_only = !!sqlite3_stmt_readonly(stmt);
769
770 RecordOneEvent(Connection::EVENT_STATEMENT_RUN);
771 while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
772 // TODO(shess): Audit to see if this can become a DCHECK. I think PRAGMA
773 // is the only legitimate case for this.
774 RecordOneEvent(Connection::EVENT_STATEMENT_ROWS);
775 }
776
777 // sqlite3_finalize() returns SQLITE_OK if the most recent sqlite3_step()
778 // returned SQLITE_DONE or SQLITE_ROW, otherwise the error code.
779 rc = sqlite3_finalize(stmt);
780 if (rc == SQLITE_OK)
781 RecordOneEvent(Connection::EVENT_STATEMENT_SUCCESS);
782
783 // sqlite3_exec() does this, presumably to avoid spinning the parser for
784 // trailing whitespace.
785 // TODO(shess): Audit to see if this can become a DCHECK.
786 while (IsAsciiWhitespace(*sql)) {
787 sql++;
788 }
789
790 const base::TimeDelta delta = Now() - before;
791 RecordTimeAndChanges(delta, read_only);
792 }
793 return rc;
660 } 794 }
661 795
662 bool Connection::Execute(const char* sql) { 796 bool Connection::Execute(const char* sql) {
663 if (!db_) { 797 if (!db_) {
664 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db"; 798 DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db";
665 return false; 799 return false;
666 } 800 }
667 801
668 int error = ExecuteAndReturnErrorCode(sql); 802 int error = ExecuteAndReturnErrorCode(sql);
669 if (error != SQLITE_OK) 803 if (error != SQLITE_OK)
(...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after
879 AssertIOAllowed(); 1013 AssertIOAllowed();
880 1014
881 if (db_) { 1015 if (db_) {
882 DLOG(FATAL) << "sql::Connection is already open."; 1016 DLOG(FATAL) << "sql::Connection is already open.";
883 return false; 1017 return false;
884 } 1018 }
885 1019
886 // Make sure sqlite3_initialize() is called before anything else. 1020 // Make sure sqlite3_initialize() is called before anything else.
887 InitializeSqlite(); 1021 InitializeSqlite();
888 1022
1023 // Setup the stats histograms immediately rather than allocating lazily.
1024 // Connections which won't exercise all of these probably shouldn't exist.
1025 if (!histogram_tag_.empty()) {
1026 stats_histogram_ =
1027 base::LinearHistogram::FactoryGet(
1028 "Sqlite.Stats." + histogram_tag_, 1, EVENT_MX, EVENT_MX + 1,
1029 base::HistogramBase::kUmaTargetedHistogramFlag);
1030
1031 // The timer setup matches UMA_HISTOGRAM_MEDIUM_TIMES(). 3 minutes is an
1032 // unreasonable time for any single operation, so there is not much value to
1033 // knowing if it was 3 minutes or 5 minutes. In reality at that point
1034 // things are entirely busted.
1035 commit_time_histogram_ =
1036 GetMediumTimeHistogram("Sqlite.CommitTime." + histogram_tag_);
1037
1038 autocommit_time_histogram_ =
1039 GetMediumTimeHistogram("Sqlite.AutoCommitTime." + histogram_tag_);
1040
1041 update_time_histogram_ =
1042 GetMediumTimeHistogram("Sqlite.UpdateTime." + histogram_tag_);
1043
1044 query_time_histogram_ =
1045 GetMediumTimeHistogram("Sqlite.QueryTime." + histogram_tag_);
1046 }
1047
889 // If |poisoned_| is set, it means an error handler called 1048 // If |poisoned_| is set, it means an error handler called
890 // RazeAndClose(). Until regular Close() is called, the caller 1049 // RazeAndClose(). Until regular Close() is called, the caller
891 // should be treating the database as open, but is_open() currently 1050 // should be treating the database as open, but is_open() currently
892 // only considers the sqlite3 handle's state. 1051 // only considers the sqlite3 handle's state.
893 // TODO(shess): Revise is_open() to consider poisoned_, and review 1052 // TODO(shess): Revise is_open() to consider poisoned_, and review
894 // to see if any non-testing code even depends on it. 1053 // to see if any non-testing code even depends on it.
895 DLOG_IF(FATAL, poisoned_) << "sql::Connection is already open."; 1054 DLOG_IF(FATAL, poisoned_) << "sql::Connection is already open.";
896 poisoned_ = false; 1055 poisoned_ = false;
897 1056
898 int err = sqlite3_open(file_name.c_str(), &db_); 1057 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) 1180 if (was_poisoned && retry_flag == RETRY_ON_POISON)
1022 return OpenInternal(file_name, NO_RETRY); 1181 return OpenInternal(file_name, NO_RETRY);
1023 return false; 1182 return false;
1024 } 1183 }
1025 1184
1026 return true; 1185 return true;
1027 } 1186 }
1028 1187
1029 void Connection::DoRollback() { 1188 void Connection::DoRollback() {
1030 Statement rollback(GetCachedStatement(SQL_FROM_HERE, "ROLLBACK")); 1189 Statement rollback(GetCachedStatement(SQL_FROM_HERE, "ROLLBACK"));
1031 rollback.Run(); 1190
1191 // Collect the rollback time manually, sql::Statement would register it as
1192 // query time only.
1193 const base::TimeTicks before = Now();
1194 rollback.RunWithoutTimers();
1195 const base::TimeDelta delta = Now() - before;
1196
1197 RecordUpdateTime(delta);
1198 RecordOneEvent(EVENT_ROLLBACK);
1199
1032 needs_rollback_ = false; 1200 needs_rollback_ = false;
1033 } 1201 }
1034 1202
1035 void Connection::StatementRefCreated(StatementRef* ref) { 1203 void Connection::StatementRefCreated(StatementRef* ref) {
1036 DCHECK(open_statements_.find(ref) == open_statements_.end()); 1204 DCHECK(open_statements_.find(ref) == open_statements_.end());
1037 open_statements_.insert(ref); 1205 open_statements_.insert(ref);
1038 } 1206 }
1039 1207
1040 void Connection::StatementRefDeleted(StatementRef* ref) { 1208 void Connection::StatementRefDeleted(StatementRef* ref) {
1041 StatementRefSet::iterator i = open_statements_.find(ref); 1209 StatementRefSet::iterator i = open_statements_.find(ref);
1042 if (i == open_statements_.end()) 1210 if (i == open_statements_.end())
1043 DLOG(FATAL) << "Could not find statement"; 1211 DLOG(FATAL) << "Could not find statement";
1044 else 1212 else
1045 open_statements_.erase(i); 1213 open_statements_.erase(i);
1046 } 1214 }
1047 1215
1216 void Connection::set_histogram_tag(const std::string& tag) {
1217 DCHECK(!is_open());
1218 histogram_tag_ = tag;
1219 }
1220
1048 void Connection::AddTaggedHistogram(const std::string& name, 1221 void Connection::AddTaggedHistogram(const std::string& name,
1049 size_t sample) const { 1222 size_t sample) const {
1050 if (histogram_tag_.empty()) 1223 if (histogram_tag_.empty())
1051 return; 1224 return;
1052 1225
1053 // TODO(shess): The histogram macros create a bit of static storage 1226 // TODO(shess): The histogram macros create a bit of static storage
1054 // for caching the histogram object. This code shouldn't execute 1227 // for caching the histogram object. This code shouldn't execute
1055 // often enough for such caching to be crucial. If it becomes an 1228 // often enough for such caching to be crucial. If it becomes an
1056 // issue, the object could be cached alongside histogram_prefix_. 1229 // issue, the object could be cached alongside histogram_prefix_.
1057 std::string full_histogram_name = name + "." + histogram_tag_; 1230 std::string full_histogram_name = name + "." + histogram_tag_;
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
1130 ret = stmt.Succeeded(); 1303 ret = stmt.Succeeded();
1131 } 1304 }
1132 1305
1133 // Best effort to put things back as they were before. 1306 // Best effort to put things back as they were before.
1134 const char kNoWritableSchema[] = "PRAGMA writable_schema = OFF"; 1307 const char kNoWritableSchema[] = "PRAGMA writable_schema = OFF";
1135 ignore_result(Execute(kNoWritableSchema)); 1308 ignore_result(Execute(kNoWritableSchema));
1136 1309
1137 return ret; 1310 return ret;
1138 } 1311 }
1139 1312
1313 base::TimeTicks TimeSource::Now() {
1314 return base::TimeTicks::Now();
1315 }
1316
1140 } // namespace sql 1317 } // 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