Skip to content

Commit

Permalink
Vacuum the sqlite rpmdb if necessary
Browse files Browse the repository at this point in the history
Check if there is 20MB in free pages and then execute a VACUUM command.
The threshold can be controlled by the _sqlite_vacuum macro. Don't add
this to the macros file on purpose as we don't want people to get
involved with such details. Here it is mainly used for testing.

Using a 20 MB threshold should prevent the vacuuming to happend too often
while still triggering after large transactions. As we install new
headers first and then remove the old ones transactions leave behind large
amounts of free pages.

We do not use PRAGMA auto_vacuum here as it does not defrag the
database and only frees empty pages. So it still requires running VACUUM
from time to time. Freeing the empty pages would get rid of the condition we
use here for running VACUUM.

Using pure C for the macro expansion on purpopse in case we want to back
port this.

Resolves: #3309
  • Loading branch information
ffesti committed Nov 18, 2024
1 parent 010564a commit 5e674ed
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 0 deletions.
26 changes: 26 additions & 0 deletions lib/backend/sqlite.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <sqlite3.h>
#include <fcntl.h>
#include <cinttypes>

#include <rpm/rpmlog.h>
#include <rpm/rpmfileutil.h>
Expand Down Expand Up @@ -185,6 +186,20 @@ static int sqlite_init(rpmdb rdb, const char * dbhome)
return rc;
}

static long sqlite_free_space(sqlite3 * db)
{
long result = 0;
sqlite3_stmt *s = NULL;
const char *cmd = "SELECT (freelist_count*page_size) as FreeSizeEstimate FROM pragma_freelist_count, pragma_page_size ;";
if (sqlite3_prepare_v2(db, cmd, -1, &s, NULL) == SQLITE_OK) {
while (sqlite3_step(s) == SQLITE_ROW) {
result = sqlite3_column_int64(s, 0);
}
sqlite3_finalize(s);
}
return result;
}

static int sqlite_fini(rpmdb rdb)
{
int rc = 0;
Expand All @@ -194,8 +209,19 @@ static int sqlite_fini(rpmdb rdb)
rdb->db_opens--;
} else {
if (sqlite3_db_readonly(sdb, NULL) == 0) {

sqlexec(sdb, "PRAGMA optimize");
sqlexec(sdb, "PRAGMA wal_checkpoint = TRUNCATE");

int max_size = rpmExpandNumeric("%{?_sqlite_vacuum}");
if (max_size <= 0)
max_size = 20*1024*1024;
int64_t free_space = sqlite_free_space(sdb);

if (free_space > max_size) {
sqlexec(sdb, "VACUUM");
rpmlog(RPMLOG_DEBUG, "Rpmdb Sqlite backend VACUUM maxfree: %i, free: %" PRIu64 " -> %" PRIu64 "\n", max_size, free_space, sqlite_free_space(sdb));
}
}
rdb->db_dbenv = NULL;
int xx = sqlite3_close(sdb);
Expand Down
17 changes: 17 additions & 0 deletions tests/rpmdb.at
Original file line number Diff line number Diff line change
Expand Up @@ -683,3 +683,20 @@ runroot rpm -q 'versiontest-1.0~2-1'
[])

RPMTEST_CLEANUP

# ------------------------------
AT_SETUP([rpmdb vacuum])
AT_KEYWORDS([install rpmdb sqlite])
RPMDB_INIT
RPMTEST_CHECK([
runroot rpm -U --noscripts --nodeps --ignorearch --noverify \
/data/RPMS/hello-1.0-1.i386.rpm
runroot rpm -D "_sqlite_vacuum 1024" -vv -U --noscripts --nodeps --ignorearch \
/data/RPMS/hello-2.0-1.i686.rpm 2>&1 | grep VACUUM
],
[0],
[D: VACUUM: 0
D: Rpmdb Sqlite backend VACUUM maxfree: 1024, free: 8192 -> 0
],
[])
RPMTEST_CLEANUP

0 comments on commit 5e674ed

Please sign in to comment.