From 0e7d8f9091ad9127291cc0e495a19eeaa1503039 Mon Sep 17 00:00:00 2001 From: Kurt King Date: Sat, 28 Feb 2026 07:14:47 -0700 Subject: [PATCH] Refactor scheduler to use metrics service (#32992) * Migrate scheduler metrics to MetricsService Signed-off-by: Kurt King * Add missing changeset Signed-off-by: Kurt King * Update API report Signed-off-by: Kurt King * Release as minor with breaking change verbiage Signed-off-by: Kurt King * Apply suggestion from @aramissennyeydd Signed-off-by: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com> * fix prettier Signed-off-by: aramissennyeydd --------- Signed-off-by: Kurt King Signed-off-by: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com> Signed-off-by: aramissennyeydd Co-authored-by: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com> Co-authored-by: aramissennyeydd --- .changeset/deep-files-clap.md | 5 +++ .../backend-defaults/report-scheduler.api.md | 2 ++ .../lib/DefaultSchedulerService.test.ts | 4 +++ .../scheduler/lib/DefaultSchedulerService.ts | 3 ++ .../lib/PluginTaskSchedulerImpl.test.ts | 2 ++ .../scheduler/lib/PluginTaskSchedulerImpl.ts | 35 ++++++++++++------- .../scheduler/schedulerServiceFactory.ts | 4 +++ 7 files changed, 42 insertions(+), 13 deletions(-) create mode 100644 .changeset/deep-files-clap.md diff --git a/.changeset/deep-files-clap.md b/.changeset/deep-files-clap.md new file mode 100644 index 0000000000..a3ab54eb63 --- /dev/null +++ b/.changeset/deep-files-clap.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-defaults': minor +--- + +The scheduler service now uses the metrics service to create metrics, providing plugin-scoped attribution. diff --git a/packages/backend-defaults/report-scheduler.api.md b/packages/backend-defaults/report-scheduler.api.md index e0a85710a5..44a503d18c 100644 --- a/packages/backend-defaults/report-scheduler.api.md +++ b/packages/backend-defaults/report-scheduler.api.md @@ -6,6 +6,7 @@ import { DatabaseService } from '@backstage/backend-plugin-api'; import { HttpRouterService } from '@backstage/backend-plugin-api'; import { LoggerService } from '@backstage/backend-plugin-api'; +import { MetricsService } from '@backstage/backend-plugin-api/alpha'; import { PluginMetadataService } from '@backstage/backend-plugin-api'; import { RootLifecycleService } from '@backstage/backend-plugin-api'; import { SchedulerService } from '@backstage/backend-plugin-api'; @@ -17,6 +18,7 @@ export class DefaultSchedulerService { static create(options: { database: DatabaseService; logger: LoggerService; + metrics: MetricsService; rootLifecycle: RootLifecycleService; httpRouter: HttpRouterService; pluginMetadata: PluginMetadataService; diff --git a/packages/backend-defaults/src/entrypoints/scheduler/lib/DefaultSchedulerService.test.ts b/packages/backend-defaults/src/entrypoints/scheduler/lib/DefaultSchedulerService.test.ts index 61f9bd0639..3de69ba566 100644 --- a/packages/backend-defaults/src/entrypoints/scheduler/lib/DefaultSchedulerService.test.ts +++ b/packages/backend-defaults/src/entrypoints/scheduler/lib/DefaultSchedulerService.test.ts @@ -20,6 +20,7 @@ import waitForExpect from 'wait-for-expect'; import { DefaultSchedulerService } from './DefaultSchedulerService'; import { createTestScopedSignal } from './__testUtils__/createTestScopedSignal'; import { PluginMetadataService } from '@backstage/backend-plugin-api'; +import { metricsServiceMock } from '@backstage/backend-test-utils/alpha'; jest.setTimeout(60_000); @@ -32,6 +33,7 @@ describe('TaskScheduler', () => { getId: () => 'test', } satisfies PluginMetadataService; const testScopedSignal = createTestScopedSignal(); + const metrics = metricsServiceMock.mock(); it.each(databases.eachSupportedId())( 'can return a working v1 plugin impl, %p', @@ -42,6 +44,7 @@ describe('TaskScheduler', () => { const manager = DefaultSchedulerService.create({ database, logger, + metrics, rootLifecycle, httpRouter, pluginMetadata, @@ -71,6 +74,7 @@ describe('TaskScheduler', () => { const manager = DefaultSchedulerService.create({ database, logger, + metrics, rootLifecycle, httpRouter, pluginMetadata, diff --git a/packages/backend-defaults/src/entrypoints/scheduler/lib/DefaultSchedulerService.ts b/packages/backend-defaults/src/entrypoints/scheduler/lib/DefaultSchedulerService.ts index 0a7b9fbb87..07531cffc8 100644 --- a/packages/backend-defaults/src/entrypoints/scheduler/lib/DefaultSchedulerService.ts +++ b/packages/backend-defaults/src/entrypoints/scheduler/lib/DefaultSchedulerService.ts @@ -27,6 +27,7 @@ import { Duration } from 'luxon'; import { migrateBackendTasks } from '../database/migrateBackendTasks'; import { PluginTaskSchedulerImpl } from './PluginTaskSchedulerImpl'; import { PluginTaskSchedulerJanitor } from './PluginTaskSchedulerJanitor'; +import { MetricsService } from '@backstage/backend-plugin-api/alpha'; /** * Default implementation of the task scheduler service. @@ -37,6 +38,7 @@ export class DefaultSchedulerService { static create(options: { database: DatabaseService; logger: LoggerService; + metrics: MetricsService; rootLifecycle: RootLifecycleService; httpRouter: HttpRouterService; pluginMetadata: PluginMetadataService; @@ -67,6 +69,7 @@ export class DefaultSchedulerService { options.pluginMetadata.getId(), databaseFactory, options.logger, + options.metrics, options.rootLifecycle, ); diff --git a/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.test.ts b/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.test.ts index 5872ae793e..2148148c9e 100644 --- a/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.test.ts +++ b/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.test.ts @@ -27,6 +27,7 @@ import { parseDuration, } from './PluginTaskSchedulerImpl'; import { createDeferred } from '@backstage/types'; +import { metricsServiceMock } from '@backstage/backend-test-utils/alpha'; jest.setTimeout(60_000); @@ -56,6 +57,7 @@ describe('PluginTaskManagerImpl', () => { 'myplugin', async () => knex, mockServices.logger.mock(), + metricsServiceMock.mock(), { addShutdownHook, addBeforeShutdownHook: jest.fn(), diff --git a/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.ts b/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.ts index 90f5d08e4a..e711744123 100644 --- a/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.ts +++ b/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.ts @@ -24,7 +24,13 @@ import { SchedulerServiceTaskRunner, SchedulerServiceTaskScheduleDefinition, } from '@backstage/backend-plugin-api'; -import { Counter, Histogram, Gauge, metrics, trace } from '@opentelemetry/api'; +import { trace } from '@opentelemetry/api'; +import { + MetricsService, + MetricsServiceCounter, + MetricsServiceGauge, + MetricsServiceHistogram, +} from '@backstage/backend-plugin-api/alpha'; import { Knex } from 'knex'; import { Duration } from 'luxon'; import express from 'express'; @@ -45,10 +51,10 @@ export class PluginTaskSchedulerImpl implements SchedulerService { private readonly allScheduledTasks: SchedulerServiceTaskDescriptor[] = []; private readonly shutdownInitiated: Promise; - private readonly counter: Counter; - private readonly duration: Histogram; - private readonly lastStarted: Gauge; - private readonly lastCompleted: Gauge; + private readonly counter: MetricsServiceCounter; + private readonly duration: MetricsServiceHistogram; + private readonly lastStarted: MetricsServiceGauge; + private readonly lastCompleted: MetricsServiceGauge; private readonly pluginId: string; private readonly databaseFactory: () => Promise; @@ -58,24 +64,27 @@ export class PluginTaskSchedulerImpl implements SchedulerService { pluginId: string, databaseFactory: () => Promise, logger: LoggerService, + metrics: MetricsService, rootLifecycle: RootLifecycleService, ) { this.pluginId = pluginId; this.databaseFactory = databaseFactory; this.logger = logger; - const meter = metrics.getMeter('default'); - this.counter = meter.createCounter('backend_tasks.task.runs.count', { + this.counter = metrics.createCounter('backend_tasks.task.runs.count', { description: 'Total number of times a task has been run', }); - this.duration = meter.createHistogram('backend_tasks.task.runs.duration', { - description: 'Histogram of task run durations', - unit: 'seconds', - }); - this.lastStarted = meter.createGauge('backend_tasks.task.runs.started', { + this.duration = metrics.createHistogram( + 'backend_tasks.task.runs.duration', + { + description: 'Histogram of task run durations', + unit: 'seconds', + }, + ); + this.lastStarted = metrics.createGauge('backend_tasks.task.runs.started', { description: 'Epoch timestamp seconds when the task was last started', unit: 'seconds', }); - this.lastCompleted = meter.createGauge( + this.lastCompleted = metrics.createGauge( 'backend_tasks.task.runs.completed', { description: 'Epoch timestamp seconds when the task was last completed', diff --git a/packages/backend-defaults/src/entrypoints/scheduler/schedulerServiceFactory.ts b/packages/backend-defaults/src/entrypoints/scheduler/schedulerServiceFactory.ts index 186e5f6940..8aacdc0005 100644 --- a/packages/backend-defaults/src/entrypoints/scheduler/schedulerServiceFactory.ts +++ b/packages/backend-defaults/src/entrypoints/scheduler/schedulerServiceFactory.ts @@ -18,6 +18,7 @@ import { coreServices, createServiceFactory, } from '@backstage/backend-plugin-api'; +import { metricsServiceRef } from '@backstage/backend-plugin-api/alpha'; import { DefaultSchedulerService } from './lib/DefaultSchedulerService'; /** @@ -37,6 +38,7 @@ export const schedulerServiceFactory = createServiceFactory({ rootLifecycle: coreServices.rootLifecycle, httpRouter: coreServices.httpRouter, pluginMetadata: coreServices.pluginMetadata, + metrics: metricsServiceRef, }, async factory({ database, @@ -44,6 +46,7 @@ export const schedulerServiceFactory = createServiceFactory({ rootLifecycle, httpRouter, pluginMetadata, + metrics, }) { return DefaultSchedulerService.create({ database, @@ -51,6 +54,7 @@ export const schedulerServiceFactory = createServiceFactory({ rootLifecycle, httpRouter, pluginMetadata, + metrics, }); }, });