Git-Repository für bestehenden archivdms-Code initialisiert, Branch-/Commit-Konvention (feature/<ticket>-<slug>-Branches, Ticket-Prefix in Commit-Nachricht) etabliert.
60 lines
3.2 KiB
SQL
60 lines
3.2 KiB
SQL
-- PROJ: mandanten-job-queue (Phase 1+2)
|
|
-- Doku-only (siehe README.md in diesem Verzeichnis) — die tatsächliche
|
|
-- Ausführung passiert idempotent über
|
|
-- internal/storage/processing_jobs.go initProcessingJobsSchema(),
|
|
-- aufgerufen aus (*Store).initSchema() (zuletzt in der Kette, da die Tabelle
|
|
-- documents(id) FK-referenziert).
|
|
--
|
|
-- Mandanten-faire Verarbeitungs-Queue für die NACHGELAGERTE Dokument-
|
|
-- verarbeitung (OCR-Extraktion, Taxonomie-Autozuordnung, on_upload-Workflows).
|
|
-- Vorher lief das synchron im Upload-Request und erzeugte bei Batch-Scans /
|
|
-- SFTP-Massenuploads Lastspitzen.
|
|
--
|
|
-- * Kein Redis: die Queue ist eine ganz normale Postgres-Tabelle. Dokument-
|
|
-- INSERT und Job-INSERT laufen in EINER Transaktion
|
|
-- (CreateDocumentWithJob) — nie ein Dokument ohne Job, nie ein Job ohne
|
|
-- Dokument.
|
|
-- * WORM bleibt synchron im Request: Hash, Ablage unter
|
|
-- store/<tenant>/<yyyy>/<mm>/<sha256>.<ext> und chmod 0440 passieren VOR
|
|
-- dem INSERT. Der Job liest die archivierte Datei nur und schreibt
|
|
-- ausschließlich abgeleitete Metadaten.
|
|
-- * Dispatch: Round-Robin über die Mandanten (je Runde ein Job pro Mandant),
|
|
-- Locking über FOR UPDATE SKIP LOCKED.
|
|
-- * Retry: exponentielles Backoff über next_attempt_at (2^retry_count
|
|
-- Sekunden). Ab retry_count > max_retries (Default 5) bleibt der Job
|
|
-- dauerhaft 'failed' — kein Automatik-Retry mehr.
|
|
-- * derive_title: merkt sich, ob der Titel beim Staging nur ein Platzhalter
|
|
-- war und aus dem OCR-Text ersetzt werden darf. Ein vom Benutzer bzw. aus
|
|
-- dem SFTP-Dateinamen vorgegebener Titel wird nie überschrieben.
|
|
|
|
CREATE TABLE IF NOT EXISTS processing_jobs (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
tenant_id BIGINT NOT NULL,
|
|
document_id BIGINT NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
|
|
status TEXT NOT NULL DEFAULT 'queued', -- queued | processing | done | failed
|
|
retry_count INT NOT NULL DEFAULT 0,
|
|
derive_title BOOLEAN NOT NULL DEFAULT false,
|
|
error_message TEXT,
|
|
next_attempt_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
started_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
ALTER TABLE processing_jobs ADD COLUMN IF NOT EXISTS derive_title BOOLEAN NOT NULL DEFAULT false;
|
|
ALTER TABLE processing_jobs ADD COLUMN IF NOT EXISTS next_attempt_at TIMESTAMPTZ NOT NULL DEFAULT now();
|
|
ALTER TABLE processing_jobs ADD COLUMN IF NOT EXISTS started_at TIMESTAMPTZ;
|
|
ALTER TABLE processing_jobs ADD COLUMN IF NOT EXISTS error_message TEXT;
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_processing_jobs_dispatch
|
|
ON processing_jobs(tenant_id, status, next_attempt_at, created_at);
|
|
CREATE INDEX IF NOT EXISTS idx_processing_jobs_document
|
|
ON processing_jobs(document_id);
|
|
CREATE INDEX IF NOT EXISTS idx_processing_jobs_status
|
|
ON processing_jobs(status);
|
|
|
|
-- Anzeige-/Ablaufstatus am Dokument. Default 'done': der komplette Altbestand
|
|
-- wurde noch synchron verarbeitet und ist per Definition fertig — bewusst KEIN
|
|
-- Backfill-Skript, der Spalten-Default deckt alle Bestandszeilen ab.
|
|
ALTER TABLE documents ADD COLUMN IF NOT EXISTS processing_status TEXT NOT NULL DEFAULT 'done';
|