@libsql/client
Version:
libSQL driver for TypeScript and JavaScript
92 lines (91 loc) • 3.24 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SqlCache = void 0;
class SqlCache {
#owner;
#sqls;
capacity;
constructor(owner, capacity) {
this.#owner = owner;
this.#sqls = new Lru();
this.capacity = capacity;
}
// Replaces SQL strings with cached `hrana.Sql` objects in the statements in `hranaStmts`. After this
// function returns, we guarantee that all `hranaStmts` refer to valid (not closed) `hrana.Sql` objects,
// but _we may invalidate any other `hrana.Sql` objects_ (by closing them, thus removing them from the
// server).
//
// In practice, this means that after calling this function, you can use the statements only up to the
// first `await`, because concurrent code may also use the cache and invalidate those statements.
apply(hranaStmts) {
if (this.capacity <= 0) {
return;
}
const usedSqlObjs = new Set();
for (const hranaStmt of hranaStmts) {
if (typeof hranaStmt.sql !== "string") {
continue;
}
const sqlText = hranaStmt.sql;
// Stored SQL cannot exceed 5kb.
// https://github.com/tursodatabase/libsql/blob/e9d637e051685f92b0da43849507b5ef4232fbeb/libsql-server/src/hrana/http/request.rs#L10
if (sqlText.length >= 5000) {
continue;
}
let sqlObj = this.#sqls.get(sqlText);
if (sqlObj === undefined) {
while (this.#sqls.size + 1 > this.capacity) {
const [evictSqlText, evictSqlObj] = this.#sqls.peekLru();
if (usedSqlObjs.has(evictSqlObj)) {
// The SQL object that we are trying to evict is already in use in this batch, so we
// must not evict and close it.
break;
}
evictSqlObj.close();
this.#sqls.delete(evictSqlText);
}
if (this.#sqls.size + 1 <= this.capacity) {
sqlObj = this.#owner.storeSql(sqlText);
this.#sqls.set(sqlText, sqlObj);
}
}
if (sqlObj !== undefined) {
hranaStmt.sql = sqlObj;
usedSqlObjs.add(sqlObj);
}
}
}
}
exports.SqlCache = SqlCache;
class Lru {
// This maps keys to the cache values. The entries are ordered by their last use (entires that were used
// most recently are at the end).
#cache;
constructor() {
this.#cache = new Map();
}
get(key) {
const value = this.#cache.get(key);
if (value !== undefined) {
// move the entry to the back of the Map
this.#cache.delete(key);
this.#cache.set(key, value);
}
return value;
}
set(key, value) {
this.#cache.set(key, value);
}
peekLru() {
for (const entry of this.#cache.entries()) {
return entry;
}
return undefined;
}
delete(key) {
this.#cache.delete(key);
}
get size() {
return this.#cache.size;
}
}