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

Side by Side Diff: chrome/browser/extensions/activity_log/activity_database.cc

Issue 16510002: Better ActivityLog error handling (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Cleanup before review Created 7 years, 6 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 | Annotate | Revision Log
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 <string> 5 #include <string>
6 #include "base/command_line.h" 6 #include "base/command_line.h"
7 #include "base/logging.h" 7 #include "base/logging.h"
8 #include "base/string_util.h" 8 #include "base/string_util.h"
9 #include "base/stringprintf.h" 9 #include "base/stringprintf.h"
10 #include "base/threading/thread.h" 10 #include "base/threading/thread.h"
11 #include "base/threading/thread_checker.h" 11 #include "base/threading/thread_checker.h"
12 #include "base/time.h" 12 #include "base/time.h"
13 #include "base/time/clock.h" 13 #include "base/time/clock.h"
14 #include "chrome/browser/extensions/activity_log/activity_database.h" 14 #include "chrome/browser/extensions/activity_log/activity_database.h"
15 #include "chrome/common/chrome_switches.h" 15 #include "chrome/common/chrome_switches.h"
16 #include "sql/error_delegate_util.h"
16 #include "sql/transaction.h" 17 #include "sql/transaction.h"
18 #include "third_party/sqlite/sqlite3.h"
17 19
18 #if defined(OS_MACOSX) 20 #if defined(OS_MACOSX)
19 #include "base/mac/mac_util.h" 21 #include "base/mac/mac_util.h"
20 #endif 22 #endif
21 23
22 using content::BrowserThread; 24 using content::BrowserThread;
23 25
24 namespace { 26 namespace {
25 27
26 bool SortActionsByTime(const scoped_refptr<extensions::Action> a, 28 bool SortActionsByTime(const scoped_refptr<extensions::Action> a,
27 const scoped_refptr<extensions::Action> b) { 29 const scoped_refptr<extensions::Action> b) {
28 return a->time() > b->time(); 30 return a->time() > b->time();
29 } 31 }
30 32
31 } // namespace 33 } // namespace
32 34
33 namespace extensions { 35 namespace extensions {
34 36
35 ActivityDatabase::ActivityDatabase() 37 ActivityDatabase::ActivityDatabase()
36 : testing_clock_(NULL), 38 : testing_clock_(NULL),
37 initialized_(false) { 39 valid_db_(false),
40 already_closed_(false),
41 already_ran_(false) {
38 // We don't batch commits when in testing mode. 42 // We don't batch commits when in testing mode.
39 batch_mode_ = !(CommandLine::ForCurrentProcess()-> 43 batch_mode_ = !(CommandLine::ForCurrentProcess()->
40 HasSwitch(switches::kEnableExtensionActivityLogTesting)); 44 HasSwitch(switches::kEnableExtensionActivityLogTesting));
41 } 45 }
42 46
43 ActivityDatabase::~ActivityDatabase() {} 47 ActivityDatabase::~ActivityDatabase() {}
44 48
45 void ActivityDatabase::SetErrorCallback(
46 const sql::Connection::ErrorCallback& error_callback) {
47 db_.set_error_callback(error_callback);
48 }
49
50 void ActivityDatabase::Init(const base::FilePath& db_name) { 49 void ActivityDatabase::Init(const base::FilePath& db_name) {
50 if (already_ran_) return;
Matt Perry 2013/06/07 20:58:01 this is an ambiguous name. how about "initialized_
felt 2013/06/08 00:01:48 Done.
51 already_ran_ = true;
51 if (BrowserThread::IsMessageLoopValid(BrowserThread::DB)) 52 if (BrowserThread::IsMessageLoopValid(BrowserThread::DB))
52 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 53 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
54 db_.set_error_callback(
55 base::Bind(&ActivityDatabase::DatabaseErrorCallback,
56 base::Unretained(this)));
53 db_.set_page_size(4096); 57 db_.set_page_size(4096);
54 db_.set_cache_size(32); 58 db_.set_cache_size(32);
55 59
56 if (!db_.Open(db_name)) { 60 if (!db_.Open(db_name)) {
57 LOG(ERROR) << db_.GetErrorMessage(); 61 LOG(ERROR) << db_.GetErrorMessage();
58 return LogInitFailure(); 62 return LogInitFailure();
59 } 63 }
60 64
61 // Wrap the initialization in a transaction so that the db doesn't 65 // Wrap the initialization in a transaction so that the db doesn't
62 // get corrupted if init fails/crashes. 66 // get corrupted if init fails/crashes.
(...skipping 17 matching lines...) Expand all
80 return LogInitFailure(); 84 return LogInitFailure();
81 85
82 // Create the BlockedAction database. 86 // Create the BlockedAction database.
83 if (!BlockedAction::InitializeTable(&db_)) 87 if (!BlockedAction::InitializeTable(&db_))
84 return LogInitFailure(); 88 return LogInitFailure();
85 89
86 sql::InitStatus stat = committer.Commit() ? sql::INIT_OK : sql::INIT_FAILURE; 90 sql::InitStatus stat = committer.Commit() ? sql::INIT_OK : sql::INIT_FAILURE;
87 if (stat != sql::INIT_OK) 91 if (stat != sql::INIT_OK)
88 return LogInitFailure(); 92 return LogInitFailure();
89 93
90 initialized_ = true; 94 valid_db_ = true;
91 timer_.Start(FROM_HERE, 95 timer_.Start(FROM_HERE,
92 base::TimeDelta::FromMinutes(2), 96 base::TimeDelta::FromMinutes(2),
93 this, 97 this,
94 &ActivityDatabase::RecordBatchedActions); 98 &ActivityDatabase::RecordBatchedActions);
95 } 99 }
96 100
97 void ActivityDatabase::LogInitFailure() { 101 void ActivityDatabase::LogInitFailure() {
98 LOG(ERROR) << "Couldn't initialize the activity log database."; 102 LOG(ERROR) << "Couldn't initialize the activity log database.";
103 SoftFailureClose();
99 } 104 }
100 105
101 void ActivityDatabase::RecordAction(scoped_refptr<Action> action) { 106 void ActivityDatabase::RecordAction(scoped_refptr<Action> action) {
102 if (initialized_) { 107 if (!valid_db_) return;
103 if (batch_mode_) 108 if (batch_mode_) {
104 batched_actions_.push_back(action); 109 batched_actions_.push_back(action);
105 else 110 } else {
106 action->Record(&db_); 111 if (!action->Record(&db_)) SoftFailureClose();
107 } 112 }
108 } 113 }
109 114
110 void ActivityDatabase::RecordBatchedActions() { 115 void ActivityDatabase::RecordBatchedActions() {
111 std::vector<scoped_refptr<Action> >::size_type i; 116 std::vector<scoped_refptr<Action> >::size_type i;
112 for (i = 0; i != batched_actions_.size(); ++i) { 117 for (i = 0; i != batched_actions_.size(); ++i) {
113 batched_actions_.at(i)->Record(&db_); 118 if (!valid_db_) return;
Matt Perry 2013/06/07 20:58:01 move this out of the for loop.
felt 2013/06/08 00:01:48 Done.
119 if (!batched_actions_.at(i)->Record(&db_)) return SoftFailureClose();
Matt Perry 2013/06/07 20:58:01 should probably break; here instead, and clear out
felt 2013/06/08 00:01:48 We should clear out the batched actions, you're ri
114 } 120 }
115 batched_actions_.clear(); 121 batched_actions_.clear();
116 } 122 }
117 123
118 void ActivityDatabase::SetBatchModeForTesting(bool batch_mode) { 124 void ActivityDatabase::SetBatchModeForTesting(bool batch_mode) {
119 if (batch_mode && !batch_mode_) { 125 if (batch_mode && !batch_mode_) {
120 timer_.Start(FROM_HERE, 126 timer_.Start(FROM_HERE,
121 base::TimeDelta::FromMinutes(2), 127 base::TimeDelta::FromMinutes(2),
122 this, 128 this,
123 &ActivityDatabase::RecordBatchedActions); 129 &ActivityDatabase::RecordBatchedActions);
124 } else if (!batch_mode && batch_mode_) { 130 } else if (!batch_mode && batch_mode_) {
125 timer_.Stop(); 131 timer_.Stop();
126 RecordBatchedActions(); 132 RecordBatchedActions();
127 } 133 }
128 batch_mode_ = batch_mode; 134 batch_mode_ = batch_mode;
129 } 135 }
130 136
131 scoped_ptr<std::vector<scoped_refptr<Action> > > ActivityDatabase::GetActions( 137 scoped_ptr<std::vector<scoped_refptr<Action> > > ActivityDatabase::GetActions(
132 const std::string& extension_id, const int days_ago) { 138 const std::string& extension_id, const int days_ago) {
133 if (BrowserThread::IsMessageLoopValid(BrowserThread::DB)) 139 if (BrowserThread::IsMessageLoopValid(BrowserThread::DB))
134 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 140 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
135 DCHECK_GE(days_ago, 0); 141 DCHECK_GE(days_ago, 0);
136 scoped_ptr<std::vector<scoped_refptr<Action> > > 142 scoped_ptr<std::vector<scoped_refptr<Action> > >
137 actions(new std::vector<scoped_refptr<Action> >()); 143 actions(new std::vector<scoped_refptr<Action> >());
138 if (!initialized_) 144 if (!valid_db_)
139 return actions.Pass(); 145 return actions.Pass();
140 // Compute the time bounds for that day. 146 // Compute the time bounds for that day.
141 base::Time morning_midnight = testing_clock_ ? 147 base::Time morning_midnight = testing_clock_ ?
142 testing_clock_->Now().LocalMidnight() : 148 testing_clock_->Now().LocalMidnight() :
143 base::Time::Now().LocalMidnight(); 149 base::Time::Now().LocalMidnight();
144 int64 early_bound = 0; 150 int64 early_bound = 0;
145 int64 late_bound = 0; 151 int64 late_bound = 0;
146 if (days_ago == 0) { 152 if (days_ago == 0) {
147 early_bound = morning_midnight.ToInternalValue(); 153 early_bound = morning_midnight.ToInternalValue();
148 late_bound = base::Time::Max().ToInternalValue(); 154 late_bound = base::Time::Max().ToInternalValue();
149 } else { 155 } else {
150 base::Time early_time = morning_midnight - 156 base::Time early_time = morning_midnight -
151 base::TimeDelta::FromDays(days_ago); 157 base::TimeDelta::FromDays(days_ago);
152 base::Time late_time = morning_midnight - 158 base::Time late_time = morning_midnight -
153 base::TimeDelta::FromDays(days_ago-1); 159 base::TimeDelta::FromDays(days_ago-1);
154 early_bound = early_time.ToInternalValue(); 160 early_bound = early_time.ToInternalValue();
155 late_bound = late_time.ToInternalValue(); 161 late_bound = late_time.ToInternalValue();
156 } 162 }
157 // Get the DOMActions. 163 // Get the DOMActions.
158 std::string dom_str = base::StringPrintf("SELECT * FROM %s " 164 std::string dom_str = base::StringPrintf("SELECT * FROM %s "
159 "WHERE extension_id=? AND " 165 "WHERE extension_id=? AND "
160 "time>? AND time<=?", 166 "time>? AND time<=?",
161 DOMAction::kTableName); 167 DOMAction::kTableName);
162 sql::Statement dom_statement(db_.GetCachedStatement(SQL_FROM_HERE, 168 sql::Statement dom_statement(db_.GetCachedStatement(SQL_FROM_HERE,
163 dom_str.c_str())); 169 dom_str.c_str()));
164 dom_statement.BindString(0, extension_id); 170 dom_statement.BindString(0, extension_id);
165 dom_statement.BindInt64(1, early_bound); 171 dom_statement.BindInt64(1, early_bound);
166 dom_statement.BindInt64(2, late_bound); 172 dom_statement.BindInt64(2, late_bound);
167 while (dom_statement.Step()) { 173 while (dom_statement.is_valid() && dom_statement.Step()) {
168 scoped_refptr<DOMAction> action = new DOMAction(dom_statement); 174 scoped_refptr<DOMAction> action = new DOMAction(dom_statement);
169 actions->push_back(action); 175 actions->push_back(action);
170 } 176 }
171 // Get the APIActions. 177 // Get the APIActions.
172 std::string api_str = base::StringPrintf("SELECT * FROM %s " 178 std::string api_str = base::StringPrintf("SELECT * FROM %s "
173 "WHERE extension_id=? AND " 179 "WHERE extension_id=? AND "
174 "time>? AND time<=?", 180 "time>? AND time<=?",
175 APIAction::kTableName); 181 APIAction::kTableName);
176 sql::Statement api_statement(db_.GetCachedStatement(SQL_FROM_HERE, 182 sql::Statement api_statement(db_.GetCachedStatement(SQL_FROM_HERE,
177 api_str.c_str())); 183 api_str.c_str()));
178 api_statement.BindString(0, extension_id); 184 api_statement.BindString(0, extension_id);
179 api_statement.BindInt64(1, early_bound); 185 api_statement.BindInt64(1, early_bound);
180 api_statement.BindInt64(2, late_bound); 186 api_statement.BindInt64(2, late_bound);
181 while (api_statement.Step()) { 187 while (api_statement.is_valid() && api_statement.Step()) {
182 scoped_refptr<APIAction> action = new APIAction(api_statement); 188 scoped_refptr<APIAction> action = new APIAction(api_statement);
183 actions->push_back(action); 189 actions->push_back(action);
184 } 190 }
185 // Get the BlockedActions. 191 // Get the BlockedActions.
186 std::string blocked_str = base::StringPrintf("SELECT * FROM %s " 192 std::string blocked_str = base::StringPrintf("SELECT * FROM %s "
187 "WHERE extension_id=? AND " 193 "WHERE extension_id=? AND "
188 "time>? AND time<=?", 194 "time>? AND time<=?",
189 BlockedAction::kTableName); 195 BlockedAction::kTableName);
190 sql::Statement blocked_statement(db_.GetCachedStatement(SQL_FROM_HERE, 196 sql::Statement blocked_statement(db_.GetCachedStatement(SQL_FROM_HERE,
191 blocked_str.c_str())); 197 blocked_str.c_str()));
192 blocked_statement.BindString(0, extension_id); 198 blocked_statement.BindString(0, extension_id);
193 blocked_statement.BindInt64(1, early_bound); 199 blocked_statement.BindInt64(1, early_bound);
194 blocked_statement.BindInt64(2, late_bound); 200 blocked_statement.BindInt64(2, late_bound);
195 while (blocked_statement.Step()) { 201 while (blocked_statement.is_valid() && blocked_statement.Step()) {
196 scoped_refptr<BlockedAction> action = new BlockedAction(blocked_statement); 202 scoped_refptr<BlockedAction> action = new BlockedAction(blocked_statement);
197 actions->push_back(action); 203 actions->push_back(action);
198 } 204 }
199 // Sort by time (from newest to oldest). 205 // Sort by time (from newest to oldest).
200 std::sort(actions->begin(), actions->end(), SortActionsByTime); 206 std::sort(actions->begin(), actions->end(), SortActionsByTime);
201 return actions.Pass(); 207 return actions.Pass();
202 } 208 }
203 209
204 void ActivityDatabase::BeginTransaction() {
205 db_.BeginTransaction();
206 }
207
208 void ActivityDatabase::CommitTransaction() {
209 db_.CommitTransaction();
210 }
211
212 void ActivityDatabase::RollbackTransaction() {
213 db_.RollbackTransaction();
214 }
215
216 bool ActivityDatabase::Raze() {
217 return db_.Raze();
218 }
219
220 void ActivityDatabase::Close() { 210 void ActivityDatabase::Close() {
221 timer_.Stop(); 211 timer_.Stop();
222 RecordBatchedActions(); 212 if (!already_closed_) {
223 db_.Close(); 213 RecordBatchedActions();
214 db_.reset_error_callback();
215 }
216 valid_db_ = false;
217 already_closed_ = true;
224 delete this; 218 delete this;
225 } 219 }
226 220
227 void ActivityDatabase::KillDatabase() { 221 void ActivityDatabase::HardFailureClose() {
222 if (already_closed_) return;
223 valid_db_ = false;
228 timer_.Stop(); 224 timer_.Stop();
225 db_.reset_error_callback();
229 db_.RazeAndClose(); 226 db_.RazeAndClose();
227 already_closed_ = true;
228 }
229
230 void ActivityDatabase::SoftFailureClose() {
231 valid_db_ = false;
232 timer_.Stop();
233 }
234
235 void ActivityDatabase::DatabaseErrorCallback(int error, sql::Statement* stmt) {
236 if (sql::IsErrorCatastrophic(error)) {
237 LOG(ERROR) << "Killing the ActivityDatabase due to catastrophic error.";
238 HardFailureClose();
239 } else if (error != SQLITE_BUSY) {
240 // We ignore SQLITE_BUSY errors because they are presumably transient.
241 LOG(ERROR) << "Closing the ActivityDatabase due to error.";
242 SoftFailureClose();
243 }
230 } 244 }
231 245
232 void ActivityDatabase::SetClockForTesting(base::Clock* clock) { 246 void ActivityDatabase::SetClockForTesting(base::Clock* clock) {
233 testing_clock_ = clock; 247 testing_clock_ = clock;
234 } 248 }
235 249
236 void ActivityDatabase::RecordBatchedActionsWhileTesting() { 250 void ActivityDatabase::RecordBatchedActionsWhileTesting() {
237 RecordBatchedActions(); 251 RecordBatchedActions();
238 timer_.Stop(); 252 timer_.Stop();
239 } 253 }
240 254
241 void ActivityDatabase::SetTimerForTesting(int ms) { 255 void ActivityDatabase::SetTimerForTesting(int ms) {
242 timer_.Stop(); 256 timer_.Stop();
243 timer_.Start(FROM_HERE, 257 timer_.Start(FROM_HERE,
244 base::TimeDelta::FromMilliseconds(ms), 258 base::TimeDelta::FromMilliseconds(ms),
245 this, 259 this,
246 &ActivityDatabase::RecordBatchedActionsWhileTesting); 260 &ActivityDatabase::RecordBatchedActionsWhileTesting);
247 } 261 }
248 262
249 } // namespace extensions 263 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698