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

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: Update comments 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
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 // Adding an Action to the database is a two step process that depends on
227 // whether the count on an existing row can be incremented or a new row needs
228 // to be inserted.
229 // 1. Run the query in locate_str to search for a row which matches and can
230 // have the count incremented.
231 // 2a. If found, increment the count using update_str and the rowid found in
232 // step 1, or
233 // 2b. If not found, insert a new row using insert_str.
234 std::string locate_str =
235 "SELECT rowid FROM " + std::string(kTableName) +
236 " WHERE time >= ? AND time < ?";
226 std::string insert_str = 237 std::string insert_str =
227 "INSERT INTO " + std::string(kTableName) + "(count, time"; 238 "INSERT INTO " + std::string(kTableName) + "(count, time";
228 std::string update_str = 239 std::string update_str =
229 "UPDATE " + std::string(kTableName) + 240 "UPDATE " + std::string(kTableName) +
230 " SET count = count + ?, time = max(?, time)" 241 " SET count = count + ?, time = max(?, time)"
231 " WHERE time >= ? AND time < ?"; 242 " WHERE rowid = ?";
232 243
233 for (size_t i = 0; i < arraysize(matched_columns); i++) { 244 for (size_t i = 0; i < arraysize(matched_columns); i++) {
245 locate_str = base::StringPrintf(
246 "%s AND %s IS ?", locate_str.c_str(), matched_columns[i]);
234 insert_str = 247 insert_str =
235 base::StringPrintf("%s, %s", insert_str.c_str(), matched_columns[i]); 248 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 } 249 }
239 insert_str += ") VALUES (?, ?"; 250 insert_str += ") VALUES (?, ?";
240 for (size_t i = 0; i < arraysize(matched_columns); i++) { 251 for (size_t i = 0; i < arraysize(matched_columns); i++) {
241 insert_str += ", ?"; 252 insert_str += ", ?";
242 } 253 }
254 locate_str += " ORDER BY time DESC LIMIT 1";
243 insert_str += ")"; 255 insert_str += ")";
244 256
245 for (ActionQueue::iterator i = queue.begin(); i != queue.end(); ++i) { 257 for (ActionQueue::iterator i = queue.begin(); i != queue.end(); ++i) {
246 const Action& action = *i->first; 258 const Action& action = *i->first;
247 int count = i->second; 259 int count = i->second;
248 260
249 base::Time day_start = action.time().LocalMidnight(); 261 base::Time day_start = action.time().LocalMidnight();
250 base::Time next_day = Util::AddDays(day_start, 1); 262 base::Time next_day = Util::AddDays(day_start, 1);
251 263
252 // The contents in values must match up with fields in matched_columns. A 264 // 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 } 320 }
309 321
310 if (action.other()) { 322 if (action.other()) {
311 if (!string_table_.StringToInt(db, Util::Serialize(action.other()), &id)) 323 if (!string_table_.StringToInt(db, Util::Serialize(action.other()), &id))
312 return false; 324 return false;
313 matched_values.push_back(id); 325 matched_values.push_back(id);
314 } else { 326 } else {
315 matched_values.push_back(-1); 327 matched_values.push_back(-1);
316 } 328 }
317 329
318 // Assume there is an existing row for this action, and try to update the 330 // Search for a matching row for this action whose count can be
319 // count. 331 // incremented.
320 sql::Statement update_statement(db->GetCachedStatement( 332 sql::Statement locate_statement(db->GetCachedStatement(
321 sql::StatementID(SQL_FROM_HERE), update_str.c_str())); 333 sql::StatementID(SQL_FROM_HERE), locate_str.c_str()));
322 update_statement.BindInt(0, count); 334 locate_statement.BindInt64(0, day_start.ToInternalValue());
323 update_statement.BindInt64(1, action.time().ToInternalValue()); 335 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++) { 336 for (size_t j = 0; j < matched_values.size(); j++) {
327 // A call to BindNull when matched_values contains -1 is likely not 337 // A call to BindNull when matched_values contains -1 is likely not
328 // necessary as parameters default to null before they are explicitly 338 // necessary as parameters default to null before they are explicitly
329 // bound. But to be completely clear, and in case a cached statement 339 // bound. But to be completely clear, and in case a cached statement
330 // ever comes with some values already bound, we bind all parameters 340 // ever comes with some values already bound, we bind all parameters
331 // (even null ones) explicitly. 341 // (even null ones) explicitly.
332 if (matched_values[j] == -1) 342 if (matched_values[j] == -1)
333 update_statement.BindNull(j + 4); 343 locate_statement.BindNull(j + 2);
334 else 344 else
335 update_statement.BindInt64(j + 4, matched_values[j]); 345 locate_statement.BindInt64(j + 2, matched_values[j]);
336 } 346 }
337 if (!update_statement.Run()) 347
348 if (locate_statement.Step()) {
349 // A matching row was found. Update the count and time.
350 int64 rowid = locate_statement.ColumnInt64(0);
351 sql::Statement update_statement(db->GetCachedStatement(
352 sql::StatementID(SQL_FROM_HERE), update_str.c_str()));
353 update_statement.BindInt(0, count);
354 update_statement.BindInt64(1, action.time().ToInternalValue());
355 update_statement.BindInt64(2, rowid);
356 if (!update_statement.Run())
357 return false;
358 } else if (locate_statement.Succeeded()) {
359 // No matching row was found, so we need to insert one.
360 sql::Statement insert_statement(db->GetCachedStatement(
361 sql::StatementID(SQL_FROM_HERE), insert_str.c_str()));
362 insert_statement.BindInt(0, count);
363 insert_statement.BindInt64(1, action.time().ToInternalValue());
364 for (size_t j = 0; j < matched_values.size(); j++) {
365 if (matched_values[j] == -1)
366 insert_statement.BindNull(j + 2);
367 else
368 insert_statement.BindInt64(j + 2, matched_values[j]);
369 }
370 if (!insert_statement.Run())
371 return false;
372 } else {
373 // Database error.
338 return false; 374 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 } 375 }
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 } 376 }
363 377
364 if (clean_database) { 378 if (clean_database) {
365 base::Time cutoff = (Now() - retention_time()).LocalMidnight(); 379 base::Time cutoff = (Now() - retention_time()).LocalMidnight();
366 if (!CleanOlderThan(db, cutoff)) 380 if (!CleanOlderThan(db, cutoff))
367 return false; 381 return false;
368 last_database_cleaning_time_ = Now(); 382 last_database_cleaning_time_ = Now();
369 } 383 }
370 384
371 if (!transaction.Commit()) 385 if (!transaction.Commit())
(...skipping 368 matching lines...) Expand 10 before | Expand all | Expand 10 after
740 return true; 754 return true;
741 } 755 }
742 756
743 void CountingPolicy::Close() { 757 void CountingPolicy::Close() {
744 // The policy object should have never been created if there's no DB thread. 758 // The policy object should have never been created if there's no DB thread.
745 DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::DB)); 759 DCHECK(BrowserThread::IsMessageLoopValid(BrowserThread::DB));
746 ScheduleAndForget(activity_database(), &ActivityDatabase::Close); 760 ScheduleAndForget(activity_database(), &ActivityDatabase::Close);
747 } 761 }
748 762
749 } // namespace extensions 763 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698