feat: render health endpoint + test script + pipeline fixes
- GET /api/worker/health/render: checks render-worker (thumbnail_rendering queue), Blender availability via active_queues inspect, queue depth, last render recency — returns ok/degraded/down status - scripts/test_render_pipeline.py: integration test for full pipeline (--health, --sample, --full modes) - PLAN.md: appended Render Pipeline Fixes section with all B-Fixes - LEARNINGS.md: documented 5 new learnings (queue mismatch, circular import, 307 redirect, worker capability detection) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -160,6 +160,28 @@ __all__ = ["User"]
|
||||
|
||||
---
|
||||
|
||||
### 2026-03-06 | Circular Import | template_service ↔ domains/rendering/service — Render nie ausgeführt
|
||||
**Problem:** `app.services.template_service` war ein Shim der `app.domains.rendering.service` importiert. `app.domains.rendering.service` importierte wiederum `app.services.template_service` → zirkulärer Import → `resolve_template` konnte nie geladen werden → jeder Render schlug fehl mit "cannot import name 'resolve_template' from partially initialized module".
|
||||
**Ursache:** B1-Refactor hat beide Module zu Shims gemacht die aufeinander zeigen. Die eigentliche Implementierung wurde nicht in die neue Domäne übertragen.
|
||||
**Lösung:** `template_service.py` mit der Originalimplementierung aus dem git-Log wiederhergestellt (sync SQLAlchemy, Celery-sicher, 4-stufige Cascade). `domains/rendering/service.py` importiert jetzt korrekt aus `template_service` ohne Rückimport.
|
||||
**Für künftige Projekte:** Nach Refactoring immer prüfen ob Shims auf die echte Implementierung zeigen oder wieder auf andere Shims. `grep -rn "def resolve_template"` vor dem Commit muss mindestens 1 Treffer liefern.
|
||||
|
||||
---
|
||||
|
||||
### 2026-03-06 | Multi-Tenancy | audit_log.tenant_id NOT NULL blockiert alle Notifications
|
||||
**Problem:** Migration 036 machte `audit_log.tenant_id NOT NULL`, aber `emit_notification` setzt kein `tenant_id`. Die Notification-Insert schlug fehl → rollback → nachfolgende Session-Zugriffe schlugen fehl → Order-Submit gab 500 zurück.
|
||||
**Lösung:** `audit_log.tenant_id` via `ALTER TABLE audit_log ALTER COLUMN tenant_id DROP NOT NULL` nullable gemacht. Broadcast-Notifications (system-weit, kein konkreter Tenant) DÜRFEN NULL tenant_id haben.
|
||||
**Für künftige Projekte:** Audit-Logs die als Broadcast an alle Tenants gehen benötigen nullable tenant_id. Nie NOT NULL auf Tabellen setzen die auch System-Events speichern.
|
||||
|
||||
---
|
||||
|
||||
### 2026-03-06 | Frontend | GET /api/tenants gibt 307 Redirect zurück
|
||||
**Problem:** FastAPI router registriert `/tenants/` (mit trailing slash). `GET /tenants` → 307 Redirect zu `/tenants/`. Axios folgt dem Redirect aber verliert den Authorization-Header → 401 → leere Tenant-Liste im Frontend.
|
||||
**Lösung:** `getTenants()` in `api/tenants.ts` auf `/tenants/` (mit trailing slash) geändert.
|
||||
**Für künftige Projekte:** FastAPI APIRouter mit `prefix="/tenants"` und `@router.get("")` erzeugt `/tenants` (kein Slash). Mit `@router.get("/")` erzeugt `/tenants/`. Axios folgt 307 nicht mit Auth-Header. Immer trailing slash im Frontend verwenden wenn Router mit Slash registriert.
|
||||
|
||||
---
|
||||
|
||||
## Offene Fragen
|
||||
- [ ] Azure AI Credentials für Phase 4 (Bildvalidierung) noch nicht konfiguriert
|
||||
- [ ] pythonOCC verfügbar im render-worker (via cadquery dependency)? Deployment-Test ausstehend
|
||||
@@ -176,3 +198,54 @@ Blender-Renderer verarbeitet nur 1 Request gleichzeitig. Wenn worker (concurrenc
|
||||
|
||||
### 2026-03-06 | Alembic | Migration exit code 100 bei enum-Konflikt
|
||||
SQLAlchemy `Enum(create_type=False)` funktioniert nicht zuverlässig mit asyncpg. Bei bereits existierenden PostgreSQL-Enum-Typen: Raw SQL mit `DO $$ BEGIN CREATE TYPE ...; EXCEPTION WHEN duplicate_object THEN NULL; END $$;` verwenden. Für Tabellen: `CREATE TABLE IF NOT EXISTS`.
|
||||
|
||||
### 2026-03-06 | Render-Pipeline | Circular Shim blockiert alle Order-Renders
|
||||
**Problem:** `dispatch_order_line_render` → `dispatch_render` (Shim A→B→A Circular Import) → Render startet nie. Die einzige funktionierende Render-Implementierung `render_order_line_task` war nie aus dem Dispatch-Chain erreichbar.
|
||||
**Lösung:** `dispatch_order_line_render` direkt auf `render_order_line_task.delay()` umleiten. `render_dispatcher.py`-Shim ebenfalls repariert. Dispatch-Service `_legacy_dispatch` ebenfalls auf `render_order_line_task` umgeleitet.
|
||||
**Erkenntnisse:** Bei Refactoring immer prüfen ob Shims zirkulär werden. Wenn zwei Module sich gegenseitig importieren (A→B und B→A), entsteht ein Circular Import — keine echte Implementierung wird aufgerufen. Den echten Aufruf-Pfad von der API zum Task vor Refactoring dokumentieren.
|
||||
|
||||
---
|
||||
|
||||
### 2026-03-06 | Render-Pipeline | render_order_line_task auf falschem Worker (kein Blender)
|
||||
**Problem:** `render_order_line_task` war auf Queue `step_processing` → lief im `worker`-Container (Backend-Dockerfile, kein Blender). `render_to_file()` fiel still auf Pillow-Placeholder zurück. Renders scheinbar erfolgreich aber nur graue Platzhalterbilder.
|
||||
**Ursache:** `is_blender_available()` prüft `BLENDER_BIN`-Env-Var — im `worker`-Container nicht gesetzt. Fallback auf Pillow passiert lautlos ohne Exception.
|
||||
**Lösung:** `render_order_line_task` queue auf `thumbnail_rendering` geändert → läuft jetzt im `render-worker`-Container (hat Blender 5.0.1 + cadquery). `worker-thumbnail`-Service aus `docker-compose.yml` entfernt (hatte keinen Blender, blockierte aber die Queue).
|
||||
**Für künftige Projekte:** Blender-Tasks IMMER auf `thumbnail_rendering` Queue routen. `worker-thumbnail` = kein Blender, `render-worker` = hat Blender. Wenn `is_blender_available()` False zurückgibt ist der Task auf dem falschen Worker.
|
||||
|
||||
---
|
||||
|
||||
### 2026-03-06 | Docker | worker-thumbnail vs render-worker — beide auf thumbnail_rendering
|
||||
**Problem:** Sowohl `worker-thumbnail` (kein Blender) als auch `render-worker` (hat Blender) lauschten auf `thumbnail_rendering` Queue. Tasks wurden round-robin verteilt → 50% der Blender-Tasks schlugen fehl (Pillow-Fallback, kein echter Fehler).
|
||||
**Lösung:** `worker-thumbnail`-Service aus docker-compose entfernt. `render-worker` ist der alleinige Consumer von `thumbnail_rendering`. Dieser hat Blender + cadquery + alle Render-Scripts.
|
||||
**Für künftige Projekte:** Nie zwei Services mit unterschiedlichen Capabilities auf die gleiche Queue hören lassen.
|
||||
|
||||
---
|
||||
|
||||
### 2026-03-06 | Multi-Tenancy | tenant_id NOT NULL verletzt bei Order-Erstellung
|
||||
**Problem:** Migration 036 machte `tenant_id NOT NULL` auf `orders`, `order_lines`, `order_items`. Alle Create-Endpoints übergaben `tenant_id` nicht → PostgreSQL NOT NULL Constraint Violation.
|
||||
**Lösung:** Überall `tenant_id=getattr(user, 'tenant_id', None)` in Model-Konstruktoren: `orders.py` (create_order, split_order, add_line_to_order), `uploads.py` (finalize_excel).
|
||||
**Für künftige Projekte:** Nach jeder RLS-Migration alle Create-Endpoints prüfen ob das neue Pflichtfeld befüllt wird. `getattr(user, 'tenant_id', None)` als sicheres Default-Pattern verwenden.
|
||||
|
||||
### 2026-03-06 | Celery | render_order_line_task auf falscher Queue → Pillow-Fallback
|
||||
**Problem:** `render_order_line_task` war auf `step_processing` Queue → wurde von `worker`-Container bearbeitet, der kein Blender hat. `is_blender_available()` → False → Pillow-Placeholder-Bild ohne Fehlermeldung.
|
||||
**Lösung:** Queue zu `thumbnail_rendering` geändert → nur `render-worker` (mit Blender 5.0.1) verarbeitet diese Tasks.
|
||||
**Für künftige Projekte:** Nach jeder Architektur-Änderung (Container-Entfernung, Queue-Umbenennung) alle Celery-Task-Dekoratoren prüfen ob sie noch auf dem richtigen Worker laufen.
|
||||
|
||||
### 2026-03-06 | Celery | Zwei Worker auf derselben Queue mit unterschiedlichen Fähigkeiten
|
||||
**Problem:** `worker-thumbnail` und `render-worker` konkurrierten auf `thumbnail_rendering`. `worker-thumbnail` hatte kein Blender → 50% aller Render-Tasks liefen auf dem falschen Worker → Silent-Fail.
|
||||
**Lösung:** `worker-thumbnail` aus docker-compose.yml entfernt. `render-worker` ist einziger Consumer von `thumbnail_rendering`.
|
||||
**Regel:** Jede Queue sollte nur von Workers mit identischen Fähigkeiten konsumiert werden. Nie zwei Worker unterschiedlicher Ausstattung auf dieselbe Queue setzen.
|
||||
|
||||
### 2026-03-06 | Python | Circular Import via doppelte Shim-Schicht
|
||||
**Problem:** `template_service.py` importierte aus `domains/rendering/service.py`, das wiederum aus `template_service.py` importierte. Beide waren leere Shims. `resolve_template()` war nie aufrufbar → Render-Tasks crashing mit ImportError.
|
||||
**Lösung:** Volle Implementierung in `template_service.py` wiederhergestellt (aus git history). `domains/rendering/service.py` importiert nur davon — kein Rückimport.
|
||||
**Für künftige Projekte:** Shim-Layer immer auf circular imports prüfen. `domains/X/service.py` sollte entweder die echte Implementierung enthalten ODER aus einer anderen Domain importieren, aber nicht im Kreis.
|
||||
|
||||
### 2026-03-06 | FastAPI | 307-Redirect verliert Authorization-Header
|
||||
**Problem:** `GET /api/tenants` → 307 Temporary Redirect zu `/api/tenants/` (trailing slash). axios folgt dem Redirect, verliert dabei den Authorization-Header → 401 → leere Tenant-Liste im Frontend.
|
||||
**Lösung:** Frontend-API-Call auf `/tenants/` mit trailing slash geändert.
|
||||
**Für künftige Projekte:** FastAPI-Router immer mit trailing slash aufrufen oder `redirect_slashes=False` am Router setzen.
|
||||
|
||||
### 2026-03-06 | Celery Inspect | active_queues() zum Worker-Capability-Check
|
||||
**Erkenntnis:** `celery_app.control.inspect().active_queues()` gibt pro Worker zurück welche Queues er konsumiert. Damit kann man gezielt prüfen ob ein Worker mit bestimmten Fähigkeiten (z.B. `thumbnail_rendering`) connected ist — besser als Worker-Namen-Heuristiken.
|
||||
**Anwendung:** `GET /api/worker/health/render` nutzt `active_queues()` um `render_worker_connected` und `blender_available` korrekt zu bestimmen.
|
||||
|
||||
Reference in New Issue
Block a user