Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ SQLite is great for high-performance local cache. This library is perfectly viab
database: ':memory:', // or path to your database on disk
defaultTtlMs: 1000 * 60 * 60, // optional TTL in milliseconds
maxItems: 1000, // optional LRU
compress: true, // use gzip for values > 1024 bytes, can be smaller, but slower
compress: true, // use gzip for values > 1024 bytes, can be smaller, but slower,
cacheTableName: "cache1234" // Optional and will default to "cache"
});

await cache.set('bar', 'baz')
Expand Down
29 changes: 18 additions & 11 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,28 @@ export interface SqliteCacheConfiguration {
* @default false
*/
readonly compress?: boolean;

/**
* The name of the cache table in the database
* @default "cache"
*/
readonly cacheTableName?: string;
}

const configurationSchema = z.object({
database: z.string(),
defaultTtlMs: z.number().positive().optional(),
maxItems: z.number().positive().optional(),
compress: z.boolean().optional().default(false),
cacheTableName: z.string().optional().default("cache")
});

async function initSqliteCache(configuration: SqliteCacheConfiguration) {
const db = new Database(configuration.database, {});

db.transaction(() => {
db.prepare(
`CREATE TABLE IF NOT EXISTS cache (
`CREATE TABLE IF NOT EXISTS ${configuration.cacheTableName} (
key TEXT PRIMARY KEY,
value BLOB,
expires INT,
Expand All @@ -54,33 +61,33 @@ async function initSqliteCache(configuration: SqliteCacheConfiguration) {
)`
).run();

db.prepare(`CREATE UNIQUE INDEX IF NOT EXISTS key ON cache (key)`).run();
db.prepare(`CREATE INDEX IF NOT EXISTS expires ON cache (expires)`).run();
db.prepare(`CREATE UNIQUE INDEX IF NOT EXISTS key ON ${configuration.cacheTableName} (key)`).run();
db.prepare(`CREATE INDEX IF NOT EXISTS expires ON ${configuration.cacheTableName} (expires)`).run();
db.prepare(
`CREATE INDEX IF NOT EXISTS lastAccess ON cache (lastAccess)`
`CREATE INDEX IF NOT EXISTS lastAccess ON ${configuration.cacheTableName} (lastAccess)`
).run();
})();

return {
db,
getStatement: db.prepare(
`UPDATE OR IGNORE cache
`UPDATE OR IGNORE ${configuration.cacheTableName}
SET lastAccess = @now
WHERE key = @key AND (expires > @now OR expires IS NULL)
RETURNING value, compressed`
),
setStatement: db.prepare(
`INSERT OR REPLACE INTO cache
`INSERT OR REPLACE INTO ${configuration.cacheTableName}
(key, value, expires, lastAccess, compressed) VALUES (@key, @value, @expires, @now, @compressed)`
),
deleteStatement: db.prepare(`DELETE FROM cache WHERE key = @key`),
clearStatement: db.prepare(`DELETE FROM cache`),
deleteStatement: db.prepare(`DELETE FROM ${configuration.cacheTableName} WHERE key = @key`),
clearStatement: db.prepare(`DELETE FROM ${configuration.cacheTableName}`),
cleanupExpiredStatement: db.prepare(`
DELETE FROM cache WHERE expires < @now
DELETE FROM ${configuration.cacheTableName} WHERE expires < @now
`),
cleanupLruStatement: db.prepare(`
WITH lru AS (SELECT key FROM cache ORDER BY lastAccess DESC LIMIT -1 OFFSET @maxItems)
DELETE FROM cache WHERE key IN lru
WITH lru AS (SELECT key FROM ${configuration.cacheTableName} ORDER BY lastAccess DESC LIMIT -1 OFFSET @maxItems)
DELETE FROM ${configuration.cacheTableName} WHERE key IN lru
`),
};
}
Expand Down