renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
127 lines (126 loc) • 4.18 kB
JavaScript
import { getEnv } from "../../../env.js";
import { logger } from "../../../../logger/index.js";
import { ensureDir } from "../../../fs/index.js";
import { PackageCacheBase } from "./base.js";
import { parseInteger } from "../../../number.js";
import { promisify } from "node:util";
import fs from "fs-extra";
import upath from "upath";
import zlib, { constants } from "node:zlib";
//#region lib/util/cache/package/impl/sqlite.ts
const { exists } = fs;
const brotliCompress = promisify(zlib.brotliCompress);
const brotliDecompress = promisify(zlib.brotliDecompress);
function compress(input) {
return brotliCompress(JSON.stringify(input), { params: {
[constants.BROTLI_PARAM_MODE]: constants.BROTLI_MODE_TEXT,
[constants.BROTLI_PARAM_QUALITY]: 3
} });
}
async function decompress(input) {
const jsonStr = (await brotliDecompress(input)).toString("utf8");
return JSON.parse(jsonStr);
}
var PackageCacheSqlite = class PackageCacheSqlite extends PackageCacheBase {
static async create(cacheDir) {
const { DatabaseSync: Sqlite } = await import("node:sqlite");
const sqliteDir = upath.join(cacheDir, "renovate/renovate-cache-sqlite");
await ensureDir(sqliteDir);
const sqliteFile = upath.join(sqliteDir, "db.sqlite");
if (await exists(sqliteFile)) logger.debug(`Using SQLite package cache: ${sqliteFile}`);
else logger.debug(`Creating SQLite package cache: ${sqliteFile}`);
const { RENOVATE_X_SQLITE_BUSY_TIMEOUT } = getEnv();
return new PackageCacheSqlite(new Sqlite(sqliteFile, { timeout: parseInteger(RENOVATE_X_SQLITE_BUSY_TIMEOUT, 5e3) }));
}
upsertStatement;
getStatement;
deleteExpiredRows;
countStatement;
client;
constructor(client) {
super();
this.client = client;
client.exec("PRAGMA journal_mode = WAL");
client.exec("PRAGMA encoding = 'UTF-8'");
client.exec(`
CREATE TABLE IF NOT EXISTS package_cache (
namespace TEXT NOT NULL,
key TEXT NOT NULL,
expiry INTEGER NOT NULL,
data BLOB NOT NULL,
PRIMARY KEY (namespace, key)
)
`);
client.exec("CREATE INDEX IF NOT EXISTS expiry ON package_cache (expiry)");
this.upsertStatement = client.prepare(`
INSERT INTO package_cache (namespace, key, data, expiry)
VALUES (@namespace, @key, @data, unixepoch() + @ttlSeconds)
ON CONFLICT (namespace, key) DO UPDATE SET
data = @data,
expiry = unixepoch() + @ttlSeconds
`);
this.getStatement = client.prepare(`
SELECT data FROM package_cache
WHERE
namespace = @namespace AND key = @key AND expiry > unixepoch()
`);
this.deleteExpiredRows = client.prepare(`
DELETE FROM package_cache
WHERE expiry <= unixepoch()
`);
this.countStatement = client.prepare("SELECT COUNT(*) as total FROM package_cache");
}
async set(namespace, key, value, hardTtlMinutes) {
try {
const compressedData = await compress(value);
const ttlSeconds = hardTtlMinutes * 60;
this.upsertStatement.run({
namespace,
key,
data: compressedData,
ttlSeconds
});
} catch (err) {
logger.once.warn({ err }, "Error while setting SQLite cache value");
}
}
async get(namespace, key) {
try {
const data = this.getStatement.get({
namespace,
key
})?.data;
if (!data) {
logger.trace({
namespace,
key
}, "Cache miss");
return;
}
return await decompress(data);
} catch (err) {
logger.once.warn({ err }, "Error while reading SQLite cache value");
return;
}
}
destroy() {
try {
const startTime = Date.now();
const totalCount = this.countStatement.get().total;
const { changes: deletedCount } = this.deleteExpiredRows.run();
const durationMs = Date.now() - startTime;
logger.debug(`SQLite package cache: deleted ${deletedCount} of ${totalCount} entries in ${durationMs}ms`);
} catch (err) {
logger.warn({ err }, "SQLite package cache cleanup failed");
}
try {
this.client.close();
} catch (err) {
logger.warn({ err }, "SQLite package cache close failed");
}
return Promise.resolve();
}
};
//#endregion
export { PackageCacheSqlite };
//# sourceMappingURL=sqlite.js.map