зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1463938 - Recalculate frecency in chunks on idle. r=mak
MozReview-Commit-ID: 7Mp0hT3Ziw5 --HG-- extra : rebase_source : 76db4d423004b95a0f75b582725053912dacfe2e
This commit is contained in:
Родитель
839dd30680
Коммит
5daa4014be
|
@ -238,6 +238,140 @@ protected:
|
|||
nsNavHistory& mNavHistory;
|
||||
};
|
||||
|
||||
/**
|
||||
* Recalculates invalid frecencies in chunks on the storage thread, optionally
|
||||
* decays frecencies, and notifies history observers on the main thread.
|
||||
*/
|
||||
class FixAndDecayFrecencyRunnable final : public Runnable
|
||||
{
|
||||
public:
|
||||
explicit FixAndDecayFrecencyRunnable(Database* aDB, float aDecayRate)
|
||||
: Runnable("places::FixAndDecayFrecencyRunnable")
|
||||
, mDB(aDB)
|
||||
, mDecayRate(aDecayRate)
|
||||
, mDecayReason(mozIStorageStatementCallback::REASON_FINISHED)
|
||||
{}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override {
|
||||
if (NS_IsMainThread()) {
|
||||
nsNavHistory *navHistory = nsNavHistory::GetHistoryService();
|
||||
NS_ENSURE_STATE(navHistory);
|
||||
|
||||
navHistory->DecayFrecencyCompleted(mDecayReason);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!NS_IsMainThread(),
|
||||
"Frecencies should be recalculated on async thread");
|
||||
|
||||
nsCOMPtr<mozIStorageStatement> updateStmt = mDB->GetStatement(
|
||||
"UPDATE moz_places "
|
||||
"SET frecency = CALCULATE_FRECENCY(id) "
|
||||
"WHERE id IN ("
|
||||
"SELECT id FROM moz_places "
|
||||
"WHERE frecency < 0 "
|
||||
"ORDER BY frecency ASC "
|
||||
"LIMIT 400"
|
||||
")"
|
||||
);
|
||||
NS_ENSURE_STATE(updateStmt);
|
||||
nsresult rv = updateStmt->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<mozIStorageStatement> selectStmt = mDB->GetStatement(
|
||||
"SELECT id FROM moz_places WHERE frecency < 0 "
|
||||
"LIMIT 1"
|
||||
);
|
||||
NS_ENSURE_STATE(selectStmt);
|
||||
bool hasResult = false;
|
||||
rv = selectStmt->ExecuteStep(&hasResult);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (hasResult) {
|
||||
// There are more invalid frecencies to fix. Re-dispatch to the async
|
||||
// storage thread for the next chunk.
|
||||
return NS_DispatchToCurrentThread(this);
|
||||
}
|
||||
|
||||
mozStorageTransaction transaction(mDB->MainConn(), false,
|
||||
mozIStorageConnection::TRANSACTION_IMMEDIATE);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(DecayFrecencies()))) {
|
||||
mDecayReason = mozIStorageStatementCallback::REASON_ERROR;
|
||||
}
|
||||
|
||||
// We've finished fixing and decaying frecencies. Trigger frecency updates
|
||||
// for all affected origins.
|
||||
nsCOMPtr<mozIStorageStatement> updateOriginFrecenciesStmt =
|
||||
mDB->GetStatement("DELETE FROM moz_updateoriginsupdate_temp");
|
||||
NS_ENSURE_STATE(updateOriginFrecenciesStmt);
|
||||
rv = updateOriginFrecenciesStmt->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = transaction.Commit();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Re-dispatch to the main thread to notify observers.
|
||||
return NS_DispatchToMainThread(this);
|
||||
}
|
||||
|
||||
private:
|
||||
nsresult
|
||||
DecayFrecencies()
|
||||
{
|
||||
TimeStamp start = TimeStamp::Now();
|
||||
|
||||
// Globally decay places frecency rankings to estimate reduced frecency
|
||||
// values of pages that haven't been visited for a while, i.e., they do
|
||||
// not get an updated frecency. A scaling factor of .975 results in .5 the
|
||||
// original value after 28 days.
|
||||
// When changing the scaling factor, ensure that the barrier in
|
||||
// moz_places_afterupdate_frecency_trigger still ignores these changes.
|
||||
nsCOMPtr<mozIStorageStatement> decayFrecency = mDB->GetStatement(
|
||||
"UPDATE moz_places SET frecency = ROUND(frecency * :decay_rate) "
|
||||
"WHERE frecency > 0"
|
||||
);
|
||||
NS_ENSURE_STATE(decayFrecency);
|
||||
nsresult rv = decayFrecency->BindDoubleByName(NS_LITERAL_CSTRING("decay_rate"),
|
||||
static_cast<double>(mDecayRate));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = decayFrecency->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Decay potentially unused adaptive entries (e.g. those that are at 1)
|
||||
// to allow better chances for new entries that will start at 1.
|
||||
nsCOMPtr<mozIStorageStatement> decayAdaptive = mDB->GetStatement(
|
||||
"UPDATE moz_inputhistory SET use_count = use_count * :decay_rate"
|
||||
);
|
||||
NS_ENSURE_STATE(decayAdaptive);
|
||||
rv = decayAdaptive->BindDoubleByName(NS_LITERAL_CSTRING("decay_rate"),
|
||||
static_cast<double>(mDecayRate));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = decayAdaptive->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Delete any adaptive entries that won't help in ordering anymore.
|
||||
nsCOMPtr<mozIStorageStatement> deleteAdaptive = mDB->GetStatement(
|
||||
"DELETE FROM moz_inputhistory WHERE use_count < :use_count"
|
||||
);
|
||||
NS_ENSURE_STATE(deleteAdaptive);
|
||||
rv = deleteAdaptive->BindDoubleByName(NS_LITERAL_CSTRING("use_count"),
|
||||
std::pow(static_cast<double>(mDecayRate),
|
||||
ADAPTIVE_HISTORY_EXPIRE_DAYS));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = deleteAdaptive->Execute();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
Telemetry::AccumulateTimeDelta(Telemetry::PLACES_IDLE_FRECENCY_DECAY_TIME_MS, start);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
RefPtr<Database> mDB;
|
||||
float mDecayRate;
|
||||
uint16_t mDecayReason;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
|
@ -2485,37 +2619,15 @@ nsNavHistory::Observe(nsISupports *aSubject, const char *aTopic,
|
|||
}
|
||||
|
||||
else if (strcmp(aTopic, TOPIC_IDLE_DAILY) == 0) {
|
||||
(void)DecayFrecency();
|
||||
(void)FixAndDecayFrecency();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
class PlacesDecayFrecencyCallback : public AsyncStatementTelemetryTimer
|
||||
{
|
||||
public:
|
||||
PlacesDecayFrecencyCallback()
|
||||
: AsyncStatementTelemetryTimer(Telemetry::PLACES_IDLE_FRECENCY_DECAY_TIME_MS)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD HandleCompletion(uint16_t aReason) override
|
||||
{
|
||||
(void)AsyncStatementTelemetryTimer::HandleCompletion(aReason);
|
||||
nsNavHistory *navHistory = nsNavHistory::GetHistoryService();
|
||||
NS_ENSURE_STATE(navHistory);
|
||||
navHistory->DecayFrecencyCompleted(aReason);
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
nsresult
|
||||
nsNavHistory::DecayFrecency()
|
||||
nsNavHistory::FixAndDecayFrecency()
|
||||
{
|
||||
nsresult rv = FixInvalidFrecencies();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
float decayRate = Preferences::GetFloat(PREF_FREC_DECAY_RATE,
|
||||
PREF_FREC_DECAY_RATE_DEF);
|
||||
if (decayRate > 1.0f) {
|
||||
|
@ -2523,58 +2635,13 @@ nsNavHistory::DecayFrecency()
|
|||
decayRate = PREF_FREC_DECAY_RATE_DEF;
|
||||
}
|
||||
|
||||
// Globally decay places frecency rankings to estimate reduced frecency
|
||||
// values of pages that haven't been visited for a while, i.e., they do
|
||||
// not get an updated frecency. A scaling factor of .975 results in .5 the
|
||||
// original value after 28 days.
|
||||
// When changing the scaling factor, ensure that the barrier in
|
||||
// moz_places_afterupdate_frecency_trigger still ignores these changes.
|
||||
nsCOMPtr<mozIStorageAsyncStatement> decayFrecency = mDB->GetAsyncStatement(
|
||||
"UPDATE moz_places SET frecency = ROUND(frecency * :decay_rate) "
|
||||
"WHERE frecency > 0"
|
||||
);
|
||||
NS_ENSURE_STATE(decayFrecency);
|
||||
rv = decayFrecency->BindDoubleByName(NS_LITERAL_CSTRING("decay_rate"),
|
||||
static_cast<double>(decayRate));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Decay potentially unused adaptive entries (e.g. those that are at 1)
|
||||
// to allow better chances for new entries that will start at 1.
|
||||
nsCOMPtr<mozIStorageAsyncStatement> decayAdaptive = mDB->GetAsyncStatement(
|
||||
"UPDATE moz_inputhistory SET use_count = use_count * :decay_rate"
|
||||
);
|
||||
NS_ENSURE_STATE(decayAdaptive);
|
||||
rv = decayAdaptive->BindDoubleByName(NS_LITERAL_CSTRING("decay_rate"),
|
||||
static_cast<double>(decayRate));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Delete any adaptive entries that won't help in ordering anymore.
|
||||
nsCOMPtr<mozIStorageAsyncStatement> deleteAdaptive = mDB->GetAsyncStatement(
|
||||
"DELETE FROM moz_inputhistory WHERE use_count < :use_count"
|
||||
);
|
||||
NS_ENSURE_STATE(deleteAdaptive);
|
||||
rv = deleteAdaptive->BindDoubleByName(NS_LITERAL_CSTRING("use_count"),
|
||||
std::pow(static_cast<double>(decayRate),
|
||||
ADAPTIVE_HISTORY_EXPIRE_DAYS));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<mozIStorageConnection> conn = mDB->MainConn();
|
||||
if (!conn) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
mozIStorageBaseStatement *stmts[] = {
|
||||
decayFrecency.get(),
|
||||
decayAdaptive.get(),
|
||||
deleteAdaptive.get()
|
||||
};
|
||||
nsCOMPtr<mozIStoragePendingStatement> ps;
|
||||
RefPtr<PlacesDecayFrecencyCallback> cb = new PlacesDecayFrecencyCallback();
|
||||
rv = conn->ExecuteAsync(stmts, ArrayLength(stmts), cb, getter_AddRefs(ps));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
RefPtr<FixAndDecayFrecencyRunnable> runnable =
|
||||
new FixAndDecayFrecencyRunnable(mDB, decayRate);
|
||||
nsCOMPtr<nsIEventTarget> target = do_GetInterface(mDB->MainConn());
|
||||
NS_ENSURE_STATE(target);
|
||||
|
||||
mDecayFrecencyPendingCount++;
|
||||
|
||||
return NS_OK;
|
||||
return target->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -3753,11 +3820,9 @@ nsNavHistory::UpdateFrecency(int64_t aPlaceId)
|
|||
updateFrecencyStmt.get()
|
||||
, updateHiddenStmt.get()
|
||||
};
|
||||
RefPtr<AsyncStatementCallbackNotifier> cb =
|
||||
new AsyncStatementCallbackNotifier(TOPIC_FRECENCY_UPDATED);
|
||||
nsCOMPtr<mozIStoragePendingStatement> ps;
|
||||
rv = conn->ExecuteAsync(stmts, ArrayLength(stmts), cb,
|
||||
getter_AddRefs(ps));
|
||||
rv = conn->ExecuteAsync(stmts, ArrayLength(stmts), nullptr,
|
||||
getter_AddRefs(ps));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Trigger frecency updates for all affected origins.
|
||||
|
@ -3771,58 +3836,6 @@ nsNavHistory::UpdateFrecency(int64_t aPlaceId)
|
|||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
class FixInvalidFrecenciesCallback : public AsyncStatementCallbackNotifier
|
||||
{
|
||||
public:
|
||||
FixInvalidFrecenciesCallback()
|
||||
: AsyncStatementCallbackNotifier(TOPIC_FRECENCY_UPDATED)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD HandleCompletion(uint16_t aReason) override
|
||||
{
|
||||
nsresult rv = AsyncStatementCallbackNotifier::HandleCompletion(aReason);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (aReason == REASON_FINISHED) {
|
||||
nsNavHistory *navHistory = nsNavHistory::GetHistoryService();
|
||||
NS_ENSURE_STATE(navHistory);
|
||||
navHistory->NotifyManyFrecenciesChanged();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
nsresult
|
||||
nsNavHistory::FixInvalidFrecencies()
|
||||
{
|
||||
nsCOMPtr<mozIStorageAsyncStatement> stmt = mDB->GetAsyncStatement(
|
||||
"UPDATE moz_places "
|
||||
"SET frecency = CALCULATE_FRECENCY(id) "
|
||||
"WHERE frecency < 0"
|
||||
);
|
||||
NS_ENSURE_STATE(stmt);
|
||||
|
||||
RefPtr<FixInvalidFrecenciesCallback> callback =
|
||||
new FixInvalidFrecenciesCallback();
|
||||
nsCOMPtr<mozIStoragePendingStatement> ps;
|
||||
(void)stmt->ExecuteAsync(callback, getter_AddRefs(ps));
|
||||
|
||||
// Trigger frecency updates for affected origins.
|
||||
nsCOMPtr<mozIStorageAsyncStatement> updateOriginFrecenciesStmt =
|
||||
mDB->GetAsyncStatement("DELETE FROM moz_updateoriginsupdate_temp");
|
||||
NS_ENSURE_STATE(updateOriginFrecenciesStmt);
|
||||
nsresult rv =
|
||||
updateOriginFrecenciesStmt->ExecuteAsync(nullptr, getter_AddRefs(ps));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
|
||||
nsresult
|
||||
|
|
|
@ -54,9 +54,6 @@
|
|||
#define TOPIC_AUTOCOMPLETE_FEEDBACK_UPDATED "places-autocomplete-feedback-updated"
|
||||
#endif
|
||||
|
||||
// Fired after frecency has been updated.
|
||||
#define TOPIC_FRECENCY_UPDATED "places-frecency-updated"
|
||||
|
||||
// The preference we watch to know when the mobile bookmarks folder is filled by
|
||||
// sync.
|
||||
#define MOBILE_BOOKMARKS_PREF "browser.bookmarks.showMobileBookmarks"
|
||||
|
@ -75,7 +72,6 @@ class nsIAutoCompleteController;
|
|||
class nsIEffectiveTLDService;
|
||||
class nsIIDNService;
|
||||
class nsNavHistory;
|
||||
class PlacesDecayFrecencyCallback;
|
||||
class PlacesSQLQueryBuilder;
|
||||
|
||||
// nsNavHistory
|
||||
|
@ -85,7 +81,6 @@ class nsNavHistory final : public nsSupportsWeakReference
|
|||
, public nsIObserver
|
||||
, public mozIStorageVacuumParticipant
|
||||
{
|
||||
friend class PlacesDecayFrecencyCallback;
|
||||
friend class PlacesSQLQueryBuilder;
|
||||
|
||||
public:
|
||||
|
@ -177,15 +172,6 @@ public:
|
|||
*/
|
||||
nsresult UpdateFrecency(int64_t aPlaceId);
|
||||
|
||||
/**
|
||||
* Recalculates frecency for all pages requesting that (frecency < 0). Those
|
||||
* may be generated:
|
||||
* * After a "clear private data"
|
||||
* * After removing visits
|
||||
* * After migrating from older versions
|
||||
*/
|
||||
nsresult FixInvalidFrecencies();
|
||||
|
||||
/**
|
||||
* Invalidate the frecencies of a list of places, so they will be recalculated
|
||||
* at the first idle-daily notification.
|
||||
|
@ -507,6 +493,8 @@ public:
|
|||
const RefPtr<nsNavHistoryQuery>& aQuery,
|
||||
nsNavHistoryQueryOptions* aOptions);
|
||||
|
||||
void DecayFrecencyCompleted(uint16_t reason);
|
||||
|
||||
private:
|
||||
~nsNavHistory();
|
||||
|
||||
|
@ -519,9 +507,14 @@ protected:
|
|||
RefPtr<mozilla::places::Database> mDB;
|
||||
|
||||
/**
|
||||
* Decays frecency and inputhistory values. Runs on idle-daily.
|
||||
* Recalculates frecency for all pages where frecency < 0, then decays
|
||||
* frecency and inputhistory values. Pages can invalidate frecencies:
|
||||
* * After a "clear private data"
|
||||
* * After removing visits
|
||||
* * After migrating from older versions
|
||||
* This method runs on idle-daily.
|
||||
*/
|
||||
nsresult DecayFrecency();
|
||||
nsresult FixAndDecayFrecency();
|
||||
|
||||
/**
|
||||
* Loads all of the preferences that we use into member variables.
|
||||
|
@ -634,7 +627,6 @@ protected:
|
|||
int32_t mUnvisitedTypedBonus;
|
||||
int32_t mReloadVisitBonus;
|
||||
|
||||
void DecayFrecencyCompleted(uint16_t reason);
|
||||
uint32_t mDecayFrecencyPendingCount;
|
||||
|
||||
nsresult RecalculateOriginFrecencyStatsInternal();
|
||||
|
|
|
@ -49,14 +49,13 @@ add_task(async function test_nsNavHistory_invalidateFrecencies_allPages() {
|
|||
await Promise.all([onManyFrecenciesChanged(), PlacesUtils.history.clear()]);
|
||||
});
|
||||
|
||||
// nsNavHistory::DecayFrecency and nsNavHistory::FixInvalidFrecencies
|
||||
add_task(async function test_nsNavHistory_DecayFrecency_and_nsNavHistory_FixInvalidFrecencies() {
|
||||
// FixInvalidFrecencies is at the end of a path that DecayFrecency is also on,
|
||||
// so expect two notifications. Trigger the path by making nsNavHistory
|
||||
// observe the idle-daily notification.
|
||||
// nsNavHistory::FixAndDecayFrecency
|
||||
add_task(async function test_nsNavHistory_FixAndDecayFrecency() {
|
||||
// Fix and decay frecencies by making nsNavHistory observe the idle-daily
|
||||
// notification.
|
||||
PlacesUtils.history.QueryInterface(Ci.nsIObserver).
|
||||
observe(null, "idle-daily", "");
|
||||
await Promise.all([onManyFrecenciesChanged(), onManyFrecenciesChanged()]);
|
||||
await Promise.all([onManyFrecenciesChanged()]);
|
||||
});
|
||||
|
||||
function onFrecencyChanged(expectedURI) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче