1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-01-06 03:46:47 +01:00

refactor(server): metric repo (#8278)

* refactor

* redundant `implements`

* simplify

* remove `enabled`
This commit is contained in:
Mert 2024-03-25 19:15:11 -04:00 committed by GitHub
parent c56c04a82b
commit c45e28ab53
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 79 additions and 34 deletions

View file

@ -1,13 +1,21 @@
import { MetricOptions } from '@opentelemetry/api'; import { MetricOptions } from '@opentelemetry/api';
export interface CustomMetricOptions extends MetricOptions {
enabled?: boolean;
}
export const IMetricRepository = 'IMetricRepository'; export const IMetricRepository = 'IMetricRepository';
export interface IMetricRepository { export interface MetricGroupOptions {
addToCounter(name: string, value: number, options?: CustomMetricOptions): void; enabled: boolean;
updateGauge(name: string, value: number, options?: CustomMetricOptions): void; }
updateHistogram(name: string, value: number, options?: CustomMetricOptions): void;
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;
} }

View file

@ -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 { 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 { class MetricGroupRepository implements IMetricGroupRepository {
constructor(@Inject(MetricService) private readonly metricService: MetricService) {} private enabled = false;
constructor(private readonly metricService: MetricService) {}
addToCounter(name: string, value: number, options?: CustomMetricOptions): void { addToCounter(name: string, value: number, options?: MetricOptions): void {
if (options?.enabled === false) { if (this.enabled) {
return; this.metricService.getCounter(name, options).add(value);
} }
this.metricService.getCounter(name, options).add(value);
} }
updateGauge(name: string, value: number, options?: CustomMetricOptions): void { addToGauge(name: string, value: number, options?: MetricOptions): void {
if (options?.enabled === false) { if (this.enabled) {
return; this.metricService.getUpDownCounter(name, options).add(value);
} }
this.metricService.getUpDownCounter(name, options).add(value);
} }
updateHistogram(name: string, value: number, options?: CustomMetricOptions): void { addToHistogram(name: string, value: number, options?: MetricOptions): void {
if (options?.enabled === false) { if (this.enabled) {
return; 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 });
} }
} }

View file

@ -20,7 +20,6 @@ import {
import { IMetricRepository } from 'src/interfaces/metric.interface'; import { IMetricRepository } from 'src/interfaces/metric.interface';
import { IPersonRepository } from 'src/interfaces/person.interface'; import { IPersonRepository } from 'src/interfaces/person.interface';
import { ISystemConfigRepository } from 'src/interfaces/system-config.interface'; import { ISystemConfigRepository } from 'src/interfaces/system-config.interface';
import { jobMetrics } from 'src/utils/instrumentation';
import { ImmichLogger } from 'src/utils/logger'; import { ImmichLogger } from 'src/utils/logger';
@Injectable() @Injectable()
@ -96,7 +95,7 @@ export class JobService {
throw new BadRequestException(`Job is already running`); 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) { switch (name) {
case QueueName.VIDEO_CONVERSION: { case QueueName.VIDEO_CONVERSION: {
@ -163,20 +162,20 @@ export class JobService {
const { name, data } = item; const { name, data } = item;
const queueMetric = `immich.queues.${snakeCase(queueName)}.active`; const queueMetric = `immich.queues.${snakeCase(queueName)}.active`;
this.metricRepository.updateGauge(queueMetric, 1, { enabled: jobMetrics }); this.metricRepository.jobs.addToGauge(queueMetric, 1);
try { try {
const handler = jobHandlers[name]; const handler = jobHandlers[name];
const status = await handler(data); const status = await handler(data);
const jobMetric = `immich.jobs.${name.replaceAll('-', '_')}.${status}`; 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) { if (status === JobStatus.SUCCESS || status == JobStatus.SKIPPED) {
await this.onDone(item); await this.onDone(item);
} }
} catch (error: Error | any) { } catch (error: Error | any) {
this.logger.error(`Unable to run job handler (${queueName}/${name}): ${error}`, error?.stack, data); this.logger.error(`Unable to run job handler (${queueName}/${name}): ${error}`, error?.stack, data);
} finally { } finally {
this.metricRepository.updateGauge(queueMetric, -1, { enabled: jobMetrics }); this.metricRepository.jobs.addToGauge(queueMetric, -1);
} }
}); });
} }

View file

@ -2,8 +2,29 @@ import { IMetricRepository } from 'src/interfaces/metric.interface';
export const newMetricRepositoryMock = (): jest.Mocked<IMetricRepository> => { export const newMetricRepositoryMock = (): jest.Mocked<IMetricRepository> => {
return { return {
addToCounter: jest.fn(), api: {
updateGauge: jest.fn(), addToCounter: jest.fn(),
updateHistogram: 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(),
},
}; };
}; };