{"version":3,"file":"dbMetrics.cjs","sources":["../../../../../packages/engine-http/src/prometheus/dbMetrics.ts"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/triple-slash-reference\n/// <reference path=\"../../types/prom-client/index.d.ts\" />\nimport prom from 'prom-client'\nimport { Connection, EventManager, PoolStats, poolStatsDescription } from '@contember/database'\nimport { CustomMetric } from './CustomMetric'\n\n\nconst labelNames = ['contember_project' as const, 'contember_module' as const, 'contember_project_group' as const, 'database_instance']\ntype LabelNames = typeof labelNames[number]\ntype Labels = { [K in LabelNames]: string }\ntype DatabaseMetricsEntry = {\n\tconnection: Connection.PoolStatusProvider & Connection.Queryable\n\tlabels: Labels\n}\ntype Unregistrar = () => void\ntype DatabaseMetricsRegistrar = (entry: DatabaseMetricsEntry) => Unregistrar\n\nexport const createDbMetricsRegistrar = (registry: prom.Registry): DatabaseMetricsRegistrar => {\n\tconst dbPoolCollectorInner = createDbPoolMetricsCollector(registry)\n\tconst entries = new Set<DatabaseMetricsEntry>()\n\tconst collector = () => {\n\t\tdbPoolCollectorInner(entries)\n\t}\n\tregistry.registerCollector(collector)\n\tconst sqlMetricsRegistrar = createSqlMetricsRegistrar(registry)\n\treturn entry => {\n\t\tentries.add(entry)\n\t\tconst sqlMetricsUnregistrar = sqlMetricsRegistrar(entry.connection, entry.labels)\n\t\treturn () => {\n\t\t\tentries.delete(entry)\n\t\t\tsqlMetricsUnregistrar()\n\t\t}\n\t}\n}\n\nconst createDbPoolMetricsCollector = (registry: prom.Registry) => {\n\n\tconst descriptionSuffix = 'Dimensions: contember_module (tenant or content; system is not used since it uses the same connection), contember_project_group, contember_project for contember_module=content, database_instance (single, primary, replica).'\n\tconst totalCount = new prom.Gauge({\n\t\tregisters: [registry],\n\t\tname: `contember_db_pool_total_count`,\n\t\thelp: `The total number of clients existing within the pool: ${descriptionSuffix}`,\n\t\tlabelNames,\n\t})\n\tconst idleCount = new prom.Gauge({\n\t\tregisters: [registry],\n\t\tname: `contember_db_pool_idle_count`,\n\t\thelp: `The number of clients which are not checked out but are currently idle in the pool. ${descriptionSuffix}`,\n\t\tlabelNames,\n\t})\n\tconst waitingCount = new prom.Gauge({\n\t\tregisters: [registry],\n\t\tname: `contember_db_pool_waiting_count`,\n\t\thelp: `The number of queued requests waiting on a client when all clients are checked out. It can be helpful to monitor this number to see if you need to adjust the size of the pool. ${descriptionSuffix}`,\n\t\tlabelNames,\n\t})\n\tconst maxCount = new prom.Gauge({\n\t\tregisters: [registry],\n\t\tname: `contember_db_pool_max_count`,\n\t\thelp: `Maximum number of clients the pool should contain. ${descriptionSuffix}`,\n\t\tlabelNames,\n\t})\n\tconst connectingCount = new prom.Gauge({\n\t\tregisters: [registry],\n\t\tname: `contember_db_pool_connecting_count`,\n\t\thelp: `Current value of clients establishing connection. ${descriptionSuffix}`,\n\t\tlabelNames,\n\t})\n\tconst activeCount = new prom.Gauge({\n\t\tregisters: [registry],\n\t\tname: `contember_db_pool_active_count`,\n\t\thelp: `Number of connections that are checked out. ${descriptionSuffix}`,\n\t\tlabelNames,\n\t})\n\tconst statCounters: {[K in keyof PoolStats]: CustomMetric<LabelNames>} = {} as any\n\tfor (const [key, description] of Object.entries(poolStatsDescription)) {\n\t\tconst customMetric = new CustomMetric<LabelNames>({\n\t\t\tname: `contember_db_pool_${key}`,\n\t\t\thelp: `${description} ${descriptionSuffix}`,\n\t\t\ttype: 'counter',\n\t\t\tlabelNames,\n\t\t})\n\t\tstatCounters[key as keyof PoolStats] = customMetric\n\t\tregistry.registerMetric(customMetric as unknown as prom.Counter<LabelNames>)\n\t}\n\n\treturn (entries: Set<DatabaseMetricsEntry>) => {\n\t\tfor (const counter of Object.values(statCounters)) {\n\t\t\tcounter.reset()\n\t\t}\n\t\tfor (const entry of entries) {\n\t\t\tconst db = entry.connection\n\t\t\tconst labels = entry.labels\n\t\t\tconst poolInfo = db.getPoolStatus()\n\t\t\tif (!poolInfo) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttotalCount.set(labels, poolInfo.active + poolInfo.idle + poolInfo.connecting)\n\t\t\tidleCount.set(labels, poolInfo.idle)\n\t\t\twaitingCount.set(labels, poolInfo.pending)\n\t\t\tmaxCount.set(labels, poolInfo.max)\n\t\t\tconnectingCount.set(labels, poolInfo.connecting)\n\t\t\tactiveCount.set(labels, poolInfo.active)\n\n\t\t\tfor (const [key, value] of Object.entries(poolInfo.stats)) {\n\t\t\t\tstatCounters[key as keyof PoolStats].add(labels, value)\n\t\t\t}\n\t\t}\n\t}\n}\ntype SqlMetricsRegistrar = (\n\tconnection: Connection.Queryable,\n\tlabels: Labels,\n) => Unregistrar\nconst createSqlMetricsRegistrar = (registry: prom.Registry): SqlMetricsRegistrar => {\n\tconst sqlDuration = new prom.Histogram({\n\t\tname: 'contember_sql_duration_ms',\n\t\thelp: 'Executed SQL queries by contember_module (system, tenant, content or unknown) and contember_project (or \"unknown\" for unknown project)',\n\t\tregisters: [registry],\n\t\tlabelNames,\n\t\tbuckets: [1, 2, 5, 10, 25, 50, 100, 250, 500, 1000],\n\t})\n\tconst sqlErrorRate = new prom.Counter({\n\t\tname: 'contember_sql_error_count',\n\t\thelp: 'Failed SQL queries by contember_module (system, tenant, content or unknown) and contember_project (or \"unknown\" for unknown project)',\n\t\tlabelNames,\n\t\tregisters: [registry],\n\t})\n\n\treturn (connection, labels) => {\n\t\tconst queryEndCallback: EventManager.ListenerTypes[EventManager.Event.queryEnd] = ({ meta }, { timing }) => {\n\t\t\tsqlDuration.observe(\n\t\t\t\t{\n\t\t\t\t\t...labels,\n\t\t\t\t\tcontember_module: meta.module || labels.contember_module,\n\t\t\t\t},\n\t\t\t\ttiming ? Math.round(timing.selfDuration / 1000) : 0,\n\t\t\t)\n\t\t}\n\t\tconst queryErrorCallback: EventManager.ListenerTypes[EventManager.Event.queryError] = ({ meta }) => {\n\t\t\tsqlErrorRate.inc({\n\t\t\t\t...labels,\n\t\t\t\tcontember_module: meta.module || labels.contember_module,\n\t\t\t})\n\t\t}\n\t\tconnection.eventManager.on(EventManager.Event.queryEnd, queryEndCallback)\n\t\tconnection.eventManager.on(EventManager.Event.queryError, queryErrorCallback)\n\t\treturn () => {\n\t\t\tconnection.eventManager.removeListener(EventManager.Event.queryEnd, queryEndCallback)\n\t\t\tconnection.eventManager.removeListener(EventManager.Event.queryError, queryErrorCallback)\n\t\t}\n\t}\n}\n"],"names":["poolStatsDescription","CustomMetric","EventManager"],"mappings":"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"}