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

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

Issue 23907004: [Activity log] Make database writes in counting policy more robust (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Add unit test Created 7 years, 3 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 | « no previous file | chrome/browser/extensions/activity_log/counting_policy_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 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 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 // A policy for storing activity log data to a database that performs 5 // A policy for storing activity log data to a database that performs
6 // aggregation to reduce the size of the database. The database layout is 6 // aggregation to reduce the size of the database. The database layout is
7 // nearly the same as FullStreamUIPolicy, which stores a complete log, with a 7 // nearly the same as FullStreamUIPolicy, which stores a complete log, with a
8 // few changes: 8 // few changes:
9 // - a "count" column is added to track how many log records were merged 9 // - a "count" column is added to track how many log records were merged
10 // together into this row 10 // together into this row
(...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after
216 Now() - last_database_cleaning_time_ > 216 Now() - last_database_cleaning_time_ >
217 base::TimeDelta::FromHours(kCleaningDelayInHours)); 217 base::TimeDelta::FromHours(kCleaningDelayInHours));
218 218
219 if (queue.empty() && !clean_database) 219 if (queue.empty() && !clean_database)
220 return true; 220 return true;
221 221
222 sql::Transaction transaction(db); 222 sql::Transaction transaction(db);
223 if (!transaction.Begin()) 223 if (!transaction.Begin())
224 return false; 224 return false;
225 225
226 std::string locate_str =
227 "SELECT rowid FROM " + std::string(kTableName) +
228 " WHERE time >= ? AND time < ?";
felt 2013/09/06 20:53:57 by the way, I recently discovered the existence of
mvrable 2013/09/06 21:48:33 I hadn't thought of using BETWEEN (I usually don't
226 std::string insert_str = 229 std::string insert_str =
227 "INSERT INTO " + std::string(kTableName) + "(count, time"; 230 "INSERT INTO " + std::string(kTableName) + "(count, time";
228 std::string update_str = 231 std::string update_str =
229 "UPDATE " + std::string(kTableName) + 232 "UPDATE " + std::string(kTableName) +
230 " SET count = count + ?, time = max(?, time)" 233 " SET count = count + ?, time = max(?, time)"
231 " WHERE time >= ? AND time < ?"; 234 " WHERE rowid = ?";
232 235
233 for (size_t i = 0; i < arraysize(matched_columns); i++) { 236 for (size_t i = 0; i < arraysize(matched_columns); i++) {
237 locate_str = base::StringPrintf(
238 "%s AND %s IS ?", locate_str.c_str(), matched_columns[i]);
234 insert_str = 239 insert_str =
235 base::StringPrintf("%s, %s", insert_str.c_str(), matched_columns[i]); 240 base::StringPrintf("%s, %s", insert_str.c_str(), matched_columns[i]);
236 update_str = base::StringPrintf(
237 "%s AND %s IS ?", update_str.c_str(), matched_columns[i]);
238 } 241 }
239 insert_str += ") VALUES (?, ?"; 242 insert_str += ") VALUES (?, ?";
240 for (size_t i = 0; i < arraysize(matched_columns); i++) { 243 for (size_t i = 0; i < arraysize(matched_columns); i++) {
241 insert_str += ", ?"; 244 insert_str += ", ?";
242 } 245 }
246 locate_str += " ORDER BY time DESC LIMIT 1";
243 insert_str += ")"; 247 insert_str += ")";
244 248
245 for (ActionQueue::iterator i = queue.begin(); i != queue.end(); ++i) { 249 for (ActionQueue::iterator i = queue.begin(); i != queue.end(); ++i) {
246 const Action& action = *i->first; 250 const Action& action = *i->first;
247 int count = i->second; 251 int count = i->second;
248 252
249 base::Time day_start = action.time().LocalMidnight(); 253 base::Time day_start = action.time().LocalMidnight();
250 base::Time next_day = Util::AddDays(day_start, 1); 254 base::Time next_day = Util::AddDays(day_start, 1);
251 255
252 // The contents in values must match up with fields in matched_columns. A 256 // The contents in values must match up with fields in matched_columns. A
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
308 } 312 }
309 313
310 if (action.other()) { 314 if (action.other()) {
311 if (!string_table_.StringToInt(db, Util::Serialize(action.other()), &id)) 315 if (!string_table_.StringToInt(db, Util::Serialize(action.other()), &id))
312 return false; 316 return false;
313 matched_values.push_back(id); 317 matched_values.push_back(id);
314 } else { 318 } else {
315 matched_values.push_back(-1); 319 matched_values.push_back(-1);
316 } 320 }
317 321
318 // Assume there is an existing row for this action, and try to update the 322 // Search for a matching row for this action whose count can be
319 // count. 323 // incremented.
320 sql::Statement update_statement(db->GetCachedStatement( 324 sql::Statement locate_statement(db->GetCachedStatement(
321 sql::StatementID(SQL_FROM_HERE), update_str.c_str())); 325 sql::StatementID(SQL_FROM_HERE), locate_str.c_str()));
322 update_statement.BindInt(0, count); 326 locate_statement.BindInt64(0, day_start.ToInternalValue());
323 update_statement.BindInt64(1, action.time().ToInternalValue()); 327 locate_statement.BindInt64(1, next_day.ToInternalValue());
324 update_statement.BindInt64(2, day_start.ToInternalValue());
325 update_statement.BindInt64(3, next_day.ToInternalValue());
326 for (size_t j = 0; j < matched_values.size(); j++) { 328 for (size_t j = 0; j < matched_values.size(); j++) {
327 // A call to BindNull when matched_values contains -1 is likely not 329 // A call to BindNull when matched_values contains -1 is likely not
328 // necessary as parameters default to null before they are explicitly 330 // necessary as parameters default to null before they are explicitly
329 // bound. But to be completely clear, and in case a cached statement 331 // bound. But to be completely clear, and in case a cached statement
330 // ever comes with some values already bound, we bind all parameters 332 // ever comes with some values already bound, we bind all parameters
331 // (even null ones) explicitly. 333 // (even null ones) explicitly.
332 if (matched_values[j] == -1) 334 if (matched_values[j] == -1)
333 update_statement.BindNull(j + 4); 335 locate_statement.BindNull(j + 2);
334 else 336 else
335 update_statement.BindInt64(j + 4, matched_values[j]); 337 locate_statement.BindInt64(j + 2, matched_values[j]);
336 } 338 }
337 if (!update_statement.Run()) 339
340 if (locate_statement.Step()) {
341 // A matching row was found. Update the count and time.
342 int64 rowid = locate_statement.ColumnInt64(0);
343 sql::Statement update_statement(db->GetCachedStatement(
344 sql::StatementID(SQL_FROM_HERE), update_str.c_str()));
345 update_statement.BindInt(0, count);
346 update_statement.BindInt64(1, action.time().ToInternalValue());
347 update_statement.BindInt64(2, rowid);
348 if (!update_statement.Run())
349 return false;
350 } else if (locate_statement.Succeeded()) {
351 // No matching row was found, so we need to insert one.
352 sql::Statement insert_statement(db->GetCachedStatement(
353 sql::StatementID(SQL_FROM_HERE), insert_str.c_str()));
354 insert_statement.BindInt(0, count);
355 insert_statement.BindInt64(1, action.time().ToInternalValue());
356 for (size_t j = 0; j < matched_values.size(); j++) {
357 if (matched_values[j] == -1)
358 insert_statement.BindNull(j + 2);
359 else
360 insert_statement.BindInt64(j + 2, matched_values[j]);
361 }
362 if (!insert_statement.Run())
363 return false;
364 } else {
365 // Database error.
338 return false; 366 return false;
339
340 // Check if the update succeeded (was the count of updated rows non-zero)?
341 // If it failed because no matching row existed, fall back to inserting a
342 // new record.
343 if (db->GetLastChangeCount() > 0) {
344 if (db->GetLastChangeCount() > 1) {
345 LOG(WARNING) << "Found and updated multiple rows in the activity log "
346 << "database; counts may be off!";
347 }
348 continue;
349 } 367 }
350 sql::Statement insert_statement(db->GetCachedStatement(
351 sql::StatementID(SQL_FROM_HERE), insert_str.c_str()));
352 insert_statement.BindInt(0, count);
353 insert_statement.BindInt64(1, action.time().ToInternalValue());
354 for (size_t j = 0; j < matched_values.size(); j++) {
355 if (matched_values[j] == -1)
356 insert_statement.BindNull(j + 2);
357 else
358 insert_statement.BindInt64(j + 2, matched_values[j]);
359 }
360 if (!insert_statement.Run())
361 return false;
362 } 368 }
363 369
364 if (clean_database) { 370 if (clean_database) {
365 base::Time cutoff = (Now() - retention_time()).LocalMidnight(); 371 base::Time cutoff = (Now() - retention_time()).LocalMidnight();
366 if (!CleanOlderThan(db, cutoff)) 372 if (!CleanOlderThan(db, cutoff))
367 return false; 373 return false;
368 last_database_cleaning_time_ = Now(); 374 last_database_cleaning_time_ = Now();
369 } 375 }
370 376
371 if (!transaction.Commit()) 377 if (!transaction.Commit())
(...skipping 368 matching lines...) Expand 10 before | Expand all | Expand 10 after
740 return true; 746 return true;
741 } 747 }
742 748
743 void CountingPolicy::Close() { 749 void CountingPolicy::Close() {
744 // The policy object should have never been created if there's no DB thread. 750 // The policy object should have never been created if there's no DB thread.
745 DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::DB)); 751 DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::DB));
746 ScheduleAndForget(activity_database(), &ActivityDatabase::Close); 752 ScheduleAndForget(activity_database(), &ActivityDatabase::Close);
747 } 753 }
748 754
749 } // namespace extensions 755 } // namespace extensions
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/extensions/activity_log/counting_policy_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698