| Index: chrome/browser/history/expire_history_backend.cc
|
| ===================================================================
|
| --- chrome/browser/history/expire_history_backend.cc (revision 19822)
|
| +++ chrome/browser/history/expire_history_backend.cc (working copy)
|
| @@ -24,6 +24,62 @@
|
|
|
| namespace {
|
|
|
| +// The number of days by which the expiration threshold is advanced for items
|
| +// that we want to expire early, such as those of AUTO_SUBFRAME transition type.
|
| +const int kEarlyExpirationAdvanceDays = 30;
|
| +
|
| +// Reads all types of visits starting from beginning of time to the given end
|
| +// time. This is the most general reader.
|
| +class AllVisitsReader : public ExpiringVisitsReader {
|
| + public:
|
| + virtual bool Read(Time end_time, HistoryDatabase* db,
|
| + VisitVector* visits, int max_visits) const {
|
| + DCHECK(db) << "must have a database to operate upon";
|
| + DCHECK(visits) << "visit vector has to exist in order to populate it";
|
| +
|
| + db->GetAllVisitsInRange(Time(), end_time, max_visits, visits);
|
| + // When we got the maximum number of visits we asked for, we say there could
|
| + // be additional things to expire now.
|
| + return static_cast<int>(visits->size()) == max_visits;
|
| + }
|
| +};
|
| +
|
| +// Reads only AUTO_SUBFRAME visits, within a computed range. The range is
|
| +// computed as follows:
|
| +// * |begin_time| is read from the meta table. This value is updated whenever
|
| +// there are no more additional visits to expire by this reader.
|
| +// * |end_time| is advanced forward by a constant (kEarlyExpirationAdvanceDay),
|
| +// but not past the current time.
|
| +class AutoSubframeVisitsReader : public ExpiringVisitsReader {
|
| + public:
|
| + virtual bool Read(Time end_time, HistoryDatabase* db,
|
| + VisitVector* visits, int max_visits) const {
|
| + DCHECK(db) << "must have a database to operate upon";
|
| + DCHECK(visits) << "visit vector has to exist in order to populate it";
|
| +
|
| + Time begin_time = db->GetEarlyExpirationThreshold();
|
| + // Advance |end_time| to expire early.
|
| + Time early_end_time = end_time +
|
| + TimeDelta::FromDays(kEarlyExpirationAdvanceDays);
|
| +
|
| + // We don't want to set the early expiration threshold to a time in the
|
| + // future.
|
| + Time now = Time::Now();
|
| + if (early_end_time > now)
|
| + early_end_time = now;
|
| +
|
| + db->GetVisitsInRangeForTransition(begin_time, early_end_time,
|
| + max_visits,
|
| + PageTransition::AUTO_SUBFRAME,
|
| + visits);
|
| + bool more = static_cast<int>(visits->size()) == max_visits;
|
| + if (!more)
|
| + db->UpdateEarlyExpirationThreshold(early_end_time);
|
| +
|
| + return more;
|
| + }
|
| +};
|
| +
|
| // Returns true if this visit is worth archiving. Otherwise, this visit is not
|
| // worth saving (for example, subframe navigations and redirects) and we can
|
| // just delete it when it gets old.
|
| @@ -58,7 +114,7 @@
|
| // we think there might be more items to expire. This timeout is used when the
|
| // last expiration found at least kNumExpirePerIteration and we want to check
|
| // again "soon."
|
| -const int kExpirationDelaySec = 60;
|
| +const int kExpirationDelaySec = 30;
|
|
|
| // The number of minutes between checking, as with kExpirationDelaySec, but
|
| // when we didn't find enough things to expire last time. If there was no
|
| @@ -166,14 +222,47 @@
|
| return;
|
|
|
| // Archive as much history as possible before the given date.
|
| - ArchiveSomeOldHistory(end_time, std::numeric_limits<size_t>::max());
|
| + ArchiveSomeOldHistory(end_time, GetAllVisitsReader(),
|
| + std::numeric_limits<size_t>::max());
|
| ParanoidExpireHistory();
|
| }
|
|
|
| +void ExpireHistoryBackend::InitWorkQueue() {
|
| + DCHECK(work_queue_.empty()) << "queue has to be empty prior to init";
|
| +
|
| + for (size_t i = 0; i < readers_.size(); i++)
|
| + work_queue_.push(readers_[i]);
|
| +}
|
| +
|
| +const ExpiringVisitsReader* ExpireHistoryBackend::GetAllVisitsReader() {
|
| + if (!all_visits_reader_.get())
|
| + all_visits_reader_.reset(new AllVisitsReader());
|
| + return all_visits_reader_.get();
|
| +}
|
| +
|
| +const ExpiringVisitsReader*
|
| + ExpireHistoryBackend::GetAutoSubframeVisitsReader() {
|
| + if (!auto_subframe_visits_reader_.get())
|
| + auto_subframe_visits_reader_.reset(new AutoSubframeVisitsReader());
|
| + return auto_subframe_visits_reader_.get();
|
| +}
|
| +
|
| void ExpireHistoryBackend::StartArchivingOldStuff(
|
| TimeDelta expiration_threshold) {
|
| expiration_threshold_ = expiration_threshold;
|
| - ScheduleArchive(TimeDelta::FromSeconds(kExpirationDelaySec));
|
| +
|
| + // Remove all readers, just in case this was method was called before.
|
| + readers_.clear();
|
| + // For now, we explicitly add all known readers. If we come up with more
|
| + // reader types (in case we want to expire different types of visits in
|
| + // different ways), we can make it be populated by creator/owner of
|
| + // ExpireHistoryBackend.
|
| + readers_.push_back(GetAllVisitsReader());
|
| + readers_.push_back(GetAutoSubframeVisitsReader());
|
| +
|
| + // Initialize the queue with all tasks for the first set of iterations.
|
| + InitWorkQueue();
|
| + ScheduleArchive();
|
| }
|
|
|
| void ExpireHistoryBackend::DeleteFaviconsIfPossible(
|
| @@ -413,40 +502,53 @@
|
| }
|
| }
|
|
|
| -void ExpireHistoryBackend::ScheduleArchive(TimeDelta delay) {
|
| +void ExpireHistoryBackend::ScheduleArchive() {
|
| + TimeDelta delay;
|
| + if (work_queue_.empty()) {
|
| + // If work queue is empty, reset the work queue to contain all tasks and
|
| + // schedule next iteration after a longer delay.
|
| + InitWorkQueue();
|
| + delay = TimeDelta::FromMinutes(kExpirationEmptyDelayMin);
|
| + } else {
|
| + delay = TimeDelta::FromSeconds(kExpirationDelaySec);
|
| + }
|
| +
|
| factory_.RevokeAll();
|
| MessageLoop::current()->PostDelayedTask(FROM_HERE, factory_.NewRunnableMethod(
|
| &ExpireHistoryBackend::DoArchiveIteration), delay.InMilliseconds());
|
| }
|
|
|
| void ExpireHistoryBackend::DoArchiveIteration() {
|
| - DCHECK(expiration_threshold_ != TimeDelta()) << "threshold should be set";
|
| - Time threshold = Time::Now() - expiration_threshold_;
|
| + DCHECK(!work_queue_.empty()) << "queue has to be non-empty";
|
|
|
| - if (ArchiveSomeOldHistory(threshold, kNumExpirePerIteration)) {
|
| - // Possibly more items to delete now, schedule it sooner to happen again.
|
| - ScheduleArchive(TimeDelta::FromSeconds(kExpirationDelaySec));
|
| - } else {
|
| - // If we didn't find the maximum number of items to delete, wait longer
|
| - // before trying to delete more later.
|
| - ScheduleArchive(TimeDelta::FromMinutes(kExpirationEmptyDelayMin));
|
| - }
|
| + const ExpiringVisitsReader* reader = work_queue_.front();
|
| + bool more_to_expire = ArchiveSomeOldHistory(GetCurrentArchiveTime(), reader,
|
| + kNumExpirePerIteration);
|
| +
|
| + work_queue_.pop();
|
| + // If there are more items to expire, add the reader back to the queue, thus
|
| + // creating a new task for future iterations.
|
| + if (more_to_expire)
|
| + work_queue_.push(reader);
|
| +
|
| + ScheduleArchive();
|
| }
|
|
|
| -bool ExpireHistoryBackend::ArchiveSomeOldHistory(Time time_threshold,
|
| - int max_visits) {
|
| +bool ExpireHistoryBackend::ArchiveSomeOldHistory(
|
| + base::Time end_time,
|
| + const ExpiringVisitsReader* reader,
|
| + int max_visits) {
|
| if (!main_db_)
|
| return false;
|
|
|
| - // Get all visits up to and including the threshold. This is a little tricky
|
| - // because GetAllVisitsInRange's end value is non-inclusive, so we have to
|
| - // increment the time by one unit to get the input value to be inclusive.
|
| - DCHECK(!time_threshold.is_null());
|
| - Time effective_threshold =
|
| - Time::FromInternalValue(time_threshold.ToInternalValue() + 1);
|
| + // Add an extra time unit to given end time, because
|
| + // GetAllVisitsInRange, et al. queries' end value is non-inclusive.
|
| + Time effective_end_time =
|
| + Time::FromInternalValue(end_time.ToInternalValue() + 1);
|
| +
|
| VisitVector affected_visits;
|
| - main_db_->GetAllVisitsInRange(Time(), effective_threshold, max_visits,
|
| - &affected_visits);
|
| + bool more_to_expire = reader->Read(effective_end_time, main_db_,
|
| + &affected_visits, max_visits);
|
|
|
| // Some visits we'll delete while others we'll archive.
|
| VisitVector deleted_visits, archived_visits;
|
| @@ -489,9 +591,7 @@
|
| // to not do anything if nothing was deleted.
|
| BroadcastDeleteNotifications(&deleted_dependencies);
|
|
|
| - // When we got the maximum number of visits we asked for, we say there could
|
| - // be additional things to expire now.
|
| - return static_cast<int>(affected_visits.size()) == max_visits;
|
| + return more_to_expire;
|
| }
|
|
|
| void ExpireHistoryBackend::ParanoidExpireHistory() {
|
|
|