Optional prom-client peer dependency
This commit is contained in:
parent
ee67d81e94
commit
460ded7641
12
package.json
12
package.json
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import fs from 'node:fs';
|
||||
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' }));
|
@ -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 ()
|
||||
@ -386,6 +407,7 @@ class MariaDB
|
||||
{
|
||||
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)}`);
|
||||
});
|
||||
|
@ -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 = {})
|
||||
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue
Block a user