메모리에서 기다리는 순서와 DB에 남는 상태는 다르다
Kaya Toast의 글 생성은 Claude CLI를 호출하는 비동기 작업이다. 요청마다 즉시 실행하면 동시에 시작하는 수를 제어할 수 없고, 메모리 큐만 쓰면 프로세스 종료 뒤 어떤 작업이 남았는지 알 수 없다. 구현은 실행 조정과 작업 기록을 두 층으로 나눴다.
당시 설계는 Astro SSR과 큐를 하나의 Node·PM2 프로세스에 두고 동시 실행 수를 3으로 정했다. p-queue는 콜백의 동시성을 제한하고, SQLite는 큐에 넣기 전에 pending 작업 행을 만든다. 별도 워커나 Redis를 구성하지 않는 형태를 유지하면서 재시작 뒤 판단할 최소 상태를 남기는 구조다.
- SQLite에 pending 저장작업 ID와 생성 대상을 먼저 기록한다.
- 메모리 큐에 실행 콜백 추가한 프로세스 안에서 최대 3개를 실행한다.
- running에서 done 또는 failed로 변경성공한 글의 연결이나 실패 사유를 DB에 남긴다.
대기 작업은 다시 넣고, 중단된 실행은 실패로 정리한다
재시작하면 메모리의 실행 순서는 사라진다. 애플리케이션은 재시작 후 최초 사용 시 DB와 큐를 준비하면서 running 행을 failed, 오류를 restarted로 바꾼다. 이어 pending ID를 읽어 새 메모리 큐에 다시 넣는다. DB 기록이 있으므로 대기 작업까지 모두 잃는 구조는 아니다.
| 남은 상태 | 처리 |
|---|---|
| pending | 같은 작업 ID로 재투입 |
| running | 중단된 작업을 failed로 변경 · 실행 재개 없음 |
| failed 수동 재시도 | 기존 대상과 날짜로 새 작업 ID를 만들어 추가 |
| done | 완료 기록과 결과 글 연결 유지 |
실행을 자동으로 이어가는 대신 중단 사실을 남기고 재시도를 별도 작업으로 만드는 정책이다. 외부 CLI가 결과를 만들었지만 로컬 완료 저장 전에 프로세스가 죽었다면, 실패 행만으로 외부 작업의 완료 여부를 확정할 수 없다. 새 작업을 만드는 수동 재시도도 외부 생성의 정확히 한 번 실행을 보장하지 않는다.
작업 ID를 넘긴다고 중복 실행이 막히지는 않는다
큐의 add(_key, task)는 key를 사용하지 않는다. DB의 running 변경도 pending 조건 없이 ID로 갱신한다. 같은 ID를 두 번 넣으면 두 번 실행할 수 있고, 다른 프로세스의 큐와 실행 허가를 공유하지 않는다. 프로세스 내부 이벤트 알림 역시 놓친 이벤트를 영구 저장해 재전송하는 기능은 아니다.
큐 테스트는 실제 비동기 콜백 10개로 최대 동시 실행 수가 3 이하인지 확인하고, 실패한 콜백 옆의 작업들이 계속 실행되는지 확인한다. runner 테스트는 mock CLI의 성공·종료 실패·잘못된 응답을 다룬다. 부팅 복구, 같은 ID의 중복 투입, 다중 프로세스 경합은 이 테스트들의 범위 밖이다.
Kaya Toast의 이 구조는 한 프로세스의 실행 제한과 상태별 재시작 정책을 함께 제공한다. 프로세스를 늘리려면 상태 기록에 더해 조건부 실행권 획득과 워커 간 조정이 필요하다. SQLite에 이력이 있다는 사실만으로 그 실행 보장까지 얻는 것은 아니다.