Optional prom-client peer dependency

This commit is contained in:
Erik 2023-11-11 17:11:47 +02:00
parent ee67d81e94
commit 460ded7641
5 changed files with 97 additions and 27 deletions

View File

@ -22,7 +22,9 @@
"build/**/*"
],
"scripts": {
"build": "tsc && tsc -p tsconfig.cjs.json && node ./scripts/declareTypes.js",
"build": "tsc && tsc -p tsconfig.cjs.json && node ./scripts/declareTypes.js --common --module",
"build:cjs": "tsc -p tsconfig.cjs.json && node ./scripts/declareTypes.js --common",
"build:module": "tsc && node ./scripts/declareTypes.js --module",
"test": "yarn build && jest",
"release": "yarn build && yarn publish",
"lint": "eslint --fix"
@ -41,5 +43,13 @@
"amqplib": "^0.10.3",
"mongodb": "^5.7.0",
"mysql": "^2.18.1"
},
"peerDependencies": {
"prom-client": "^15.0.0"
},
"peerDependenciesMeta": {
"prom-client": {
"optional": true
}
}
}

View File

@ -1,3 +1,5 @@
import fs from 'node:fs';
fs.writeFileSync('./build/cjs/package.json', JSON.stringify({ type: 'commonjs' }));
fs.writeFileSync('./build/esm/package.json', JSON.stringify({ type: 'module' }));
if (process.argv.includes('--common'))
fs.writeFileSync('./build/cjs/package.json', JSON.stringify({ type: 'commonjs' }));
if (process.argv.includes('--module'))
fs.writeFileSync('./build/esm/package.json', JSON.stringify({ type: 'module' }));

View File

@ -25,7 +25,8 @@ export type MariaOptions = {
client?: PoolConfig,
credentials: Credentials,
loggerOptions?: LoggerClientOptions,
donorQuery?: boolean
donorQuery?: boolean,
recordMetrics?: boolean
}
type MariaError = {
@ -95,6 +96,8 @@ class MariaDB
#pool: PoolCluster | null;
#nodes: Node[];
#canQueryDonor: boolean;
#queryHistogram?: {startTimer: (labels: object) => () => void};
#server: IServer;
constructor (server: IServer, options: MariaOptions)
{
@ -128,7 +131,7 @@ class MariaDB
this.#cluster = this.#credentials.length > 1;
this.#canQueryDonor = options.donorQuery ?? false;
this.#logger = server.createLogger(this, options.loggerOptions);
this.#server = server;
}
get ready ()
@ -141,6 +144,7 @@ class MariaDB
return this.#_activeQueries;
}
// eslint-disable-next-line max-lines-per-function
async init ()
{
if (!this.#load)
@ -230,10 +234,27 @@ class MariaDB
}
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const Prometheus = await import('prom-client').catch(() => null);
if (this.#config.recordMetrics && !Prometheus)
{
this.#logger.warn('Metrics recording was enabled but missing prom-client dependency');
}
else if (this.#config.recordMetrics && Prometheus)
{
this.#logger.info('Setting up metric recording');
this.#queryHistogram = new Prometheus.Histogram({
name: 'sql_queries',
help: 'Tracks query duration and frequency',
buckets: Prometheus?.exponentialBuckets(0.005, 2, 10),
labelNames: [ 'type' ] as const
});
this.#server.registerMetric(this.#queryHistogram!);
}
this.#_ready = true;
return this;
}
async close ()
@ -382,10 +403,11 @@ class MariaDB
Promise<T[] | FieldInfo[] | [OkPacket]>
{
const connection = await this.getConnection(node ?? null, errorIfNodeUnavailable ?? false);
try
try
{
const result = await new Promise<T[] | FieldInfo[] | [OkPacket]| undefined>((resolve, reject) =>
{
const endTimer = this.#queryHistogram?.startTimer({ type: [ 'SELECT', 'UPDATE', 'INSERT', 'DELETE' ].find(entry => query.toUpperCase().includes(entry)) ?? 'OTHER' });
const q = connection.query({ timeout, sql: query }, values, (err, results, fields) =>
{
if (err)
@ -397,6 +419,8 @@ class MariaDB
else
resolve(fields);
connection.release();
if (endTimer)
endTimer();
});
this.#logger.debug(`Constructed query: ${q.sql.substring(0, 1024)}`);
});

View File

@ -21,7 +21,8 @@ export type MongoOptions = {
credentials: Credentials,
loggerOptions?: LoggerClientOptions,
client?: MongoClientOptions,
load?: boolean
load?: boolean,
recordMetrics?: boolean
}
type StringIndexable = {[key: string]: boolean | string | number | Document | object}
@ -54,6 +55,8 @@ class MongoDB
#db: Db | null;
#_client: MongoClient;
#queryHistogram?: { startTimer: (labels: object) => () => void };
#server: IServer;
constructor (server: IServer, config: MongoOptions)
{
@ -73,6 +76,7 @@ class MongoDB
this.#load = config.load ?? true;
this.#logger = server.createLogger(this, config.loggerOptions);
this.#server = server;
if (URI)
{
@ -122,7 +126,6 @@ class MongoDB
*/
async init ()
{
if (!this.#load)
return this.#logger.info('Not loading MongoDB');
@ -133,12 +136,29 @@ class MongoDB
await this.#_client.connect();
this.#logger.debug('Connected, selecting DB');
this.#db = await this.#_client.db(this.#_database);
this.#db = this.#_client.db(this.#_database);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const Prometheus = await import('prom-client').catch(() => null);
if (this.#config.recordMetrics && !Prometheus)
{
this.#logger.warn('Metrics recording was enabled but missing prom-client dependency');
}
else if (this.#config.recordMetrics && Prometheus)
{
this.#logger.info('Setting up metric recording');
this.#queryHistogram = new Prometheus.Histogram({
name: 'mongo_queries',
help: 'Tracks query duration and frequency',
buckets: Prometheus?.exponentialBuckets(0.005, 2, 10),
labelNames: [ 'type' ] as const
});
this.#server.registerMetric(this.#queryHistogram!);
}
this.#logger.status('MongoDB ready');
return this;
}
async close ()
@ -167,7 +187,6 @@ class MongoDB
*/
async find<T extends Document> (db: string, query: MongoQuery, options?: FindOptions<T>): Promise<WithId<T>[]>
{
if (!this.#db)
throw new Error('MongoDB not connected');
@ -179,9 +198,12 @@ class MongoDB
this.#logger.debug(`Incoming find query for ${db} with parameters ${inspect(query)}`);
const endTimer = this.#queryHistogram?.startTimer({ type: 'find' });
const cursor = this.#db.collection<T>(db).find(query as Filter<T>, options);
return cursor.toArray();
const data = await cursor.toArray();
if (endTimer)
endTimer();
return data;
}
/**
@ -194,7 +216,6 @@ class MongoDB
*/
async findOne<T extends Document> (db: string, query: MongoQuery, options: FindOptions<T> = {}): Promise<WithId<T> | null>
{
if (!this.#db)
throw new Error('MongoDB not connected');
if (typeof db !== 'string')
@ -204,9 +225,11 @@ class MongoDB
query._id = new ObjectId(query._id);
this.#logger.debug(`Incoming findOne query for ${db} with parameters ${inspect(query)}`);
const endTimer = this.#queryHistogram?.startTimer({ type: 'findOne' });
const result = await this.#db.collection<T>(db).findOne(query as Filter<T>, options);
if (endTimer)
endTimer();
return result;
}
/**
@ -220,7 +243,6 @@ class MongoDB
*/
async updateMany<T extends Document> (db: string, filter: MongoQuery, data: T, upsert = false)
{
if (!this.#db)
throw new Error('MongoDB not connected');
if (typeof db !== 'string')
@ -231,9 +253,11 @@ class MongoDB
filter._id = new ObjectId(filter._id);
this.#logger.debug(`Incoming update query for '${db}' with parameters\n${inspect(filter)}\nand data\n${inspect(data)}`);
const endTimer = this.#queryHistogram?.startTimer({ type: 'updateMany' });
const result = await this.#db.collection<T>(db).updateMany(filter as Filter<T>, { $set: data }, { upsert });
if (endTimer)
endTimer();
return result;
}
/**
@ -247,7 +271,6 @@ class MongoDB
*/
async updateOne (db: string, filter: MongoQuery, data: Document, upsert = false)
{
if (!this.#db)
throw new Error('MongoDB not connected');
if (typeof db !== 'string')
@ -256,9 +279,11 @@ class MongoDB
filter._id = new ObjectId(filter._id);
this.#logger.debug(`Incoming updateOne query for ${db} with parameters ${inspect(filter)}`);
const endTimer = this.#queryHistogram?.startTimer({ type: 'updateOne' });
const result = await this.#db.collection(db).updateOne(filter as Filter<Document>, { $set: data }, { upsert });
if (endTimer)
endTimer();
return result;
}
/**
@ -272,7 +297,6 @@ class MongoDB
*/
async insertOne (db: string, data: Document)
{
if (!this.#db)
throw new Error('MongoDB not connected');
if (typeof db !== 'string')
@ -281,9 +305,11 @@ class MongoDB
data._id = new ObjectId(data._id);
this.#logger.debug(`Incoming insertOne query for ${db} with parameters ${inspect(data)}`);
const endTimer = this.#queryHistogram?.startTimer({ type: 'insertOne' });
const result = await this.#db.collection(db).insertOne(data);
if (endTimer)
endTimer();
return result;
}
async deleteOne (db: string, filter: Document)
@ -296,7 +322,10 @@ class MongoDB
filter._id = new ObjectId(filter._id);
this.#logger.debug(`Incoming deleteOne query for ${db} with parameters ${inspect(filter)}`);
const endTimer = this.#queryHistogram?.startTimer({ type: 'deleteOne' });
const result = await this.#db.collection(db).deleteOne(filter);
if (endTimer)
endTimer();
return result;
}
@ -311,7 +340,10 @@ class MongoDB
if (typeof filter._id === 'string')
filter._id = new ObjectId(filter._id);
const endTimer = this.#queryHistogram?.startTimer({ type: 'findOneAndDelete' });
const result = await this.#db.collection<T>(db).findOneAndDelete(filter as Filter<T>, { includeResultMetadata: meta });
if (endTimer)
endTimer();
return result;
}
@ -335,7 +367,10 @@ class MongoDB
filter._id = new ObjectId(filter._id);
this.#logger.debug(`Incoming push query for ${db}, with upsert ${upsert} and with parameters ${inspect(filter)} and data ${inspect(data)}`);
const endTimer = this.#queryHistogram?.startTimer({ type: 'push' });
const result = await this.#db.collection(db).updateOne(filter, { $push: data }, { upsert });
if (endTimer)
endTimer();
return result;
}
@ -350,7 +385,6 @@ class MongoDB
*/
random<T extends Document> (db: string, filter: Document = {}, amount = 1)
{
if (!this.#db)
throw new Error('MongoDB not connected');
if (typeof db !== 'string')
@ -365,7 +399,6 @@ class MongoDB
const cursor = this.#db.collection(db).aggregate<T>([{ $match: filter }, { $sample: { size: amount } }]);
return cursor.toArray();
}
stats (options = {})

View File

@ -1,5 +1,6 @@
import { ILogger, LoggerClientOptions } from "./Logger.js";
import { ILogger, LoggerClientOptions } from './Logger.js';
export interface IServer {
createLogger(obj: object, options?: Partial<LoggerClientOptions>): ILogger
registerMetric(metric: object): void
}