콘텐츠로 이동

@gj-kit/nest-operations-jobs

인증 없는 trigger, 그리고 잡을 두 번 돌게 만드는 설정은 scheduler의 첫 호출이 아니라 컴파일과 부팅에서 걸립니다.

  • npm 0.1.2
  • unit test 230개 이상
  • @ts-expect-error 가드 11개
  • store 계약 case 13개
  • 런타임 의존성 0

직접 만든 cron endpoint의 장애는 대개 모든 대시보드가 초록인 채로 옵니다. claim에서 unique 위반을 통째로 삼키면 영구히 막힌 잡이 SKIPPED/200 스트림으로 둔갑하고, stale 판정 예산을 heartbeat 주기 아래로 내리면 건강한 실행이 다음 trigger에게 reap 대상으로 보여 두 번째 본문이 시작됩니다. 그 사이 trigger route는 짧은 shared secret을 ===로 비교한 채 배포되고, 저장된 실행 기록이 반환된 status와 어긋나도 아무도 모르며, scheduler의 attempt deadline을 잡의 timeout보다 짧게 잡으면 실제로 성공한 긴 실행이 실패로 기록됩니다.

  • auth 누락은 컴파일 에러입니다 — auth는 OperationsJobsModuleOptions의 필수 필드라, 이걸 빼고 배선한 module은 타입 검사부터 통과하지 못합니다. 빈 auth나 32자 미만 secret은 forRoot가 module을 조립하는 시점에 ERR_JOB_AUTH_MISCONFIGURED로 죽습니다.
  • 결과 필드는 좁힌 뒤에만 존재합니다 — JobExecutionResult에서 error·reason·summary는 status로 좁히기 전에는 접근할 수 없습니다. @ts-expect-error 픽스처 3개가 이를 고정합니다.
  • 단일 실행 보장을 지우는 튜닝을 거부합니다 — staleRunAfterMs가 heartbeatIntervalMs의 2배 미만이면 createJobRunner가 ERR_JOB_INVALID를 던져 부팅을 멈춥니다. 건강한 run의 watermark가 자기 beat 사이에서 liveness 예산보다 오래돼 보일 수 있는 하한입니다.
  • 저장소 원자성을 호스트 테스트에서 검사합니다 — jobRunStoreContractCases()는 프레임워크 없는 적합성 케이스 13개를 돌려줍니다. S1–S6 의무를 실제 database에 그대로 겁니다 — 동시 claim burst, 같은 세 행을 노리는 두 개의 동시 reap. inspect를 넘기면 S7까지 붙어 16개가 됩니다.
  • /core에 Nest가 없음을 산출물로 증명합니다 — guard test가 src/core/**·src/testing/**와 빌드 산출물 dist/core.·dist/testing. 청크 전량을 훑어 @nestjs·rxjs·reflect-metadata 참조가 하나도 없음을 확인합니다. 같은 파일의 대조군이 dist/index.js에는 @nestjs가 있다고 못 박기 때문에, 빈 결과가 ’스캐너가 아무것도 안 봤다’는 뜻일 수 없습니다.

완료 상태: 앱 소유 실행 저장소로 뒷받침되는 인증된 운영 작업 경계를 만듭니다.

Terminal window
pnpm add @gj-kit/nest-operations-jobs

2. 앱이 소유할 경계를 정합니다

섹션 제목: “2. 앱이 소유할 경계를 정합니다”

JobRunStore를 구현한 뒤 module을 등록하기 전에 32자 이상의 shared secret 또는 token verifier를 설정합니다.

먼저 아래 코드를 복사한 뒤, 위에서 언급한 앱 소유 값만 교체하세요.

import { OperationsJobsModule, type JobRunStore } from '@gj-kit/nest-operations-jobs';
declare const store: JobRunStore; // Your database-backed run store.
declare const secret: string; // At least 32 characters; keep it outside source control.
export const operations = OperationsJobsModule.forRoot({
store,
auth: { secret },
trigger: { path: 'internal/jobs' },
});

status로 좁히기 전에는 error에 접근할 수 없고, recorded는 모든 분기에 있습니다. 저장된 행이 반환된 status와 어긋나는 상황이야말로 알림을 걸 지점이기 때문입니다.

import type { JobExecutionResult } from '@gj-kit/nest-operations-jobs/core';
declare const result: JobExecutionResult; // await runner.execute(...)
declare function pageOncall(jobKey: string, runId: string): void; // the app owns this
export function report(): void {
// console.error(result.error.code);
// -> error TS2339: Property 'error' does not exist on type 'JobExecutionResult'.
if (result.status === 'TIMED_OUT') {
console.error(result.jobKey, result.error.code); // 'ERR_JOB_TIMEOUT'
} else if (result.status === 'SKIPPED') {
console.warn(result.jobKey, result.reason); // 'overlap' - the only value
}
// `recorded` sits on every branch: 'superseded' means a reaper already
// finalised the row, so a second body may run under a different runId.
if (result.recorded === 'superseded') pageOncall(result.jobKey, result.runId);
}

Nest 앱에서 명시적 동시성, 권한, 실행 영속성을 갖춘 스케줄 또는 운영자 실행 작업이 필요할 때 사용합니다.

제품 비즈니스 규칙, queue 인프라, 앱 권한 정책을 이 통합 뒤에 숨기지 마세요.

엔진: node >=20

Peer 지원 범위 필요한 위치
@nestjs/common `^10
@nestjs/core `^10
reflect-metadata `^0.1.13
rxjs ^7 패키지 통합
Entry Exported symbols
Root entry 60
./core 61
./testing 13

작업 실행 권한과 앱 데이터 소유권은 host 앱에 둡니다. 편의 route를 인증 없는 운영 endpoint로 만들지 마세요.

전체 API reference에는 선언 파일에서 생성한 모든 공개 함수, 클래스, 타입, 상수, 오류 계약이 있습니다. 각 항목은 import path, release signature, package version을 표시합니다.

관계 패키지 함께 쓰는 이유
단독 사용 — 다른 GJ Kit 패키지 없이 도입할 수 있습니다.
선택 조합 @gj-kit/nest-notifications 앱 소유 job 결과 뒤에 운영자나 사용자에게 알릴 때 함께 사용합니다.

npm · GitHub