From 46502abe755f5eb1e3155a54ae320b88aa258d7b Mon Sep 17 00:00:00 2001 From: sysops Date: Wed, 24 Jun 2026 23:54:37 +0200 Subject: [PATCH] =?UTF-8?q?fix(PROJ-58):=20Lockfile=20gegen=20=C3=BCberlap?= =?UTF-8?q?pende=20Cron-Batch-L=C3=A4ufe=20+=20update.sh=20synct=20Cron-Da?= =?UTF-8?q?teien?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root Cause für ausbleibende Lastsenkung: update.sh hat /etc/cron.d/archivmail nie auf den Server kopiert, daher fehlten die PROJ-58-Cronzeilen trotz aktivem batch_mode komplett. Jetzt kopiert update.sh die Cron-Datei und alle Wrapper-Skripte bei jedem Deploy automatisch ein. Zusätzlich: Wrapper-Skripte (analog mailpiler indexer.delta.sh) verhindern per Lockfile, dass sich Cron-Läufe bei großem Backlog überlappen. Co-Authored-By: Claude Sonnet 4.6 --- deploy/cron.d/archivmail | 11 ++++++++-- deploy/cron.d/archivmail-index-pending.sh | 25 +++++++++++++++++++++++ deploy/cron.d/archivmail-ocr-reprocess.sh | 21 +++++++++++++++++++ features/PROJ-58-cron-batch-index-ocr.md | 6 ++++++ update.sh | 9 ++++++++ 5 files changed, 70 insertions(+), 2 deletions(-) create mode 100755 deploy/cron.d/archivmail-index-pending.sh create mode 100755 deploy/cron.d/archivmail-ocr-reprocess.sh diff --git a/deploy/cron.d/archivmail b/deploy/cron.d/archivmail index bfba9bd..6a60d3d 100644 --- a/deploy/cron.d/archivmail +++ b/deploy/cron.d/archivmail @@ -46,15 +46,22 @@ # dauerhaft und arbeitet alles sofort ab. Wer batch_mode nicht nutzt, kann # die beiden Zeilen einfach auskommentiert lassen. # +# Die Jobs rufen Wrapper-Skripte auf (archivmail-index-pending.sh / +# archivmail-ocr-reprocess.sh), die per PID-Lockfile unter /var/run/archivmail/ +# überlappende Läufe verhindern — analog zum mailpiler-Vorbild +# (indexer.delta.sh mit MAINTMPFILE/DELTATMPFILE). Ohne diesen Schutz könnte +# ein Lauf mit großem Backlog länger dauern als das Intervall und sich mit +# dem nächsten Tick überlappen, was die Last-Glättung wieder aufheben würde. +# # Anpassen: Intervall/Uhrzeiten unten nach Bedarf ändern (crontab-Syntax). # Die beiden Jobs sind bewusst leicht versetzt, damit Index- und OCR-Lauf # nicht exakt zeitgleich starten. # Index-Backlog (indexed_at IS NULL) alle 15 Minuten verarbeiten -*/15 * * * * root /opt/archivmail/archivmail index-pending --config /etc/archivmail/config.yml --limit 500 >> /var/log/archivmail/index-pending.log 2>&1 +*/15 * * * * root /usr/local/bin/archivmail-index-pending.sh >> /var/log/archivmail/index-pending.log 2>&1 # OCR-Backlog (ocr_status='pending') alle 15 Minuten, um 5 Min versetzt -5,20,35,50 * * * * root /opt/archivmail/archivmail ocr-reprocess --config /etc/archivmail/config.yml --status pending --limit 500 >> /var/log/archivmail/ocr-reprocess.log 2>&1 +5,20,35,50 * * * * root /usr/local/bin/archivmail-ocr-reprocess.sh >> /var/log/archivmail/ocr-reprocess.log 2>&1 # ── Weitere Jobs (geplant, noch nicht implementiert) ──────────────────── # 30 2 * * * archivmail /opt/archivmail/archivmail reindex # nächtlicher Voll-Reindex diff --git a/deploy/cron.d/archivmail-index-pending.sh b/deploy/cron.d/archivmail-index-pending.sh new file mode 100755 index 0000000..73b6a25 --- /dev/null +++ b/deploy/cron.d/archivmail-index-pending.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# archivmail — Cron-Wrapper für 'index-pending' (PROJ-58 batch_mode). +# +# Verhindert überlappende Läufe per PID-Lockfile, analog zum mailpiler-Vorbild +# (/usr/libexec/piler/indexer.delta.sh, MAINTMPFILE/DELTATMPFILE-Muster): +# Läuft ein Cron-Job länger als das Intervall (z.B. großer Backlog), würde +# sonst ein zweiter, parallel gestarteter Lauf zusätzliche Last erzeugen statt +# sie zu glätten — genau das soll batch_mode ja vermeiden. +set -euo pipefail + +LOCKFILE=/var/run/archivmail/index-pending.lock +LOGTAG=archivmail-index-pending +mkdir -p "$(dirname "$LOCKFILE")" + +if [[ -f "$LOCKFILE" ]]; then + echo "INDEX-PENDING ERROR: bereits laufender Lauf seit $(cat "$LOCKFILE")" | logger -t "$LOGTAG" + exit 0 +fi + +date > "$LOCKFILE" +trap 'rm -f "$LOCKFILE"' EXIT + +echo "INDEX-PENDING INFO: Lauf gestartet" | logger -t "$LOGTAG" +/opt/archivmail/archivmail index-pending --config /etc/archivmail/config.yml --limit "${ARCHIVMAIL_INDEX_LIMIT:-500}" +echo "INDEX-PENDING INFO: Lauf beendet" | logger -t "$LOGTAG" diff --git a/deploy/cron.d/archivmail-ocr-reprocess.sh b/deploy/cron.d/archivmail-ocr-reprocess.sh new file mode 100755 index 0000000..d7a8bad --- /dev/null +++ b/deploy/cron.d/archivmail-ocr-reprocess.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# archivmail — Cron-Wrapper für 'ocr-reprocess --status pending' (PROJ-58 +# batch_mode). Lockfile-Schutz gegen überlappende Läufe, siehe +# archivmail-index-pending.sh für die ausführliche Begründung. +set -euo pipefail + +LOCKFILE=/var/run/archivmail/ocr-reprocess.lock +LOGTAG=archivmail-ocr-reprocess +mkdir -p "$(dirname "$LOCKFILE")" + +if [[ -f "$LOCKFILE" ]]; then + echo "OCR-REPROCESS ERROR: bereits laufender Lauf seit $(cat "$LOCKFILE")" | logger -t "$LOGTAG" + exit 0 +fi + +date > "$LOCKFILE" +trap 'rm -f "$LOCKFILE"' EXIT + +echo "OCR-REPROCESS INFO: Lauf gestartet" | logger -t "$LOGTAG" +/opt/archivmail/archivmail ocr-reprocess --config /etc/archivmail/config.yml --status pending --limit "${ARCHIVMAIL_OCR_LIMIT:-500}" +echo "OCR-REPROCESS INFO: Lauf beendet" | logger -t "$LOGTAG" diff --git a/features/PROJ-58-cron-batch-index-ocr.md b/features/PROJ-58-cron-batch-index-ocr.md index 479062d..4912aa2 100644 --- a/features/PROJ-58-cron-batch-index-ocr.md +++ b/features/PROJ-58-cron-batch-index-ocr.md @@ -59,6 +59,12 @@ Aktuell laufen Indexierung (`internal/index/tenant_worker.go`) und OCR (`interna - Minor-Finding behoben: `index-pending` fehlte in `printHelp()` (cmd_import.go) — ergänzt. - Keine Critical/High-Findings. Server 192.168.1.131 nicht angefasst während der QA. +## Nachtrag (2026-06-24): Lockfile-Schutz gegen überlappende Cron-Läufe +Nutzer wies auf mailpiler-Vorbild hin (`indexer.delta.sh`, PID-Lockfile-Muster), um zu verhindern, dass sich Cron-Läufe bei großem Backlog überlappen (würde die Last-Glättung wieder aufheben). Umgesetzt: +- Neue Wrapper-Skripte `deploy/cron.d/archivmail-index-pending.sh` und `deploy/cron.d/archivmail-ocr-reprocess.sh` — Lockfile unter `/var/run/archivmail/*.lock`, analog zu Pilers `MAINTMPFILE`/`DELTATMPFILE`-Muster (Datei mit Zeitstempel statt PID, da `trap ... EXIT` zuverlässig aufräumt; bei bereits laufendem Lauf wird einfach übersprungen statt zu warten/abzubrechen mit Fehlercode, damit Cron keine Fehlermail wegen "schon belegt" verschickt). +- `deploy/cron.d/archivmail` ruft jetzt die Wrapper-Skripte unter `/usr/local/bin/` auf statt das Binary direkt. +- **Root Cause für "Last geht nicht runter" gefunden:** `update.sh` synct `/etc/cron.d/archivmail` nie auf den Server — die PROJ-58-Cronzeilen fehlten auf 131 UND 132 komplett, obwohl `batch_mode` im Code bereits aktiv war. Daher lief weiterhin nichts batch-weise, weil der Backlog nie per Cron abgeholt wurde. Fix: `update.sh` kopiert jetzt `deploy/cron.d/archivmail` nach `/etc/cron.d/archivmail` und alle `deploy/cron.d/*.sh`-Wrapper nach `/usr/local/bin/` bei jedem Deploy. + ## Nachtrag (2026-06-24): batch_mode als Default für Neuinstallationen Auf Nutzerwunsch ist `index.batch_mode: true` und `ocr.batch_mode: true` jetzt aktiv (nicht mehr auskommentiert) in `config/config.docker.yml.example` gesetzt, damit neue Installationen direkt mit Cron-Batch statt Dauerbetrieb starten. Bestehende Installationen (wie 192.168.1.131/132) sind davon nicht betroffen, da deren `/etc/archivmail/config.yml` unabhängig vom Repo-Beispiel ist und weiterhin ohne `batch_mode`-Einträge (= Dauerbetrieb) läuft. diff --git a/update.sh b/update.sh index e6e59e7..f428fb4 100755 --- a/update.sh +++ b/update.sh @@ -160,6 +160,15 @@ cp "$BIN_DIR/archivmail" "$INSTALL_DIR/archivmail" ln -sf "$BIN_DIR/archivmail" /usr/local/bin/archivmail log "Backend eingespielt" +info "Spiele Cron-Jobs ein..." +cp "$BUILD_DIR/deploy/cron.d/archivmail" /etc/cron.d/archivmail +chmod 644 /etc/cron.d/archivmail +for script in "$BUILD_DIR"/deploy/cron.d/*.sh; do + cp "$script" "/usr/local/bin/$(basename "$script")" + chmod 755 "/usr/local/bin/$(basename "$script")" +done +log "Cron-Jobs eingespielt (/etc/cron.d/archivmail + Wrapper-Skripte)" + info "Spiele Frontend ein (standalone)..." # Next.js standalone mirrors the absolute build path — find the dir that contains server.js. # The path is typically: .next/standalone//server.js