diff options
author | shess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-28 05:27:47 +0000 |
---|---|---|
committer | shess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-28 05:27:47 +0000 |
commit | c7e6ac0197dc474cc17aa48600e06bb08e00c5d4 (patch) | |
tree | 4dd392691c674b8b8d1ccd8e4f439535ae286192 /sql | |
parent | d3eecdd6edebe3fd2e847099abe02476bdcf7f57 (diff) | |
download | chromium_src-c7e6ac0197dc474cc17aa48600e06bb08e00c5d4.zip chromium_src-c7e6ac0197dc474cc17aa48600e06bb08e00c5d4.tar.gz chromium_src-c7e6ac0197dc474cc17aa48600e06bb08e00c5d4.tar.bz2 |
[sql] Annotate sql::Recovery failure cases.
A large number of thumbnail_database.cc recovery failures are in
building the scoped recovery instance. Break down failure cases for
sql::Recovery to see if there is any action to be taken.
Additionally, track failure calling sql::Recovery::Recovered().
BUG=109482
TBR=sky@chromium.org
Review URL: https://codereview.chromium.org/83323005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@237680 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sql')
-rw-r--r-- | sql/recovery.cc | 137 |
1 files changed, 128 insertions, 9 deletions
diff --git a/sql/recovery.cc b/sql/recovery.cc index 46f609b..89abbc9 100644 --- a/sql/recovery.cc +++ b/sql/recovery.cc @@ -7,6 +7,7 @@ #include "base/files/file_path.h" #include "base/format_macros.h" #include "base/logging.h" +#include "base/metrics/histogram.h" #include "base/metrics/sparse_histogram.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" @@ -16,6 +17,81 @@ namespace sql { +namespace { + +enum RecoveryEventType { + // Init() completed successfully. + RECOVERY_SUCCESS_INIT = 0, + + // Failed to open temporary database to recover into. + RECOVERY_FAILED_OPEN_TEMPORARY, + + // Failed to initialize recover vtable system. + RECOVERY_FAILED_VIRTUAL_TABLE_INIT, + + // System SQLite doesn't support vtable. + RECOVERY_FAILED_VIRTUAL_TABLE_SYSTEM_SQLITE, + + // Failed attempting to enable writable_schema. + RECOVERY_FAILED_WRITABLE_SCHEMA, + + // Failed to attach the corrupt database to the temporary database. + RECOVERY_FAILED_ATTACH, + + // Backup() successfully completed. + RECOVERY_SUCCESS_BACKUP, + + // Failed sqlite3_backup_init(). Error code in Sqlite.RecoveryHandle. + RECOVERY_FAILED_BACKUP_INIT, + + // Failed sqlite3_backup_step(). Error code in Sqlite.RecoveryStep. + RECOVERY_FAILED_BACKUP_STEP, + + // AutoRecoverTable() successfully completed. + RECOVERY_SUCCESS_AUTORECOVER, + + // The target table contained a type which the code is not equipped + // to handle. This should only happen if things are fubar. + RECOVERY_FAILED_AUTORECOVER_UNRECOGNIZED_TYPE, + + // The target table does not exist. + RECOVERY_FAILED_AUTORECOVER_MISSING_TABLE, + + // The recovery virtual table creation failed. + RECOVERY_FAILED_AUTORECOVER_CREATE, + + // Copying data from the recovery table to the target table failed. + RECOVERY_FAILED_AUTORECOVER_INSERT, + + // Dropping the recovery virtual table failed. + RECOVERY_FAILED_AUTORECOVER_DROP, + + // SetupMeta() successfully completed. + RECOVERY_SUCCESS_SETUP_META, + + // Failure creating recovery meta table. + RECOVERY_FAILED_META_CREATE, + + // GetMetaVersionNumber() successfully completed. + RECOVERY_SUCCESS_META_VERSION, + + // Failed in querying recovery meta table. + RECOVERY_FAILED_META_QUERY, + + // No version key in recovery meta table. + RECOVERY_FAILED_META_NO_VERSION, + + // Always keep this at the end. + RECOVERY_EVENT_MAX, +}; + +void RecordRecoveryEvent(RecoveryEventType recovery_event) { + UMA_HISTOGRAM_ENUMERATION("Sqlite.RecoveryEvents", + recovery_event, RECOVERY_EVENT_MAX); +} + +} // namespace + // static bool Recovery::FullRecoverySupported() { // TODO(shess): See comment in Init(). @@ -102,8 +178,14 @@ bool Recovery::Init(const base::FilePath& db_path) { ignore_result(db_->Execute("PRAGMA locking_mode=NORMAL")); ignore_result(db_->Execute("SELECT COUNT(*) FROM sqlite_master")); - if (!recover_db_.OpenTemporary()) + // TODO(shess): If this is a common failure case, it might be + // possible to fall back to a memory database. But it probably + // implies that the SQLite tmpdir logic is busted, which could cause + // a variety of other random issues in our code. + if (!recover_db_.OpenTemporary()) { + RecordRecoveryEvent(RECOVERY_FAILED_OPEN_TEMPORARY); return false; + } // TODO(shess): Figure out a story for USE_SYSTEM_SQLITE. The // virtual table implementation relies on SQLite internals for some @@ -117,20 +199,29 @@ bool Recovery::Init(const base::FilePath& db_path) { #if !defined(USE_SYSTEM_SQLITE) int rc = recoverVtableInit(recover_db_.db_); if (rc != SQLITE_OK) { + RecordRecoveryEvent(RECOVERY_FAILED_VIRTUAL_TABLE_INIT); LOG(ERROR) << "Failed to initialize recover module: " << recover_db_.GetErrorMessage(); return false; } +#else + // If this is infrequent enough, just wire it to Raze(). + RecordRecoveryEvent(RECOVERY_FAILED_VIRTUAL_TABLE_SYSTEM_SQLITE); #endif // Turn on |SQLITE_RecoveryMode| for the handle, which allows // reading certain broken databases. - if (!recover_db_.Execute("PRAGMA writable_schema=1")) + if (!recover_db_.Execute("PRAGMA writable_schema=1")) { + RecordRecoveryEvent(RECOVERY_FAILED_WRITABLE_SCHEMA); return false; + } - if (!recover_db_.AttachDatabase(db_path, "corrupt")) + if (!recover_db_.AttachDatabase(db_path, "corrupt")) { + RecordRecoveryEvent(RECOVERY_FAILED_ATTACH); return false; + } + RecordRecoveryEvent(RECOVERY_SUCCESS_INIT); return true; } @@ -173,11 +264,14 @@ bool Recovery::Backup() { sqlite3_backup* backup = sqlite3_backup_init(db_->db_, kMain, recover_db_.db_, kMain); if (!backup) { + RecordRecoveryEvent(RECOVERY_FAILED_BACKUP_INIT); + // Error code is in the destination database handle. - int err = sqlite3_errcode(db_->db_); + int err = sqlite3_extended_errcode(db_->db_); UMA_HISTOGRAM_SPARSE_SLOWLY("Sqlite.RecoveryHandle", err); LOG(ERROR) << "sqlite3_backup_init() failed: " << sqlite3_errmsg(db_->db_); + return false; } @@ -192,6 +286,7 @@ bool Recovery::Backup() { DCHECK_GT(pages, 0); if (rc != SQLITE_DONE) { + RecordRecoveryEvent(RECOVERY_FAILED_BACKUP_STEP); UMA_HISTOGRAM_SPARSE_SLOWLY("Sqlite.RecoveryStep", rc); LOG(ERROR) << "sqlite3_backup_step() failed: " << sqlite3_errmsg(db_->db_); @@ -220,6 +315,7 @@ bool Recovery::Backup() { // Clean up the recovery db, and terminate the main database // connection. + RecordRecoveryEvent(RECOVERY_SUCCESS_BACKUP); Shutdown(POISON); return true; } @@ -306,6 +402,7 @@ bool Recovery::AutoRecoverTable(const char* table_name, // - other -> "NUMERIC" // Just code those in as they come up. NOTREACHED() << " Unsupported type " << column_type; + RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVER_UNRECOGNIZED_TYPE); return false; } @@ -336,8 +433,10 @@ bool Recovery::AutoRecoverTable(const char* table_name, } // Receiving no column information implies that the table doesn't exist. - if (create_column_decls.empty()) + if (create_column_decls.empty()) { + RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVER_MISSING_TABLE); return false; + } // If the PRIMARY KEY was a single INTEGER column, convert it to ROWID. if (pk_column_count == 1 && !rowid_decl.empty()) @@ -366,10 +465,13 @@ bool Recovery::AutoRecoverTable(const char* table_name, std::string recover_drop(base::StringPrintf( "DROP TABLE temp.recover_%s", table_name)); - if (!db()->Execute(recover_create.c_str())) + if (!db()->Execute(recover_create.c_str())) { + RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVER_CREATE); return false; + } if (!db()->Execute(recover_insert.c_str())) { + RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVER_INSERT); ignore_result(db()->Execute(recover_drop.c_str())); return false; } @@ -377,7 +479,12 @@ bool Recovery::AutoRecoverTable(const char* table_name, *rows_recovered = db()->GetLastChangeCount(); // TODO(shess): Is leaving the recover table around a breaker? - return db()->Execute(recover_drop.c_str()); + if (!db()->Execute(recover_drop.c_str())) { + RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVER_DROP); + return false; + } + RecordRecoveryEvent(RECOVERY_SUCCESS_AUTORECOVER); + return true; } bool Recovery::SetupMeta() { @@ -388,7 +495,12 @@ bool Recovery::SetupMeta() { "key TEXT NOT NULL," "value ANY" // Whatever is stored. ")"; - return db()->Execute(kCreateSql); + if (!db()->Execute(kCreateSql)) { + RecordRecoveryEvent(RECOVERY_FAILED_META_CREATE); + return false; + } + RecordRecoveryEvent(RECOVERY_SUCCESS_SETUP_META); + return true; } bool Recovery::GetMetaVersionNumber(int* version) { @@ -400,9 +512,16 @@ bool Recovery::GetMetaVersionNumber(int* version) { const char kVersionSql[] = "SELECT value FROM temp.recover_meta WHERE key = 'version'"; sql::Statement recovery_version(db()->GetUniqueStatement(kVersionSql)); - if (!recovery_version.Step()) + if (!recovery_version.Step()) { + if (!recovery_version.Succeeded()) { + RecordRecoveryEvent(RECOVERY_FAILED_META_QUERY); + } else { + RecordRecoveryEvent(RECOVERY_FAILED_META_NO_VERSION); + } return false; + } + RecordRecoveryEvent(RECOVERY_SUCCESS_META_VERSION); *version = recovery_version.ColumnInt(0); return true; } |