feat(scan): 마이그 365 scan_jobs · 366 pending_command 채널 (scan-feature-build)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
-- 스캔 기능: 잡 모델 + 배치 + 에이전트 생존 (plan: scan-feature-build r3)
|
||||
-- 웹(fastapi)=intent/명령, 호스트 스캔 에이전트=결과. 싱글톤 스캐너 직렬화.
|
||||
-- 주: 러너 규약상 이 파일은 schema_migrations 를 건드리지 않음(스탬프는 외부). BEGIN/COMMIT 없음.
|
||||
-- 순서: 테이블 먼저 → 시드 → 인덱스 (인덱스 실패가 테이블 생성 막지 않게).
|
||||
|
||||
-- 잡: 한 스캔 세션 = 한 논리 문서 (배치 N개 → 합본 1 PDF → Inbox)
|
||||
CREATE TABLE IF NOT EXISTS scan_jobs (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
title TEXT NOT NULL, -- 사람 입력 제목 (commit 시 documents.title 로 전파)
|
||||
settings JSONB NOT NULL DEFAULT '{}'::jsonb, -- mode/resolution/source(ADF Duplex) 등 스캔 프로파일
|
||||
status TEXT NOT NULL DEFAULT 'draft', -- draft|queued|ready|scanning|assembling|preview|committing|committed|failed|canceled
|
||||
batch_count INTEGER NOT NULL DEFAULT 0, -- 스캔 완료 배치 수
|
||||
page_count INTEGER, -- 최종 합본 페이지 수 (assembling 후)
|
||||
last_activity_at TIMESTAMPTZ, -- ready 휴지 벽시계 idle 타임아웃 기준 (방치 데드락 방지)
|
||||
last_progress_at TIMESTAMPTZ, -- 잡 진행 갱신 (에이전트 생존과 분리)
|
||||
staging_path TEXT, -- 호스트 로컬 잡 스테이징 디렉토리
|
||||
nas_staging_path TEXT, -- NAS .scan-staging 합본 경로 (B안 미리보기/commit 소스)
|
||||
inbox_path TEXT, -- 최종 PKM/Inbox 경로 (commit 후)
|
||||
file_hash CHAR(64), -- 합본 sha256 = 정체성/멱등 커밋 키 (commit 시 채움)
|
||||
doc_id BIGINT REFERENCES documents(id) ON DELETE SET NULL, -- commit 후 연결 (title 전파)
|
||||
error TEXT, -- failed 사유 (no-silent)
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 배치: 스캔 1회(ADF 한 묶음) 단위. batch_seq = 결합 순서(글롭 정렬 아님).
|
||||
CREATE TABLE IF NOT EXISTS scan_job_batches (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
job_id BIGINT REFERENCES scan_jobs(id) ON DELETE CASCADE NOT NULL,
|
||||
batch_seq INTEGER NOT NULL, -- 1-based 결합 순서
|
||||
staging_path TEXT, -- 이 배치 PDF (호스트 로컬)
|
||||
page_count INTEGER,
|
||||
status TEXT NOT NULL DEFAULT 'scanned', -- scanned | discarded (잼 폐기 후 재스캔)
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
UNIQUE (job_id, batch_seq)
|
||||
);
|
||||
|
||||
-- 에이전트 생존: 싱글톤 1행. 잡 진행(last_progress_at)과 분리 — queued 잡 stale 오탐 방지.
|
||||
CREATE TABLE IF NOT EXISTS scan_agent_status (
|
||||
id INTEGER PRIMARY KEY DEFAULT 1 CHECK (id = 1), -- 단일 행 강제
|
||||
last_heartbeat TIMESTAMPTZ,
|
||||
agent_version TEXT,
|
||||
current_job_id BIGINT REFERENCES scan_jobs(id) ON DELETE SET NULL,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
INSERT INTO scan_agent_status (id) VALUES (1) ON CONFLICT (id) DO NOTHING; -- 시드 1행
|
||||
|
||||
-- 활성 잡 락: 스캐너 싱글톤 → in-progress 잡은 전체에서 1개만(나머지 queued).
|
||||
-- 상수 TRUE 에 unique + in-progress 필터 = 그 상태 행 최대 1개 강제.
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uq_scan_jobs_single_active
|
||||
ON scan_jobs ((TRUE))
|
||||
WHERE status IN ('ready','scanning','assembling','preview','committing');
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_scan_jobs_queued ON scan_jobs (created_at) WHERE status = 'queued';
|
||||
CREATE INDEX IF NOT EXISTS idx_scan_jobs_file_hash ON scan_jobs (file_hash) WHERE file_hash IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_scan_job_batches_job ON scan_job_batches (job_id, batch_seq);
|
||||
@@ -0,0 +1,4 @@
|
||||
-- 스캔 잡 명령 채널 (이중 라이터: API=intent/명령, 에이전트=result) — plan scan-feature-build r3
|
||||
-- API/수동이 pending_command 설정 → 에이전트가 조건부 claim(WHERE pending_command=X AND status=기대값) → 실행 → 결과 status write.
|
||||
ALTER TABLE scan_jobs ADD COLUMN IF NOT EXISTS pending_command TEXT; -- scan_batch | finish | commit | cancel
|
||||
ALTER TABLE scan_jobs ADD COLUMN IF NOT EXISTS command_requested_at TIMESTAMPTZ; -- 명령 요청 시각(staleness/디버그)
|
||||
Reference in New Issue
Block a user