본문 바로가기
WIKI 기술 지식 베이스

Scheduler 서비스

원문 보기 위키 갱신

플러그인을 작성할 때 정해진 스케줄로 실행되거나, 백엔드 플러그인이 실행되는 인스턴스 전체에 분산되는 cron 작업과 비슷한 것을 실행하고 싶을 때가 있어요. 이를 위해 플러그인별로 범위가 지정된 태스크 스케줄러를 제공해서, 이런 태스크를 만들고 그 실행을 조율할 수 있게 해줘요.

출처: 문서

본문

플러그인을 작성할 때 정해진 스케줄로 실행되거나, 백엔드 플러그인이 실행되는 인스턴스 전체에 분산되는 cron 작업과 비슷한 것을 실행하고 싶을 때가 있어요. 이를 위해 플러그인별로 범위가 지정된 태스크 스케줄러를 제공해서, 이런 태스크를 만들고 그 실행을 조율할 수 있게 해줘요.

서비스 사용하기

다음 예시는 example 백엔드에서 scheduler 서비스를 가져와 주어진 간격으로 인스턴스 전체에서 실행되는 예약 태스크를 발행하는 방법을 보여줘요.

import {
  coreServices,
  createBackendPlugin,
} from '@backstage/backend-plugin-api';

createBackendPlugin({
  pluginId: 'example',
  register(env) {
    env.registerInit({
      deps: {
        scheduler: coreServices.scheduler,
      },
      async init({ scheduler }) {
        await scheduler.scheduleTask({
          frequency: { minutes: 10 },
          timeout: { seconds: 30 },
          id: 'ping-google',
          fn: async () => {
            await fetch('http://google.com/ping');
          },
        });
      },
    });
  },
});

REST API

스케줄러는 각 플러그인의 베이스 URL 위에 REST API를 노출하며, 이를 통해 해당 플러그인의 모든 태스크의 현재 상태를 검사하고 영향을 줄 수 있어요.

GET <pluginBaseURL>/.backstage/scheduler/v1/tasks

주어진 플러그인이 시작 시점에 등록한 모든 태스크와 그 현재 상태를 나열해요.

예를 들어 Catalog 플러그인의 모든 예약 태스크를 나열하려면:

curl 'https://<instance-name>/api/catalog/.backstage/scheduler/v1/tasks'

Backstage 데모 인스턴스에서 시도해볼 수 있어요.

curl 'https://demo.backstage.io/api/catalog/.backstage/scheduler/v1/tasks'

응답 모양은 다음과 같아요.

{
  "tasks": [
    {
      "taskId": "InternalOpenApiDocumentationProvider:refresh",
      "pluginId": "catalog",
      "scope": "global",
      "settings": {
        "version": 2,
        "cadence": "PT10S",
        "initialDelayDuration": "PT10S",
        "timeoutAfterDuration": "PT1M"
      },
      "taskState": {
        "status": "idle",
        "startsAt": "2025-04-11T20:35:13.418+02:00",
        "lastRunEndedAt": "2025-04-11T20:35:03.453+02:00"
      },
      "workerState": {
        "status": "initial-wait"
      }
    }
  ]
}

각 태스크는 다음과 같은 속성을 가져요.

Field Format Description
taskId string A unique (per plugin) ID for the task
pluginId string The plugin where the task is scheduled
scope string Either local (runs on each worker node with potential overlaps, similar to setInterval), or global (runs on one worker node at a time, without overlaps)
settings object Serialized form of the initial settings passed in when scheduling the task. The only completely fixed well known field is version; the others depend on what version is used
settings.version string Internal identifier of the format of the settings object. The format of this object can change completely for each version. This document describes version 2 specifically
settings.cadence string; ISO duration How often the task runs. Either the string manual (only runs when manually triggered), or an ISO duration string starting with the letter P, or a cron format string
settings.initialDelayDuration string; ISO duration How long workers wait at service startup before starting to look for work, to give the service some time to stabilize, as an ISO duration string (if configured)
settings.timeoutAfterDuration string; ISO duration How long after a task starts that it's considered timed out and available for retries
taskState object The current state of the task (see below for details)
workerState object The status of the worker responsible for task

taskState 모양은 태스크가 현재 실행 중인지 여부에 따라 달라져요. 실행 중일 때:

Field Format Optional Description
taskState.status string running
taskState.startedAt string; ISO timestamp When the current task run started
taskState.timesOutAt string; ISO timestamp When the current task run will time out if it does not finish before that
taskState.lastRunError string; JSON serialized error optional When the task last ran, if it threw an error, this field contains it
taskState.lastRunEndedAt string; ISO timestamp optional When the task last ran, it ended at this time

태스크가 유휴 상태일 때:

Field Format Optional Description
taskState.status string idle
taskState.startsAt string; ISO timestamp optional When the task is scheduled to run next; will not be set if the task uses manual scheduling
taskState.lastRunError string; JSON serialized error optional When the task last ran, if it threw an error, this field contains it
taskState.lastRunEndedAt string; ISO timestamp optional When the task last ran, it ended at this time

workerState 모양은 다음과 같아요.

Field Description
workerState.status The status of the worker responsible for task; either initial-wait (right at service startup), running (task is currently running), or idle (task is not running at the moment)

POST <pluginBaseURL>/.backstage/scheduler/v1/tasks/<taskId>/trigger

다음 예약된 시간 슬롯을 기다리지 않고, 주어진 태스크 ID를 즉시 실행하도록 예약해요.

예를 들어 특정 Catalog 태스크를 트리거하려면:

curl -X POST "https://<instance-name>/api/catalog/.backstage/scheduler/v1/tasks/InternalOpenApiDocumentationProvider:refresh/trigger"

작동하는 예시는 다음과 같아요.

curl -X POST "https://demo.backstage.io/api/catalog/.backstage/scheduler/v1/tasks/InternalOpenApiDocumentationProvider:refresh/trigger"

워커가 태스크가 완료되어 실제로 이를 수행할 시점임을 발견하기까지 추가로 작은 지연이 있을 수 있다는 점에 주의하세요. 이는 보통 1초 미만이지만 달라질 수 있어요.

요청에는 본문이 없어요.

응답은 다음과 같아요.

  • 성공하면 200 OK
  • 이 플러그인에 그런 등록된 태스크가 없으면 404 Not Found
  • 태스크가 이미 실행 중 상태였다면 409 Conflict

POST <pluginBaseURL>/.backstage/scheduler/v1/tasks/<taskId>/cancel

주어진 태스크 ID로 실행 중인 태스크를 취소해요.

<taskId>는 URL에서 단일 경로 세그먼트로 유지되도록 URL 인코딩되어야 한다는 점에 주의하세요(예: JavaScript에서 encodeURIComponent를 사용하거나 표준 퍼센트 인코딩).

예를 들어 특정 Catalog 태스크를 취소하려면:

curl -X POST "https://<instance-name>/api/catalog/.backstage/scheduler/v1/tasks/InternalOpenApiDocumentationProvider%3Arefresh/cancel"

작동하는 예시는 다음과 같아요.

curl -X POST "https://demo.backstage.io/api/catalog/.backstage/scheduler/v1/tasks/InternalOpenApiDocumentationProvider%3Arefresh/cancel"

워커가 태스크가 취소되었음을 발견하기까지 추가로 작은 지연이 있을 수 있다는 점에 주의하세요. 이는 몇 초까지 걸릴 수 있어요. 또한 태스크에 전달된 abort 신호에 제대로 반응하는 것은 태스크 구현의 몫이라는 점도 유의하세요.

요청에는 본문이 없어요.

응답은 다음과 같아요.

  • 성공하면 200 OK
  • 이 플러그인에 그런 등록된 태스크가 없으면 404 Not Found
  • 태스크가 실행 중 상태가 아니었다면 409 Conflict

테스트

@backstage/backend-test-utils 패키지는 scheduler 서비스의 모의 구현을 제공하는 mockServices.scheduler를 제공해요. 이 모의 구현은 기본적으로 startTestBackend에서 사용되며, 수동 실행이나 초기 지연으로 구성되지 않은 경우 시작 시점에 등록된 태스크를 즉시 실행해요.

테스트 중 더 많은 제어를 위해 전용 인스턴스를 사용할 수도 있어요.

it('should trigger a task', async () => {
  const scheduler = mockServices.scheduler();
  const { server } = await startTestBackend({
    features: [scheduler.factory()],
  });
  await scheduler.triggerTask('some-task-id');
  // Next verify that the plugin state is updated accordingly
  // e.g. by calling the API or verifying database state
});

더 알아보기 (Learn more)