From 65304798de612b66d40b9e3e36dea79d6bae4895 Mon Sep 17 00:00:00 2001 From: Lars Strojny Date: Thu, 24 Nov 2022 19:35:02 +0100 Subject: [PATCH] Memoized renderering (#37) Pre-render metrics on discovery once and then reuse rendered response. --- src/adapters/http/api.ts | 2 +- src/platform.ts | 2 +- src/prometheus.ts | 23 ++++++++++++----------- tests/adapters/http/fastify.test.ts | 10 +++++----- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/adapters/http/api.ts b/src/adapters/http/api.ts index 2b9abfb..1424b93 100644 --- a/src/adapters/http/api.ts +++ b/src/adapters/http/api.ts @@ -28,5 +28,5 @@ export interface HttpServer { onMetrics(): HttpResponse onNotFound(): HttpResponse onError(error: Error): HttpResponse - updateMetrics(metrics: Metric[]): void + onMetricsDiscovery(metrics: Metric[]): void } diff --git a/src/platform.ts b/src/platform.ts index 0087638..4bc6b06 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -48,7 +48,7 @@ export class PrometheusExporterPlatform implements IndependentPlatformPlugin { discover({ log: this.log, config: this.config }) .then((devices) => { const metrics = aggregate(devices, new Date()) - this.httpServer.updateMetrics(metrics) + this.httpServer.onMetricsDiscovery(metrics) this.log.debug('HAP discovery completed, %d metrics discovered', metrics.length) this.startHapDiscovery() }) diff --git a/src/prometheus.ts b/src/prometheus.ts index 60d9d6c..7ffdf7a 100644 --- a/src/prometheus.ts +++ b/src/prometheus.ts @@ -44,13 +44,17 @@ function headers(contentType: string, headers: Record = {}): Rec } export class PrometheusServer implements HttpServer { - private metricsInitialized = false - private metrics: Metric[] = [] + private metricsDiscovered = false + private metricsResponse = '' - constructor(public readonly config: HttpConfig, public readonly log: Logger | undefined = undefined) {} + constructor( + public readonly config: HttpConfig, + public readonly log: Logger | undefined = undefined, + private readonly renderer: MetricsRenderer = new MetricsRenderer(config.prefix), + ) {} onRequest(): HttpResponse | undefined { - if (!this.metricsInitialized) { + if (!this.metricsDiscovered) { return { statusCode: 503, headers: headers(textContentType, { 'Retry-After': String(retryAfterWhileDiscovery) }), @@ -60,13 +64,10 @@ export class PrometheusServer implements HttpServer { } onMetrics(): HttpResponse { - const renderer = new MetricsRenderer(this.config.prefix) - const metrics = this.metrics.map((metric) => renderer.render(metric)).join('\n') - return { statusCode: 200, headers: headers(metricsContentType), - body: metrics, + body: this.metricsResponse, } } @@ -86,9 +87,9 @@ export class PrometheusServer implements HttpServer { } } - updateMetrics(metrics: Metric[]): void { - this.metrics = metrics - this.metricsInitialized = true + onMetricsDiscovery(metrics: Metric[]): void { + this.metricsResponse = metrics.map((metric) => this.renderer.render(metric)).join('\n') + this.metricsDiscovered = true } } diff --git a/tests/adapters/http/fastify.test.ts b/tests/adapters/http/fastify.test.ts index 470a6e1..dddaaa8 100644 --- a/tests/adapters/http/fastify.test.ts +++ b/tests/adapters/http/fastify.test.ts @@ -46,7 +46,7 @@ describe('Fastify HTTP adapter', () => { test('Serves 404 on / when metrics are available', () => { const testServer = createTestServer() - testServer.prometheus.updateMetrics([]) + testServer.prometheus.onMetricsDiscovery([]) return request(testServer.http) .get('/') @@ -58,7 +58,7 @@ describe('Fastify HTTP adapter', () => { test('Serves metrics', () => { const testServer = createTestServer() const timestamp = new Date('2020-01-01 00:00:00 UTC') - testServer.prometheus.updateMetrics([ + testServer.prometheus.onMetricsDiscovery([ new Metric('metric', 0.1, timestamp, { name: 'metric' }), new Metric('total_something', 100, timestamp, { name: 'counter' }), ]) @@ -79,7 +79,7 @@ describe('Fastify HTTP adapter', () => { test('Basic auth denied without user', () => { const testServer = createTestServerWithBasicAuth({ joanna: secretAsBcrypt }) - testServer.prometheus.updateMetrics([]) + testServer.prometheus.onMetricsDiscovery([]) return request(testServer.http) .get('/metrics') @@ -90,7 +90,7 @@ describe('Fastify HTTP adapter', () => { test('Basic auth denied with incorrect user', () => { const testServer = createTestServerWithBasicAuth({ joanna: secretAsBcrypt }) - testServer.prometheus.updateMetrics([]) + testServer.prometheus.onMetricsDiscovery([]) return request(testServer.http) .get('/metrics') @@ -103,7 +103,7 @@ describe('Fastify HTTP adapter', () => { test('Basic auth grants access', () => { const testServer = createTestServerWithBasicAuth({ joanna: secretAsBcrypt }) const timestamp = new Date('2020-01-01 00:00:00 UTC') - testServer.prometheus.updateMetrics([ + testServer.prometheus.onMetricsDiscovery([ new Metric('metric', 0.1, timestamp, { name: 'metric' }), new Metric('total_something', 100, timestamp, { name: 'counter' }), ])