sqlite3orm
Version:
ORM for sqlite3 and TypeScript/JavaScript
227 lines • 8.01 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SqlConnectionPool = void 0;
const tslib_1 = require("tslib");
const node_utils_1 = require("@homeofthings/node-utils");
const _dbg = tslib_1.__importStar(require("debug"));
const SqlConnectionPoolDatabase_1 = require("./SqlConnectionPoolDatabase");
const SqlDatabase_1 = require("./SqlDatabase");
const debug = _dbg.default('sqlite3orm:pool');
/**
* A simple connection pool
*
* @export
* @class SqlConnectionPool
*/
class SqlConnectionPool {
name;
databaseFile;
mode;
min;
max;
inPool;
inUse;
settings;
_opening;
get poolSize() {
return this.inPool.length;
}
get openSize() {
return this.inUse.size;
}
/**
* Creates an instance of SqlConnectionPool.
*/
constructor(name = '') {
this.name = name;
this.databaseFile = undefined;
this.mode = SqlDatabase_1.SQL_OPEN_DEFAULT;
this.inUse = new Set();
this.inPool = [];
this.min = this.max = 0;
}
/**
* Open a database connection pool
*
* @param databaseFile - The path to the database file or URI
* @param [mode=SQL_OPEN_DEFAULT] - A bit flag combination of: SQL_OPEN_CREATE |
* SQL_OPEN_READONLY | SQL_OPEN_READWRITE
* @param [min=1] minimum connections which should be opened by this connection pool
* @param [max=0] maximum connections which can be opened by this connection pool
* @returns A promise
*/
async open(databaseFile, mode = SqlDatabase_1.SQL_OPEN_DEFAULT, min = 1, max = 0, settings) {
if (this._opening) {
try {
await this._opening;
if (this.databaseFile === databaseFile && (mode & ~SqlDatabase_1.SQL_OPEN_CREATE) === this.mode) {
// already opened
return;
}
}
catch (err) {
/* empty */
}
}
this._opening = this.openInternal(databaseFile, mode, min, max, settings);
try {
await this._opening;
}
catch (err) {
return Promise.reject(err);
}
finally {
this._opening = undefined;
}
return;
}
async openInternal(databaseFile, mode = SqlDatabase_1.SQL_OPEN_DEFAULT, min = 1, max = 0, settings) {
try {
await this.close();
}
catch (err) {
/* empty */
}
try {
this.databaseFile = databaseFile;
this.mode = mode;
this.min = min;
this.max = max;
this.settings = settings;
this.inPool.length = 0;
const promises = [];
if (this.min < 1) {
this.min = 1;
}
let sqldb = new SqlConnectionPoolDatabase_1.SqlConnectionPoolDatabase();
await sqldb.openByPool(this, this.databaseFile, this.mode, this.settings);
this.inPool.push(sqldb);
this.mode &= ~SqlDatabase_1.SQL_OPEN_CREATE;
for (let i = 1; i < this.min; i++) {
sqldb = new SqlConnectionPoolDatabase_1.SqlConnectionPoolDatabase();
promises.push(sqldb.openByPool(this, this.databaseFile, this.mode, this.settings));
this.inPool.push(sqldb);
}
await Promise.all(promises);
if (this.name.length) {
SqlConnectionPool.openNamedPools.set(this.name, this);
}
debug(`pool ${this.name}: opened: ${this.inUse.size} connections open (${this.inPool.length} in pool)`);
}
catch (err) {
try {
await this.close();
}
catch (_ignore) {
/* empty */
}
debug(`pool ${this.name}: opening ${databaseFile} failed: ${err.message}`);
return Promise.reject(err);
}
}
/**
* Close the database connection pool
*
* @returns A promise
*/
async close() {
try {
if (this.databaseFile) {
if (this.inUse.size) {
debug(`pool ${this.name}: closing: forcibly closing ${this.inUse.size} opened connections (${this.inPool.length} in pool)`);
}
else {
debug(`pool ${this.name}: closing: ${this.inUse.size} connections open (${this.inPool.length} in pool)`);
}
}
if (this.name.length) {
SqlConnectionPool.openNamedPools.delete(this.name);
}
this.databaseFile = undefined;
this.mode = SqlDatabase_1.SQL_OPEN_DEFAULT;
const promises = [];
this.inPool.forEach((value) => {
promises.push(value.closeByPool());
});
this.inPool.length = 0;
this.inUse.forEach((value) => {
promises.push(value.closeByPool());
});
this.inUse.clear();
await Promise.all(promises);
}
catch (err) {
/* istanbul ignore next */ debug(`pool ${this.name}: closing failed: ${err.message}`);
return Promise.reject(err);
}
}
/**
* test if this connection pool is connected to a database file
*/
isOpen() {
return !!this.databaseFile;
}
/**
* get a connection from the pool
*
* @param [timeout=0] The timeout to wait for a connection ( 0 is infinite )
* @returns A promise of the db connection
*/
async get(timeout = 0) {
try {
let sqldb;
const cond = () => this.inPool.length > 0;
if (this.max > 0 && !cond() && this.inUse.size >= this.max) {
await (0, node_utils_1.wait)(cond, timeout);
}
if (this.inPool.length > 0) {
sqldb = this.inPool.shift();
this.inUse.add(sqldb);
debug(`pool ${this.name}: ${this.inUse.size} connections open (${this.inPool.length} in pool)`);
return sqldb;
}
if (!this.databaseFile) {
throw new Error(`connection pool not opened`);
}
sqldb = new SqlConnectionPoolDatabase_1.SqlConnectionPoolDatabase();
await sqldb.openByPool(this, this.databaseFile, this.mode, this.settings);
this.inUse.add(sqldb);
debug(`pool ${this.name}: ${this.inUse.size} connections open (${this.inPool.length} in pool)`);
return sqldb;
}
catch (err) {
debug(`pool ${this.name}: getting connection from pool failed: ${err.message}`);
return Promise.reject(err);
}
}
/**
* release a connection to the pool
*
* @param sqldb - The db connection
*/
async release(sqldb) {
/* istanbul ignore if */
if (!(sqldb instanceof SqlConnectionPoolDatabase_1.SqlConnectionPoolDatabase) || this !== sqldb.pool) {
// not opened by this pool
return sqldb.close();
}
this.inUse.delete(sqldb);
/* istanbul ignore else */
if (sqldb.isOpen()) {
if (sqldb.dirty || this.inPool.length >= this.min) {
// close database connection
await sqldb.closeByPool();
}
else {
// transfer database connection
const newsqldb = new SqlConnectionPoolDatabase_1.SqlConnectionPoolDatabase();
await newsqldb.recycleByPool(this, sqldb, this.settings);
this.inPool.push(newsqldb);
}
debug(`pool ${this.name}: ${this.inUse.size} connections open (${this.inPool.length} in pool)`);
}
}
static openNamedPools = new Map();
}
exports.SqlConnectionPool = SqlConnectionPool;
//# sourceMappingURL=SqlConnectionPool.js.map