Bug 512854 - VACUUM places.sqlite database on daily idle once a month, r=sdwilsh

This commit is contained in:
Marco Bonardo 2009-09-14 13:06:45 +02:00
parent 565378112a
commit 0e915d6038
5 changed files with 626 additions and 52 deletions

View File

@ -144,8 +144,7 @@ using namespace mozilla::places;
#define PREF_FRECENCY_DEFAULT_VISIT_BONUS "places.frecency.defaultVisitBonus"
#define PREF_FRECENCY_UNVISITED_BOOKMARK_BONUS "places.frecency.unvisitedBookmarkBonus"
#define PREF_FRECENCY_UNVISITED_TYPED_BONUS "places.frecency.unvisitedTypedBonus"
#define PLACES_AUTOCOMPLETE_FEEDBACK_UPDATED_TOPIC "places-autocomplete-feedback-updated"
#define PREF_LAST_VACUUM "places.last_vacuum"
// Default (integer) value of PREF_DB_CACHE_PERCENTAGE from 0-100
// This is 6% of machine memory, giving 15MB for a user with 256MB of memory.
@ -212,6 +211,16 @@ using namespace mozilla::places;
#define DATE_CONT_NUM(_expireDays) \
(ADDITIONAL_DATE_CONT_NUM + PR_MIN(6, (_expireDays/30)))
// fraction of free pages in the database to force a vacuum between
// MAX_TIME_BEFORE_VACUUM and MIN_TIME_BEFORE_VACUUM.
#define VACUUM_FREEPAGES_THRESHOLD 0.1
// This is the maximum time (in microseconds) that can pass between 2 VACUUM
// operations.
#define MAX_TIME_BEFORE_VACUUM (PRInt64)60 * 24 * 60 * 60 * 1000 * 1000
// This is the minimum time (in microseconds) that should pass between 2 VACUUM
// operations.
#define MIN_TIME_BEFORE_VACUUM (PRInt64)30 * 24 * 60 * 60 * 1000 * 1000
NS_IMPL_THREADSAFE_ADDREF(nsNavHistory)
NS_IMPL_THREADSAFE_RELEASE(nsNavHistory)
@ -468,7 +477,7 @@ nsNavHistory::Init()
// Enqueue the notification, so if we init another service that requires
// nsNavHistoryService we don't recursive try to get it.
nsRefPtr<PlacesEvent> completeEvent =
new PlacesEvent(PLACES_INIT_COMPLETE_EVENT_TOPIC);
new PlacesEvent(PLACES_INIT_COMPLETE_TOPIC);
rv = NS_DispatchToMainThread(completeEvent);
NS_ENSURE_SUCCESS(rv, rv);
@ -528,7 +537,7 @@ nsNavHistory::Init()
// of the places infrastructure has been initialized.
if (mDatabaseStatus == DATABASE_STATUS_CREATE ||
mDatabaseStatus == DATABASE_STATUS_UPGRADED) {
(void)observerService->AddObserver(this, PLACES_INIT_COMPLETE_EVENT_TOPIC,
(void)observerService->AddObserver(this, PLACES_INIT_COMPLETE_TOPIC,
PR_FALSE);
}
@ -606,7 +615,7 @@ nsNavHistory::InitDBFile(PRBool aForceInit)
// We can't do much at this point, so fire a locked event so that user is
// notified that we can't ensure Places to work.
nsRefPtr<PlacesEvent> lockedEvent =
new PlacesEvent(PLACES_DB_LOCKED_EVENT_TOPIC);
new PlacesEvent(PLACES_DB_LOCKED_TOPIC);
(void)NS_DispatchToMainThread(lockedEvent);
}
NS_ENSURE_SUCCESS(rv, rv);
@ -656,7 +665,7 @@ nsNavHistory::InitDBFile(PRBool aForceInit)
// send out a notification and do not continue initialization.
// Note: We swallow errors here, since we want service init to fail anyway.
nsRefPtr<PlacesEvent> lockedEvent =
new PlacesEvent(PLACES_DB_LOCKED_EVENT_TOPIC);
new PlacesEvent(PLACES_DB_LOCKED_TOPIC);
(void)NS_DispatchToMainThread(lockedEvent);
}
NS_ENSURE_SUCCESS(rv, rv);
@ -5528,7 +5537,7 @@ nsNavHistory::CommitPendingChanges()
do_GetService("@mozilla.org/observer-service;1");
NS_ENSURE_TRUE(os, NS_ERROR_FAILURE);
nsCOMPtr<nsISimpleEnumerator> e;
nsresult rv = os->EnumerateObservers(PLACES_INIT_COMPLETE_EVENT_TOPIC,
nsresult rv = os->EnumerateObservers(PLACES_INIT_COMPLETE_TOPIC,
getter_AddRefs(e));
if (NS_SUCCEEDED(rv) && e) {
nsCOMPtr<nsIObserver> observer;
@ -5537,7 +5546,7 @@ nsNavHistory::CommitPendingChanges()
{
e->GetNext(getter_AddRefs(observer));
rv = observer->Observe(observer,
PLACES_INIT_COMPLETE_EVENT_TOPIC,
PLACES_INIT_COMPLETE_TOPIC,
nsnull);
}
}
@ -5642,58 +5651,22 @@ nsNavHistory::Observe(nsISupports *aSubject, const char *aTopic,
// to errors or during normal shutdown process.
NS_ENSURE_TRUE(mDBConn, NS_OK);
// Update frecency values
(void)FixInvalidFrecencies();
// 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. We directly modify moz_places to avoid
// bringing the whole database into places_temp through places_view. A
// scaling factor of .975 results in .5 the original value after 28 days.
nsCOMPtr<mozIStorageStatement> decayFrecency;
nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"UPDATE moz_places SET frecency = ROUND(frecency * .975) "
"WHERE frecency > 0"),
getter_AddRefs(decayFrecency));
NS_ENSURE_SUCCESS(rv, NS_OK);
// 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;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"UPDATE moz_inputhistory SET use_count = use_count * .975"),
getter_AddRefs(decayAdaptive));
NS_ENSURE_SUCCESS(rv, NS_OK);
// Delete any adaptive entries that won't help in ordering anymore
nsCOMPtr<mozIStorageStatement> deleteAdaptive;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"DELETE FROM moz_inputhistory WHERE use_count < .01"),
getter_AddRefs(deleteAdaptive));
NS_ENSURE_SUCCESS(rv, NS_OK);
nsCOMPtr<mozIStoragePendingStatement> ps;
mozIStorageStatement *stmts[] = {
decayFrecency,
decayAdaptive,
deleteAdaptive
};
rv = mDBConn->ExecuteAsync(stmts, NS_ARRAY_LENGTH(stmts), nsnull,
getter_AddRefs(ps));
NS_ENSURE_SUCCESS(rv, NS_OK);
(void)DecayFrecency();
(void)VacuumDatabase();
}
else if (strcmp(aTopic, NS_PRIVATE_BROWSING_SWITCH_TOPIC) == 0) {
if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_ENTER).Equals(aData)) {
mInPrivateBrowsing = PR_TRUE;
} else if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(aData)) {
}
else if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(aData)) {
mInPrivateBrowsing = PR_FALSE;
}
}
else if (strcmp(aTopic, PLACES_INIT_COMPLETE_EVENT_TOPIC) == 0) {
else if (strcmp(aTopic, PLACES_INIT_COMPLETE_TOPIC) == 0) {
nsCOMPtr<nsIObserverService> os =
do_GetService("@mozilla.org/observer-service;1");
NS_ENSURE_TRUE(os, NS_ERROR_FAILURE);
(void)os->RemoveObserver(this, PLACES_INIT_COMPLETE_EVENT_TOPIC);
(void)os->RemoveObserver(this, PLACES_INIT_COMPLETE_TOPIC);
// This code is only called if we've either imported or done a migration
// from a pre-frecency build, so we will calculate all their frecencies.
@ -5703,6 +5676,161 @@ nsNavHistory::Observe(nsISupports *aSubject, const char *aTopic,
return NS_OK;
}
NS_HIDDEN_(nsresult)
nsNavHistory::VacuumDatabase()
{
// SQLite cannot give us a real value for fragmentation percentage,
// we could analyze the database file page by page, and count fragmented
// space, but that would be slow and not maintainable across different SQLite
// versions.
// For this reason we just take a guess using the freelist count.
// This way we know how much pages are unused, but we don't know anything
// about fragmentation.
// This ratio is used in conjunction with a time pref to avoid vacuuming too
// often or too rarely.
PRInt32 lastVacuumPref;
PRInt64 lastVacuumTime = 0;
nsCOMPtr<nsIPrefBranch> prefSvc =
do_GetService("@mozilla.org/preferences-service;1");
NS_ENSURE_TRUE(prefSvc, NS_ERROR_OUT_OF_MEMORY);
if (NS_SUCCEEDED(prefSvc->GetIntPref(PREF_LAST_VACUUM, &lastVacuumPref))) {
// Value are seconds till epoch, convert it to microseconds.
lastVacuumTime = (PRInt64)lastVacuumPref * PR_USEC_PER_SEC;
}
nsresult rv;
float freePagesRatio = 0;
if (!lastVacuumTime ||
(lastVacuumTime < (PR_Now() - MIN_TIME_BEFORE_VACUUM) &&
lastVacuumTime > (PR_Now() - MAX_TIME_BEFORE_VACUUM))) {
// This is the first vacuum, or we are in the timeframe where vacuum could
// happen. Calculate the vacuum ratio and vacuum if it is less then
// threshold.
nsCOMPtr<mozIStorageStatement> statement;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("PRAGMA page_count"),
getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
PRBool hasResult = PR_FALSE;
rv = statement->ExecuteStep(&hasResult);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(hasResult, NS_ERROR_FAILURE);
PRInt32 pageCount = statement->AsInt32(0);
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("PRAGMA freelist_count"),
getter_AddRefs(statement));
NS_ENSURE_SUCCESS(rv, rv);
hasResult = PR_FALSE;
rv = statement->ExecuteStep(&hasResult);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(hasResult, NS_ERROR_FAILURE);
PRInt32 freelistCount = statement->AsInt32(0);
freePagesRatio = (float)(freelistCount / pageCount);
}
if (freePagesRatio > VACUUM_FREEPAGES_THRESHOLD ||
lastVacuumTime < (PR_Now() - MAX_TIME_BEFORE_VACUUM)) {
// We vacuum in 2 cases:
// - We are in the valid vacuum timeframe and vacuum ratio is high.
// - Last vacuum has been executed a lot of time ago.
// Notify we are about to vacuum. This is mostly for testability.
nsCOMPtr<nsIObserverService> observerService =
do_GetService("@mozilla.org/observer-service;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = observerService->NotifyObservers(nsnull,
PLACES_VACUUM_STARTING_TOPIC,
nsnull);
NS_ENSURE_SUCCESS(rv, rv);
// Actually vacuuming a database is a slow operation, since it could take
// seconds. Part of the time is spent in updating the journal file on disk
// and this is particularly bad on devices with slow I/O. Temporary
// moving the journal to memory could increase a bit the possibility of
// corruption if we crash during this time, but makes the process really
// faster.
nsCOMPtr<mozIStorageStatement> journalToMemory;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"PRAGMA journal_mode = MEMORY"),
getter_AddRefs(journalToMemory));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIStorageStatement> vacuum;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING("VACUUM"),
getter_AddRefs(vacuum));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<mozIStorageStatement> journalToDefault;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"PRAGMA journal_mode = " DEFAULT_JOURNAL_MODE),
getter_AddRefs(journalToDefault));
NS_ENSURE_SUCCESS(rv, rv);
mozIStorageStatement *stmts[] = {
journalToMemory,
vacuum,
journalToDefault
};
nsCOMPtr<mozIStoragePendingStatement> ps;
rv = mDBConn->ExecuteAsync(stmts, NS_ARRAY_LENGTH(stmts), nsnull,
getter_AddRefs(ps));
NS_ENSURE_SUCCESS(rv, rv);
rv = prefSvc->SetIntPref(PREF_LAST_VACUUM,
(PRInt32)(PR_Now() / PR_USEC_PER_SEC));
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
NS_HIDDEN_(nsresult)
nsNavHistory::DecayFrecency()
{
// Update frecency values.
nsresult rv = FixInvalidFrecencies();
NS_ENSURE_SUCCESS(rv, rv);
// 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. We directly modify moz_places to avoid
// bringing the whole database into places_temp through places_view. A
// scaling factor of .975 results in .5 the original value after 28 days.
nsCOMPtr<mozIStorageStatement> decayFrecency;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"UPDATE moz_places SET frecency = ROUND(frecency * .975) "
"WHERE frecency > 0"),
getter_AddRefs(decayFrecency));
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;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"UPDATE moz_inputhistory SET use_count = use_count * .975"),
getter_AddRefs(decayAdaptive));
NS_ENSURE_SUCCESS(rv, rv);
// Delete any adaptive entries that won't help in ordering anymore.
nsCOMPtr<mozIStorageStatement> deleteAdaptive;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"DELETE FROM moz_inputhistory WHERE use_count < .01"),
getter_AddRefs(deleteAdaptive));
NS_ENSURE_SUCCESS(rv, rv);
mozIStorageStatement *stmts[] = {
decayFrecency,
decayAdaptive,
deleteAdaptive
};
nsCOMPtr<mozIStoragePendingStatement> ps;
rv = mDBConn->ExecuteAsync(stmts, NS_ARRAY_LENGTH(stmts), nsnull,
getter_AddRefs(ps));
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// Lazy stuff ******************************************************************

View File

@ -98,8 +98,10 @@
// mInPrivateBrowsing member
#define PRIVATEBROWSING_NOTINITED (PRBool(0xffffffff))
#define PLACES_INIT_COMPLETE_EVENT_TOPIC "places-init-complete"
#define PLACES_DB_LOCKED_EVENT_TOPIC "places-database-locked"
#define PLACES_INIT_COMPLETE_TOPIC "places-init-complete"
#define PLACES_DB_LOCKED_TOPIC "places-database-locked"
#define PLACES_AUTOCOMPLETE_FEEDBACK_UPDATED_TOPIC "places-autocomplete-feedback-updated"
#define PLACES_VACUUM_STARTING_TOPIC "places-vacuum-starting"
class mozIAnnotationService;
class nsNavHistory;
@ -430,6 +432,15 @@ protected:
*/
nsresult FinalizeStatements();
/**
* Analyzes the database and VACUUM it, if needed.
*/
NS_HIDDEN_(nsresult) DecayFrecency();
/**
* Decays frecency and inputhistory values.
*/
NS_HIDDEN_(nsresult) VacuumDatabase();
// nsICharsetResolver
NS_DECL_NSICHARSETRESOLVER

View File

@ -0,0 +1,159 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Places unit test code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Marco Bonardo <mak77@bonardo.net> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
let bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
let bh = hs.QueryInterface(Ci.nsIBrowserHistory);
let dbConn = hs.QueryInterface(Ci.nsPIPlacesDatabase).DBConnection;
let os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
let prefs = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService).
getBranch("places.");
const LAST_VACUUM_PREF = "last_vacuum";
const VACUUM_THRESHOLD = 0.1;
const PLACES_VACUUM_STARTING_TOPIC = "places-vacuum-starting";
function getDBVacuumRatio() {
let freelistStmt = dbConn.createStatement("PRAGMA freelist_count");
freelistStmt.step();
let freelistCount = freelistStmt.row.freelist_count;
freelistStmt.finalize();
let pageCountStmt = dbConn.createStatement("PRAGMA page_count");
pageCountStmt.step();
let pageCount = pageCountStmt.row.page_count;
pageCountStmt.finalize();
let ratio = (freelistCount / pageCount);
return ratio;
}
function generateSparseDB(aType) {
let limit = 0;
if (aType == "low")
limit = 10;
else if (aType == "high")
limit = 200;
let batch = {
runBatched: function batch_runBatched() {
for (let i = 0; i < limit; i++) {
hs.addVisit(uri("http://" + i + ".mozilla.com/"),
Date.now() * 1000 + i, null, hs.TRANSITION_TYPED, false, 0);
}
for (let i = 0; i < limit; i++) {
bs.insertBookmark(bs.unfiledBookmarksFolder,
uri("http://" + i + "." + i + ".mozilla.com/"),
bs.DEFAULT_INDEX, "bookmark " + i);
}
}
}
hs.runInBatchMode(batch, null);
bh.removeAllPages();
bs.removeFolderChildren(bs.unfiledBookmarksFolder);
}
var gTests = [
{
desc: "Low ratio, last vacuum today",
ratio: "low",
elapsedDays: 0,
vacuum: false
},
{
desc: "Low ratio, last vacuum one month ago",
ratio: "low",
elapsedDays: 31,
vacuum: false
},
{
desc: "Low ratio, last vacuum two months ago",
ratio: "low",
elapsedDays: 61,
vacuum: true
},
{
desc: "High ratio, last vacuum today",
ratio: "high",
elapsedDays: 0,
vacuum: false
},
];
var observer = {
vacuum: false,
observe: function(aSubject, aTopic, aData) {
if (aTopic == PLACES_VACUUM_STARTING_TOPIC) {
this.vacuum = true;
}
}
}
os.addObserver(observer, PLACES_VACUUM_STARTING_TOPIC, false);
function run_test() {
while (gTests.length) {
observer.vacuum = false;
let test = gTests.shift();
print("PLACES TEST: " + test.desc);
let ratio = getDBVacuumRatio();
if (test.ratio == "high") {
if (ratio < VACUUM_THRESHOLD)
generateSparseDB("high");
do_check_true(getDBVacuumRatio() > VACUUM_THRESHOLD);
}
else if (test.ratio == "low") {
if (ratio == 0)
generateSparseDB("low");
do_check_true(getDBVacuumRatio() < VACUUM_THRESHOLD);
}
print("current ratio is " + getDBVacuumRatio());
prefs.setIntPref(LAST_VACUUM_PREF, ((Date.now() / 1000) - (test.elapsedDays * 24 * 60 * 60)));
os.notifyObservers(null, "idle-daily", null);
let testFunc = test.vacuum ? do_check_true : do_check_false;
testFunc(observer.vacuum);
}
os.removeObserver(observer, PLACES_VACUUM_STARTING_TOPIC);
}

View File

@ -0,0 +1,138 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Places unit test code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Marco Bonardo <mak77@bonardo.net> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
let bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
let bh = hs.QueryInterface(Ci.nsIBrowserHistory);
let dbConn = hs.QueryInterface(Ci.nsPIPlacesDatabase).DBConnection;
let os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
let prefs = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService).
getBranch("places.");
const LAST_VACUUM_PREF = "last_vacuum";
const VACUUM_THRESHOLD = 0.1;
const PLACES_VACUUM_STARTING_TOPIC = "places-vacuum-starting";
function getDBVacuumRatio() {
let freelistStmt = dbConn.createStatement("PRAGMA freelist_count");
freelistStmt.step();
let freelistCount = freelistStmt.row.freelist_count;
freelistStmt.finalize();
let pageCountStmt = dbConn.createStatement("PRAGMA page_count");
pageCountStmt.step();
let pageCount = pageCountStmt.row.page_count;
pageCountStmt.finalize();
let ratio = (freelistCount / pageCount);
return ratio;
}
function generateSparseDB(aType) {
let limit = 0;
if (aType == "low")
limit = 10;
else if (aType == "high")
limit = 200;
let batch = {
runBatched: function batch_runBatched() {
for (let i = 0; i < limit; i++) {
hs.addVisit(uri("http://" + i + ".mozilla.com/"),
Date.now() * 1000 + i, null, hs.TRANSITION_TYPED, false, 0);
}
for (let i = 0; i < limit; i++) {
bs.insertBookmark(bs.unfiledBookmarksFolder,
uri("http://" + i + "." + i + ".mozilla.com/"),
bs.DEFAULT_INDEX, "bookmark " + i);
}
}
}
hs.runInBatchMode(batch, null);
bh.removeAllPages();
bs.removeFolderChildren(bs.unfiledBookmarksFolder);
}
var gTests = [
{
desc: "High ratio, last vacuum two months ago",
ratio: "high",
elapsedDays: 61,
vacuum: true
},
];
var observer = {
vacuum: false,
observe: function(aSubject, aTopic, aData) {
if (aTopic == PLACES_VACUUM_STARTING_TOPIC) {
this.vacuum = true;
}
}
}
os.addObserver(observer, PLACES_VACUUM_STARTING_TOPIC, false);
function run_test() {
while (gTests.length) {
observer.vacuum = false;
let test = gTests.shift();
print("PLACES TEST: " + test.desc);
let ratio = getDBVacuumRatio();
if (test.ratio == "high") {
if (ratio < VACUUM_THRESHOLD)
generateSparseDB("high");
do_check_true(getDBVacuumRatio() > VACUUM_THRESHOLD);
}
else if (test.ratio == "low") {
if (ratio == 0)
generateSparseDB("low");
do_check_true(getDBVacuumRatio() < VACUUM_THRESHOLD);
}
print("current ratio is " + getDBVacuumRatio());
prefs.setIntPref(LAST_VACUUM_PREF, ((Date.now() / 1000) - (test.elapsedDays * 24 * 60 * 60)));
os.notifyObservers(null, "idle-daily", null);
let testFunc = test.vacuum ? do_check_true : do_check_false;
testFunc(observer.vacuum);
}
os.removeObserver(observer, PLACES_VACUUM_STARTING_TOPIC);
}

View File

@ -0,0 +1,138 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Places unit test code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Marco Bonardo <mak77@bonardo.net> (Original Author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
let bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
getService(Ci.nsINavBookmarksService);
let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
getService(Ci.nsINavHistoryService);
let bh = hs.QueryInterface(Ci.nsIBrowserHistory);
let dbConn = hs.QueryInterface(Ci.nsPIPlacesDatabase).DBConnection;
let os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
let prefs = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService).
getBranch("places.");
const LAST_VACUUM_PREF = "last_vacuum";
const VACUUM_THRESHOLD = 0.1;
const PLACES_VACUUM_STARTING_TOPIC = "places-vacuum-starting";
function getDBVacuumRatio() {
let freelistStmt = dbConn.createStatement("PRAGMA freelist_count");
freelistStmt.step();
let freelistCount = freelistStmt.row.freelist_count;
freelistStmt.finalize();
let pageCountStmt = dbConn.createStatement("PRAGMA page_count");
pageCountStmt.step();
let pageCount = pageCountStmt.row.page_count;
pageCountStmt.finalize();
let ratio = (freelistCount / pageCount);
return ratio;
}
function generateSparseDB(aType) {
let limit = 0;
if (aType == "low")
limit = 10;
else if (aType == "high")
limit = 200;
let batch = {
runBatched: function batch_runBatched() {
for (let i = 0; i < limit; i++) {
hs.addVisit(uri("http://" + i + ".mozilla.com/"),
Date.now() * 1000 + i, null, hs.TRANSITION_TYPED, false, 0);
}
for (let i = 0; i < limit; i++) {
bs.insertBookmark(bs.unfiledBookmarksFolder,
uri("http://" + i + "." + i + ".mozilla.com/"),
bs.DEFAULT_INDEX, "bookmark " + i);
}
}
}
hs.runInBatchMode(batch, null);
bh.removeAllPages();
bs.removeFolderChildren(bs.unfiledBookmarksFolder);
}
var gTests = [
{
desc: "High ratio, last vacuum two months ago",
ratio: "high",
elapsedDays: 61,
vacuum: true
},
];
var observer = {
vacuum: false,
observe: function(aSubject, aTopic, aData) {
if (aTopic == PLACES_VACUUM_STARTING_TOPIC) {
this.vacuum = true;
}
}
}
os.addObserver(observer, PLACES_VACUUM_STARTING_TOPIC, false);
function run_test() {
while (gTests.length) {
observer.vacuum = false;
let test = gTests.shift();
print("PLACES TEST: " + test.desc);
let ratio = getDBVacuumRatio();
if (test.ratio == "high") {
if (ratio < VACUUM_THRESHOLD)
generateSparseDB("high");
do_check_true(getDBVacuumRatio() > VACUUM_THRESHOLD);
}
else if (test.ratio == "low") {
if (ratio == 0)
generateSparseDB("low");
do_check_true(getDBVacuumRatio() < VACUUM_THRESHOLD);
}
print("current ratio is " + getDBVacuumRatio());
prefs.setIntPref(LAST_VACUUM_PREF, ((Date.now() / 1000) - (test.elapsedDays * 24 * 60 * 60)));
os.notifyObservers(null, "idle-daily", null);
let testFunc = test.vacuum ? do_check_true : do_check_false;
testFunc(observer.vacuum);
}
os.removeObserver(observer, PLACES_VACUUM_STARTING_TOPIC);
}