Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 305454eeb5 |
@@ -1,7 +1,7 @@
|
|||||||
# AI Agent Briefing — PDF OCR Hotfolder
|
# AI Agent Briefing — PDF OCR Hotfolder
|
||||||
|
|
||||||
**Zuletzt aktualisiert:** 2026-09-22
|
**Zuletzt aktualisiert:** 2026-09-23
|
||||||
**Version:** 0.6.2
|
**Version:** 0.6.3
|
||||||
**Status:** Multi-Instanz-Betrieb, Preflight-Checks, Fehlerzählung, Wiederaufnahme aus `working/` und ein abgesicherter Updater (venv-Rebuild, Backup, Verifikation, Versionssprung-Meldung, Rauchtest). Test-Suite grün (152 pytest-Tests). Ein Produktiv-Einsatz ist im Repo (README/CHANGELOG) nicht dokumentiert — die bisherigen Fixes stammen aus Issues #1–#6 und aus Vorbereitungen auf Debian 13, nicht aus einem belegten Dauerbetrieb.
|
**Status:** Multi-Instanz-Betrieb, Preflight-Checks, Fehlerzählung, Wiederaufnahme aus `working/` und ein abgesicherter Updater (venv-Rebuild, Backup, Verifikation, Versionssprung-Meldung, Rauchtest). Test-Suite grün (152 pytest-Tests). Ein Produktiv-Einsatz ist im Repo (README/CHANGELOG) nicht dokumentiert — die bisherigen Fixes stammen aus Issues #1–#6 und aus Vorbereitungen auf Debian 13, nicht aus einem belegten Dauerbetrieb.
|
||||||
|
|
||||||
> **Betriebsabläufe stehen nicht hier**, sondern in:
|
> **Betriebsabläufe stehen nicht hier**, sondern in:
|
||||||
@@ -50,7 +50,7 @@ pdf-ocr-hotfolder/
|
|||||||
├── config.example.toml
|
├── config.example.toml
|
||||||
├── install.sh # Interaktiver Installer + Instanz-Manager
|
├── install.sh # Interaktiver Installer + Instanz-Manager
|
||||||
├── update.sh # Updater (--help, --rebuild-venv), ~860 Zeilen
|
├── update.sh # Updater (--help, --rebuild-venv), ~860 Zeilen
|
||||||
├── requirements.txt # feste Pins (ocrmypdf 16.x!)
|
├── requirements.txt # feste Pins (ocrmypdf 17.x — 16.x ist unbrauchbar, s. 0.6.1)
|
||||||
├── VERSION
|
├── VERSION
|
||||||
├── CHANGELOG.md
|
├── CHANGELOG.md
|
||||||
├── README.md
|
├── README.md
|
||||||
|
|||||||
@@ -1,5 +1,59 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [0.6.3] - 2026-09-23
|
||||||
|
|
||||||
|
Reine Doku-Version — kein Code, kein Installer, kein Updater, keine Unit, keine
|
||||||
|
Tests angefasst (ausser dem Versionsstring). Beide Erkenntnisse stammen aus den
|
||||||
|
Testlaeufen auf Debian 12 und Debian 13.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- **Abschnitt "Systemanforderungen" in `docs/INSTALLATION.md`.** Die
|
||||||
|
Dimensionierung fehlte bisher komplett. Empfohlen werden **mindestens 2 GB
|
||||||
|
RAM**; 512 MB reichen fuer 300-dpi-Scans nachweislich nicht. Gemessen auf
|
||||||
|
einem LXC-Container mit 512 MB RAM + 512 MB Swap, Debian 13,
|
||||||
|
Ghostscript 10.05.1: eine **einzelne A4-Seite in 300 dpi** mit
|
||||||
|
`deskew = true` riss das cgroup-Limit und der Dienst wurde vom OOM-Killer
|
||||||
|
beendet (`oom-kill:constraint=CONSTRAINT_MEMCG, oom_memcg=/lxc/201,
|
||||||
|
task=python`, `total-vm:1168764kB, anon-rss:489676kB`). Dieselbe
|
||||||
|
Verarbeitung mit einer kleineren Seite (850x1100 px) lief in ca. 19 s sauber
|
||||||
|
durch.
|
||||||
|
- Dokumentiert ist der Zusammenhang **RAM <-> `max_workers` x `jobs` x
|
||||||
|
Aufloesung**: `max_workers` (Default 2) laesst zwei solcher Seiten
|
||||||
|
gleichzeitig laufen, der Spitzenbedarf multipliziert sich entsprechend.
|
||||||
|
Wer knapp dimensioniert, zieht zuerst `max_workers` herunter.
|
||||||
|
- Dazu, **wie sich ein OOM-Kill aeussert** (Dienst weg bzw. von systemd neu
|
||||||
|
gestartet, `NRestarts` steigt, Abbruch mitten in der Datei ohne Traceback,
|
||||||
|
PDF bleibt in `working/` liegen) und **wie man ihn nachweist**
|
||||||
|
(`/sys/fs/cgroup/memory.events` im Container, `dmesg` auf dem LXC-Host).
|
||||||
|
Ohne diese Pruefung sieht der Fall wie ein Anwendungsfehler aus und man
|
||||||
|
sucht in ocrmypdf, Tesseract oder der Config.
|
||||||
|
- Mit dem Hinweis, dass die Wiederaufnahme aus `working/` seit v0.6.0 den
|
||||||
|
Datenverlust abfaengt — der OOM selbst bleibt aber ein Problem: bei
|
||||||
|
unveraenderter Dimensionierung laeuft dieselbe Datei nach dem Neustart
|
||||||
|
erneut hinein.
|
||||||
|
- `README.md` bekommt im Schnellstart nur einen Einzeiler mit Verweis, keine
|
||||||
|
Dublette.
|
||||||
|
- **Troubleshooting-Eintrag "Keine Logs: No journal files were found" in
|
||||||
|
`docs/INSTALLATION.md`.** Auf einem der Testcontainer war
|
||||||
|
`systemd-journald.service` kaputt (`failed`, `status=243/CREDENTIALS`),
|
||||||
|
`journalctl` lieferte `No journal files were found.` Da der Dienst seit
|
||||||
|
v0.4.1 **ausschliesslich** nach journald loggt (kein FileHandler, kein
|
||||||
|
Logverzeichnis — bewusste Entscheidung), gibt es dann gar keine Dienstlogs:
|
||||||
|
ein blinder Fleck, der vor jeder Fehlersuche per
|
||||||
|
`systemctl status systemd-journald` auszuschliessen ist. Als Notbehelf ist
|
||||||
|
der Vordergrund-Aufruf dokumentiert
|
||||||
|
(`cd /opt/pdf-ocr-hotfolder && sudo -u pdfocr ./venv/bin/python -m
|
||||||
|
pdf_ocr_hotfolder --config ...`, das `cd` ist zwingend, s. 0.6.2).
|
||||||
|
Ausdruecklich festgehalten: das ist **kein** generelles LXC-Muster — der
|
||||||
|
zweite Testcontainer war in Ordnung, es war Schaden auf genau dieser
|
||||||
|
Maschine.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- `AI_AGENT_BRIEFING.md`: der Kommentar zu `requirements.txt` in der
|
||||||
|
Projektstruktur nannte noch "ocrmypdf 16.x!" — genau die Version, die seit
|
||||||
|
0.6.1 als unbrauchbar gilt und gegen die der Pin schuetzt. Korrigiert auf
|
||||||
|
17.x.
|
||||||
|
|
||||||
## [0.6.2] - 2026-09-22
|
## [0.6.2] - 2026-09-22
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ Weiter: [CHANGELOG.md](CHANGELOG.md) · [AI_AGENT_BRIEFING.md](AI_AGENT_BRIEFING
|
|||||||
|
|
||||||
## Schnellstart
|
## Schnellstart
|
||||||
|
|
||||||
|
**Voraussetzungen:** Debian 12 oder 13, Python 3.11+, root — und **mindestens
|
||||||
|
2 GB RAM** (512 MB reichen für 300-dpi-Scans nachweislich nicht, siehe
|
||||||
|
[Systemanforderungen](docs/INSTALLATION.md#systemanforderungen)).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone gitea@gitea.sonith.de:sonith_ug/pdf-ocr-hotfolder.git
|
git clone gitea@gitea.sonith.de:sonith_ug/pdf-ocr-hotfolder.git
|
||||||
cd pdf-ocr-hotfolder
|
cd pdf-ocr-hotfolder
|
||||||
@@ -158,5 +162,5 @@ MIT — © Sonith UG
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Version:** 0.6.2
|
**Version:** 0.6.3
|
||||||
**Repo:** https://gitea.sonith.de/sonith_ug/pdf-ocr-hotfolder
|
**Repo:** https://gitea.sonith.de/sonith_ug/pdf-ocr-hotfolder
|
||||||
|
|||||||
+137
-1
@@ -12,6 +12,7 @@ Verwandte Dokumente: [README](../README.md) · [Update](UPDATE.md) · [Debian-Ma
|
|||||||
|-------|-------------|
|
|-------|-------------|
|
||||||
| Betriebssystem | Debian 12 (bookworm) oder Debian 13 — systemd wird vorausgesetzt |
|
| Betriebssystem | Debian 12 (bookworm) oder Debian 13 — systemd wird vorausgesetzt |
|
||||||
| Python | 3.11+ (wegen `tomllib` aus der stdlib); kommt aus der Distribution |
|
| Python | 3.11+ (wegen `tomllib` aus der stdlib); kommt aus der Distribution |
|
||||||
|
| Arbeitsspeicher | **mindestens 2 GB** für den produktiven Betrieb — siehe [Systemanforderungen](#systemanforderungen) |
|
||||||
| Rechte | `root` (`sudo ./install.sh`) |
|
| Rechte | `root` (`sudo ./install.sh`) |
|
||||||
| Netz | apt-Zugriff für die System-Pakete, PyPI-Zugriff für die venv |
|
| Netz | apt-Zugriff für die System-Pakete, PyPI-Zugriff für die venv |
|
||||||
| Repo | muss dauerhaft liegen bleiben — `update.sh` kopiert daraus (s. [UPDATE.md](UPDATE.md)) |
|
| Repo | muss dauerhaft liegen bleiben — `update.sh` kopiert daraus (s. [UPDATE.md](UPDATE.md)) |
|
||||||
@@ -29,7 +30,7 @@ ca-certificates curl
|
|||||||
```
|
```
|
||||||
|
|
||||||
Weitere Tesseract-Sprachpakete installiert der Installer bei Bedarf pro Instanz
|
Weitere Tesseract-Sprachpakete installiert der Installer bei Bedarf pro Instanz
|
||||||
nach (siehe [OCR-Sprachen](#3-ocr-sprachen)).
|
nach (siehe [OCR-Sprachen](#4-ocr-sprachen)).
|
||||||
|
|
||||||
Die Python-Abhängigkeiten stehen **fest gepinnt** in `requirements.txt`
|
Die Python-Abhängigkeiten stehen **fest gepinnt** in `requirements.txt`
|
||||||
(ocrmypdf, watchdog, requests, paramiko). Warum das so ist und wie man die Pins
|
(ocrmypdf, watchdog, requests, paramiko). Warum das so ist und wie man die Pins
|
||||||
@@ -37,6 +38,92 @@ anhebt: [OS-UPGRADE.md](OS-UPGRADE.md#pins-in-requirementstxt).
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Systemanforderungen
|
||||||
|
|
||||||
|
CPU und Platte sind unkritisch — **der Arbeitsspeicher ist es nicht.** OCR
|
||||||
|
rastert jede Seite in voller Auflösung ins RAM; der Spitzenbedarf hängt an der
|
||||||
|
Seitengröße, nicht an der Dateigröße der PDF.
|
||||||
|
|
||||||
|
**Empfehlung: mindestens 2 GB RAM.** Für Mehr-Instanz-Betrieb oder
|
||||||
|
`max_workers > 2` entsprechend mehr.
|
||||||
|
|
||||||
|
### Warum 512 MB nachweislich nicht reichen
|
||||||
|
|
||||||
|
Gemessen auf einem LXC-Container mit **512 MB RAM + 512 MB Swap**, Debian 13,
|
||||||
|
Ghostscript 10.05.1:
|
||||||
|
|
||||||
|
| Vorgang | Ergebnis |
|
||||||
|
|---------|----------|
|
||||||
|
| eine einzelne **A4-Seite in 300 dpi**, `deskew = true` | cgroup-Limit gerissen, Dienst vom **OOM-Killer** beendet |
|
||||||
|
| dieselbe Verarbeitung mit einer kleineren Seite (850 × 1100 px) | läuft sauber durch, ca. 19 s |
|
||||||
|
|
||||||
|
Beleg aus dem `dmesg` des LXC-Hosts:
|
||||||
|
|
||||||
|
```
|
||||||
|
oom-kill:constraint=CONSTRAINT_MEMCG, oom_memcg=/lxc/201, task=python
|
||||||
|
total-vm:1168764kB, anon-rss:489676kB
|
||||||
|
```
|
||||||
|
|
||||||
|
Eine Seite, ein Worker — und schon knapp 500 MB anonymer Speicher. 512 MB sind
|
||||||
|
damit für 300-dpi-Scans keine knappe, sondern eine unzureichende Dimensionierung.
|
||||||
|
|
||||||
|
### RAM ↔ `max_workers` × `jobs` × Auflösung
|
||||||
|
|
||||||
|
Der Spitzenbedarf multipliziert sich über drei Config-Werte aus
|
||||||
|
[`[ocr]`](#ocr):
|
||||||
|
|
||||||
|
| Key | Default | Wirkung auf den Speicher |
|
||||||
|
|-----|---------|--------------------------|
|
||||||
|
| `max_workers` | `2` | **so viele PDFs gleichzeitig** — jede mit eigenem Seitenpuffer. Der direkte Multiplikator |
|
||||||
|
| `jobs` | `4` | Threads **innerhalb** einer PDF; mehrere Seiten gleichzeitig im Speicher |
|
||||||
|
| `oversample` | `300` | Auflösung gerasterter Seiten — der Bedarf wächst quadratisch mit der dpi |
|
||||||
|
|
||||||
|
Der Default `max_workers = 2` erlaubt also, dass **zwei** solcher Seiten
|
||||||
|
parallel verarbeitet werden. Wer knapp dimensioniert, zieht zuerst
|
||||||
|
`max_workers` auf `1` herunter, danach `jobs`. `oversample` unter 300 zu
|
||||||
|
drücken, spart zwar Speicher, kostet aber Erkennungsqualität — das ist der
|
||||||
|
letzte Hebel, nicht der erste.
|
||||||
|
|
||||||
|
### Wie sich ein OOM-Kill äußert
|
||||||
|
|
||||||
|
Von außen sieht ein OOM-Kill wie ein Anwendungsfehler aus — er ist keiner:
|
||||||
|
|
||||||
|
- Der Dienst ist **weg** bzw. wurde von systemd neu gestartet
|
||||||
|
(`Restart=on-failure`); `systemctl show -p NRestarts` steigt.
|
||||||
|
- Im journal bricht die Verarbeitung **mitten in der Datei** ab, ohne
|
||||||
|
Python-Traceback und ohne `ERROR`-Zeile aus dem Tool.
|
||||||
|
- Die betroffene PDF bleibt in `working/` liegen.
|
||||||
|
|
||||||
|
### Wie man ihn nachweist
|
||||||
|
|
||||||
|
**Im Container:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat /sys/fs/cgroup/memory.events
|
||||||
|
# oom_kill 1 <- alles über 0 ist ein Treffer
|
||||||
|
```
|
||||||
|
|
||||||
|
**Auf dem LXC-Host** (im Container zeigt `dmesg` diese Zeilen nicht):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dmesg -T | grep -i oom-kill
|
||||||
|
```
|
||||||
|
|
||||||
|
Diese Prüfung gehört an den **Anfang** der Fehlersuche, wenn Dateien
|
||||||
|
unerklärlich in `working/` liegen bleiben: ohne sie sucht man den Fehler in
|
||||||
|
ocrmypdf, Tesseract oder der Config, wo keiner ist.
|
||||||
|
|
||||||
|
### Datenverlust ist abgefangen, der OOM bleibt
|
||||||
|
|
||||||
|
Seit **v0.6.0** greift der Dienst beim nächsten Start auf, was in `working/`
|
||||||
|
liegen geblieben ist (siehe [UPDATE.md](UPDATE.md#wiederaufnahme-aus-working)).
|
||||||
|
Eine vom OOM-Killer unterbrochene Datei geht also nicht verloren. Behoben ist
|
||||||
|
damit aber nur die Folge: bei unveränderter Dimensionierung läuft dieselbe Datei
|
||||||
|
nach dem Neustart erneut in denselben OOM — bis `max_workers` sinkt oder das
|
||||||
|
System mehr RAM bekommt.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -480,6 +567,55 @@ cd /opt/pdf-ocr-hotfolder && sudo ./venv/bin/python -m pdf_ocr_hotfolder \
|
|||||||
Der Interpreter der venv ist weg — fast immer nach einem Distributions-Upgrade.
|
Der Interpreter der venv ist weg — fast immer nach einem Distributions-Upgrade.
|
||||||
Siehe [OS-UPGRADE.md](OS-UPGRADE.md).
|
Siehe [OS-UPGRADE.md](OS-UPGRADE.md).
|
||||||
|
|
||||||
|
### Keine Logs: "No journal files were found"
|
||||||
|
|
||||||
|
**Symptom:** `journalctl -u pdf-ocr-hotfolder@<instanz>` bleibt leer oder meldet
|
||||||
|
`No journal files were found.` — auch dann, wenn der Dienst nachweislich läuft
|
||||||
|
und Dateien verarbeitet.
|
||||||
|
|
||||||
|
**Erste Prüfung:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl status systemd-journald
|
||||||
|
```
|
||||||
|
|
||||||
|
Ist `systemd-journald.service` selbst `failed` (beobachtet mit
|
||||||
|
`status=243/CREDENTIALS`), gibt es schlicht kein journal, in das geschrieben
|
||||||
|
werden könnte.
|
||||||
|
|
||||||
|
**Einordnung.** Der Dienst loggt seit v0.4.1 **ausschließlich** nach journald —
|
||||||
|
es gibt bewusst kein eigenes Logfile und kein Logverzeichnis. Ein kaputtes
|
||||||
|
journald ist damit ein blinder Fleck: jede Fehlersuche läuft ins Leere, und der
|
||||||
|
Ausfall sieht aus wie ein stummer Dienst. **Deshalb vor jeder Fehlersuche
|
||||||
|
zuerst journald prüfen**, nicht erst, wenn nichts anderes mehr passt.
|
||||||
|
|
||||||
|
Das ist **kein** generelles LXC-Muster. Von zwei Testcontainern war genau einer
|
||||||
|
betroffen; auf dem zweiten lief journald einwandfrei. Es handelt sich um einen
|
||||||
|
Schaden auf dieser einen Maschine, nicht um eine Eigenschaft von Containern.
|
||||||
|
|
||||||
|
**Notbehelf, solange journald nicht zu retten ist:** die Instanz einmal im
|
||||||
|
Vordergrund laufen lassen — dann geht die Ausgabe direkt ins Terminal, am
|
||||||
|
journal vorbei.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl stop pdf-ocr-hotfolder@<instanz>
|
||||||
|
cd /opt/pdf-ocr-hotfolder && sudo -u pdfocr ./venv/bin/python -m pdf_ocr_hotfolder \
|
||||||
|
--config /etc/pdf-ocr-hotfolder/<instanz>.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
Das `cd` ist zwingend: das Paket wird nicht pip-installiert, sondern nach
|
||||||
|
`/opt/pdf-ocr-hotfolder` kopiert und nur über das Arbeitsverzeichnis gefunden
|
||||||
|
(ohne `cd` gibt es `No module named pdf_ocr_hotfolder`, v0.6.2). Beenden mit
|
||||||
|
`Strg+C`, danach `sudo systemctl start pdf-ocr-hotfolder@<instanz>`. Wer nur den
|
||||||
|
Bestand abarbeiten und dann aussteigen will, hängt `--once` an (siehe
|
||||||
|
[Manueller Lauf](#manueller-lauf-one-shot)).
|
||||||
|
|
||||||
|
### Dienst bricht mitten in der Verarbeitung weg
|
||||||
|
|
||||||
|
Datei bleibt in `working/`, kein Traceback, `NRestarts` steigt: das ist fast
|
||||||
|
immer der OOM-Killer, kein Anwendungsfehler. Nachweis und Dimensionierung unter
|
||||||
|
[Systemanforderungen](#wie-sich-ein-oom-kill-äußert).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Manueller Lauf (One-Shot)
|
## Manueller Lauf (One-Shot)
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
"""PDF OCR Hotfolder — Scanner-PDFs automatisch durchsuchbar machen."""
|
"""PDF OCR Hotfolder — Scanner-PDFs automatisch durchsuchbar machen."""
|
||||||
|
|
||||||
__version__ = "0.6.2"
|
__version__ = "0.6.3"
|
||||||
|
|||||||
Reference in New Issue
Block a user