From c45e28ab5375eeb10a57c1610e2f7de3472ba4c5 Mon Sep 17 00:00:00 2001 From: Mert <101130780+mertalev@users.noreply.github.com> Date: Mon, 25 Mar 2024 19:15:11 -0400 Subject: [PATCH] refactor(server): metric repo (#8278) * refactor * redundant `implements` * simplify * remove `enabled` --- server/src/interfaces/metric.interface.ts | 24 ++++++--- server/src/repositories/metric.repository.ts | 53 ++++++++++++------- server/src/services/job.service.ts | 9 ++-- .../repositories/metric.repository.mock.ts | 27 ++++++++-- 4 files changed, 79 insertions(+), 34 deletions(-) diff --git a/server/src/interfaces/metric.interface.ts b/server/src/interfaces/metric.interface.ts index cdabd57910..a87a849833 100644 --- a/server/src/interfaces/metric.interface.ts +++ b/server/src/interfaces/metric.interface.ts @@ -1,13 +1,21 @@ import { MetricOptions } from '@opentelemetry/api'; -export interface CustomMetricOptions extends MetricOptions { - enabled?: boolean; -} - export const IMetricRepository = 'IMetricRepository'; -export interface IMetricRepository { - addToCounter(name: string, value: number, options?: CustomMetricOptions): void; - updateGauge(name: string, value: number, options?: CustomMetricOptions): void; - updateHistogram(name: string, value: number, options?: CustomMetricOptions): void; +export interface MetricGroupOptions { + enabled: boolean; +} + +export interface IMetricGroupRepository { + addToCounter(name: string, value: number, options?: MetricOptions): void; + addToGauge(name: string, value: number, options?: MetricOptions): void; + addToHistogram(name: string, value: number, options?: MetricOptions): void; + configure(options: MetricGroupOptions): this; +} + +export interface IMetricRepository { + api: IMetricGroupRepository; + host: IMetricGroupRepository; + jobs: IMetricGroupRepository; + repo: IMetricGroupRepository; } diff --git a/server/src/repositories/metric.repository.ts b/server/src/repositories/metric.repository.ts index 87757d6541..c6eb953acf 100644 --- a/server/src/repositories/metric.repository.ts +++ b/server/src/repositories/metric.repository.ts @@ -1,31 +1,48 @@ -import { Inject } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; +import { MetricOptions } from '@opentelemetry/api'; import { MetricService } from 'nestjs-otel'; -import { CustomMetricOptions, IMetricRepository } from 'src/interfaces/metric.interface'; +import { IMetricGroupRepository, IMetricRepository, MetricGroupOptions } from 'src/interfaces/metric.interface'; +import { apiMetrics, hostMetrics, jobMetrics, repoMetrics } from 'src/utils/instrumentation'; -export class MetricRepository implements IMetricRepository { - constructor(@Inject(MetricService) private readonly metricService: MetricService) {} +class MetricGroupRepository implements IMetricGroupRepository { + private enabled = false; + constructor(private readonly metricService: MetricService) {} - addToCounter(name: string, value: number, options?: CustomMetricOptions): void { - if (options?.enabled === false) { - return; + addToCounter(name: string, value: number, options?: MetricOptions): void { + if (this.enabled) { + this.metricService.getCounter(name, options).add(value); } - - this.metricService.getCounter(name, options).add(value); } - updateGauge(name: string, value: number, options?: CustomMetricOptions): void { - if (options?.enabled === false) { - return; + addToGauge(name: string, value: number, options?: MetricOptions): void { + if (this.enabled) { + this.metricService.getUpDownCounter(name, options).add(value); } - - this.metricService.getUpDownCounter(name, options).add(value); } - updateHistogram(name: string, value: number, options?: CustomMetricOptions): void { - if (options?.enabled === false) { - return; + addToHistogram(name: string, value: number, options?: MetricOptions): void { + if (this.enabled) { + this.metricService.getHistogram(name, options).record(value); } + } - this.metricService.getHistogram(name, options).record(value); + configure(options: MetricGroupOptions): this { + this.enabled = options.enabled; + return this; + } +} + +@Injectable() +export class MetricRepository implements IMetricRepository { + api: MetricGroupRepository; + host: MetricGroupRepository; + jobs: MetricGroupRepository; + repo: MetricGroupRepository; + + constructor(metricService: MetricService) { + this.api = new MetricGroupRepository(metricService).configure({ enabled: apiMetrics }); + this.host = new MetricGroupRepository(metricService).configure({ enabled: hostMetrics }); + this.jobs = new MetricGroupRepository(metricService).configure({ enabled: jobMetrics }); + this.repo = new MetricGroupRepository(metricService).configure({ enabled: repoMetrics }); } } diff --git a/server/src/services/job.service.ts b/server/src/services/job.service.ts index d9291b7268..c03b7c7bc2 100644 --- a/server/src/services/job.service.ts +++ b/server/src/services/job.service.ts @@ -20,7 +20,6 @@ import { import { IMetricRepository } from 'src/interfaces/metric.interface'; import { IPersonRepository } from 'src/interfaces/person.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; -import { jobMetrics } from 'src/utils/instrumentation'; import { ImmichLogger } from 'src/utils/logger'; @Injectable() @@ -96,7 +95,7 @@ export class JobService { throw new BadRequestException(`Job is already running`); } - this.metricRepository.addToCounter(`immich.queues.${snakeCase(name)}.started`, 1), { enabled: jobMetrics }; + this.metricRepository.jobs.addToCounter(`immich.queues.${snakeCase(name)}.started`, 1); switch (name) { case QueueName.VIDEO_CONVERSION: { @@ -163,20 +162,20 @@ export class JobService { const { name, data } = item; const queueMetric = `immich.queues.${snakeCase(queueName)}.active`; - this.metricRepository.updateGauge(queueMetric, 1, { enabled: jobMetrics }); + this.metricRepository.jobs.addToGauge(queueMetric, 1); try { const handler = jobHandlers[name]; const status = await handler(data); const jobMetric = `immich.jobs.${name.replaceAll('-', '_')}.${status}`; - this.metricRepository.addToCounter(jobMetric, 1, { enabled: jobMetrics }); + this.metricRepository.jobs.addToCounter(jobMetric, 1); if (status === JobStatus.SUCCESS || status == JobStatus.SKIPPED) { await this.onDone(item); } } catch (error: Error | any) { this.logger.error(`Unable to run job handler (${queueName}/${name}): ${error}`, error?.stack, data); } finally { - this.metricRepository.updateGauge(queueMetric, -1, { enabled: jobMetrics }); + this.metricRepository.jobs.addToGauge(queueMetric, -1); } }); } diff --git a/server/test/repositories/metric.repository.mock.ts b/server/test/repositories/metric.repository.mock.ts index 87f5c399df..383845d345 100644 --- a/server/test/repositories/metric.repository.mock.ts +++ b/server/test/repositories/metric.repository.mock.ts @@ -2,8 +2,29 @@ import { IMetricRepository } from 'src/interfaces/metric.interface'; export const newMetricRepositoryMock = (): jest.Mocked => { return { - addToCounter: jest.fn(), - updateGauge: jest.fn(), - updateHistogram: jest.fn(), + api: { + addToCounter: jest.fn(), + addToGauge: jest.fn(), + addToHistogram: jest.fn(), + configure: jest.fn(), + }, + host: { + addToCounter: jest.fn(), + addToGauge: jest.fn(), + addToHistogram: jest.fn(), + configure: jest.fn(), + }, + jobs: { + addToCounter: jest.fn(), + addToGauge: jest.fn(), + addToHistogram: jest.fn(), + configure: jest.fn(), + }, + repo: { + addToCounter: jest.fn(), + addToGauge: jest.fn(), + addToHistogram: jest.fn(), + configure: jest.fn(), + }, }; };