Files
hyungi_document_server/migrations/365_scan_jobs.sql
T
2026-06-25 21:43:44 +00:00

57 lines
4.0 KiB
SQL

-- 스캔 기능: 잡 모델 + 배치 + 에이전트 생존 (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);