짧은 답: pg-boss 12.37.0은 동일 키의 동시 upsert 삽입 경쟁을 수정했습니다. 문서 색인처럼 대기 요청을 합쳐도 되는 작업에 활용하되, 인덱스 준비 상태와 재실행에 안전한 처리 로직을 함께 확인하세요.
기준일: 2026년 10월 6일 · 대상: Node.js·PostgreSQL 백엔드 · API 기준: pg-boss 12.37.0
1. 모든 변경을 처리할까, 최신 상태만 맞출까?
문서가 연속 수정될 때마다 색인 갱신 작업을 넣으면 같은 문서의 대기 요청이 쌓입니다. 실행 시 원본의 최신 내용을 읽는 구조라면 요청을 하나로 합칠 수 있습니다. 반면 감사 기록이나 변경별 알림은 합치면 정보가 사라지므로 개별 이벤트로 보존해야 합니다. 먼저 업무가 어느 쪽인지 정하세요.
2. 버전과 운영 조건 확인
12.37.0은 2026년 10월 5일 UTC, 한국 시간으로 10월 6일 공개되었습니다. 확인 시점의 GitHub Latest이며 프리릴리스가 아닙니다. npm에도 같은 버전이 배포되어 있습니다. 스키마 변경은 버전 45입니다. 릴리스 노트, npm 패키지
요구 사항은 Node.js 22.12.0 이상과 PostgreSQL 13 이상입니다. MIT 라이선스이며, 공식 README는 timgit의 1인 유지관리 프로젝트라고 밝힙니다. 최근 수정 배포는 확인되지만 장기 지원 체계는 별도로 평가하세요. 고정 버전 패키지 정보, README
최소 호환 버전과 보안 지원 기간은 다릅니다. PostgreSQL 13은 지원이 끝났으므로 새 환경에는 지원 중인 버전을 선택하세요. 기존 DB를 큐로 활용하면 운영 대상을 줄이는 대신 작업 적재·정리 부하를 함께 감당해야 합니다. PostgreSQL 지원 정책
3. 같은 키 하나가 보장하는 범위
upsert()는 created 또는 retry 작업을 찾아 갱신하고, 없으면 삽입합니다. 실행 중·완료된 작업은 갱신하지 않습니다. 이번 수정은 동시에 새 대기 작업을 삽입하는 upsert 사이의 경쟁을 다룹니다. 12.37.0 Jobs API
새 인덱스는 큐 이름과 키의 조합에 적용되며 조건은 state = 'created' AND upsert_by_key입니다. 모든 상태를 합쳐 영구적으로 한 행만 남기지는 않습니다. 활성 작업이 실패해 retry로 돌아오면 더 새 작업과 공존할 수 있습니다. 인덱스 정의
send()·insert()를 섞으면 정책에 따라 같은 키의 별도 행이 생깁니다resume()으로 복구한 작업도 새 upsert 작업 옆에 대기할 수 있습니다singleton정책은 활성 작업을 제한하며 대기 작업 여러 개를 허용합니다
합치려는 요청은 생성 경로를 통일하고 키에는 필요에 따라 테넌트 ID까지 포함하세요. 생성·복구 API, 큐 정책
4. 동시 upsert와 혼합 send를 분리해서 검사하기
폐기 가능한 테스트 DB에서 새 폴더를 만들고 버전을 고정합니다. DATABASE_URL은 환경변수로 주입하세요. 예제는 새 pgboss_upsert_demo 스키마를 전제로 하며 작업과 스키마를 남깁니다.
npm init -y
npm install --save-exact pg-boss@12.37.0
아래를 verify-upsert.mjs로 저장합니다. 이 큐를 소비하는 다른 워커가 없어야 합니다.
import assert from 'node:assert/strict';
import { randomUUID } from 'node:crypto';
import { PgBoss } from 'pg-boss';
if (!process.env.DATABASE_URL) {
throw new Error('DATABASE_URL must point to a disposable test database');
}
const boss = new PgBoss({
connectionString: process.env.DATABASE_URL,
schema: 'pgboss_upsert_demo',
});
boss.on('error', (error) => console.error(error.message));
try {
await boss.start();
const queue = 'content-reindex-demo';
await boss.createQueue(queue, { policy: 'standard' });
const documentId = randomUUID();
const singletonKey = `document:${documentId}`;
// 이 큐를 소비하는 worker는 실행하지 않습니다.
const results = await Promise.all(
Array.from({ length: 20 }, () =>
boss.upsert(queue, { documentId }, { singletonKey })
)
);
const jobs = await boss.findJobs(queue, { key: singletonKey, queued: true });
assert.equal(results.reduce((n, result) => n + result.inserted, 0), 1);
assert.equal(jobs.length, 1);
console.log('upsert-only check passed');
// standard 정책에서는 send가 같은 키의 별도 작업을 만들 수 있습니다.
await boss.send(queue, { documentId }, { singletonKey });
const mixed = await boss.findJobs(queue, { key: singletonKey, queued: true });
assert.equal(mixed.length, 2);
console.log('mixed send check passed');
} finally {
await boss.stop();
}
node verify-upsert.mjs
기대 조건은 upsert 20회에서 삽입 합계 1, 대기 행 1입니다. 다음 send() 뒤에는 standard 정책에 따라 행 2를 확인합니다. 이는 문서에 근거한 기대값이며 실제 DB 실행 결과가 아닙니다. 워커를 켜면 행이 활성 상태로 이동할 수 있으므로 소비·재시도 검사는 별도 시나리오로 추가하세요. 응답 형태와 조회 옵션
5. 업그레이드는 인덱스 준비까지 확인하기
기존 PostgreSQL 환경에서는 start() 이후 인덱스를 백그라운드에서 테이블별로 만듭니다. 기동 성공이나 스키마 45만으로 수정 적용 완료를 판단하면 안 됩니다. 기존 중복 행을 자동 병합하는 마이그레이션도 아닙니다. 업그레이드 주의사항, 마이그레이션 소스
다음은 기본 스키마의 공용 작업 테이블용 읽기 전용 점검입니다. 사용자 정의 스키마나 전용 파티션을 쓰면 실제 저장 테이블마다 대상을 바꾸세요. 내부 구조에 의존하므로 12.37.0 외 버전에서는 다시 대조해야 합니다.
SELECT indexrelid::regclass AS index_name,
indisunique, indisready, indisvalid,
pg_get_indexdef(indexrelid) AS definition
FROM pg_index
WHERE indrelid = 'pgboss.job_common'::regclass
AND pg_get_expr(indpred, indrelid) LIKE '%upsert_by_key%';
인덱스 정의가 앞서 설명한 키·조건인지, 세 상태 값이 모두 참인지 확인하세요. 결과가 없거나 거짓이면 준비 완료로 보지 않습니다. indisvalid가 거짓인 유니크 인덱스는 유일성 보장도 확정할 수 없습니다. 생성 오류가 나면 작업 상태와 원인을 조사하고, 통과시키려고 임의 삭제하지 마세요. pg_index 문서
6. 색인 결과는 재실행에도 안전하게
워커 오류는 재시도로 이어질 수 있습니다. 외부 색인 쓰기가 성공한 뒤 응답이 끊기는 상황도 있으므로, 큐의 중복 억제와 결과 저장의 멱등성을 각각 설계해야 합니다. 워커 실패 동작
애플리케이션에서는 문서 ID를 고정하고, 실행 시 원본을 다시 읽으며, 대상에 더 최신 버전이 있으면 오래된 쓰기를 원자적으로 거부하도록 설계하세요. 버전 12를 읽은 느린 작업이 버전 13보다 늦게 끝나는 경우까지 막아야 합니다. 이는 pg-boss가 자동 구현해 주는 기능이 아닙니다.
배포 전에는 요청을 합쳐도 되는지, 생성 경로가 통일됐는지, 인덱스가 준비됐는지 확인하세요. 이어 쓰기 성공 후 응답 유실과 워커 재시작을 재현해 결과가 유지되는지 검사하면 됩니다.
작성·검증 공개: AI의 도움으로 작성하고 공식 문서·12.37.0 태그 소스·배포 정보를 확인했습니다. JavaScript는 로컬 Node.js 구문 검사만 수행했습니다. 패키지 설치, DB·SQL 실행, 부하 및 장애 재현은 수행하지 않았습니다.
댓글
댓글 쓰기