Skip to content

[Phase 2] Jobs nicht in DB persistiert — Result-Page bleibt auf "ausstehend" obwohl Druck erfolgreich #93

Description

@strausmann

Symptom

Nach erfolgreichem physischen Druck via POST /api/print/{slug}/batch zeigt die Hangar Result-Page den Job dauerhaft als "ausstehend". GET /api/jobs/{id} und GET /api/batches/{id} geben 404 Not Found zurück. GET /api/jobs?limit=5 liefert [].

Tatsächlich vorgefunden in der Hub-SQLite-DB:

sqlite> SELECT COUNT(*) FROM jobs;       -- 0
sqlite> SELECT COUNT(*) FROM print_batches; -- 2 (beide eben gedruckt)

Die print_batches Rows referenzieren job_ids (UUID-Array), aber diese UUIDs existieren nirgendwo in der jobs Tabelle. Druck war physisch erfolgreich (Etikett aus PT-P750W). Inkonsistenz zwischen Batch-Storage und Job-Storage.

Ursache

app/services/print_queue.py:248 (und :283 in submit_paused, :367 in _resubmit_paused):

job = Job(
    id=str(uuid.uuid4()),
    printer_id=printer_id,
    image_payload=payload,
    tape_mm=tape_mm,
    options=dict(options),
)
self._jobs[job.id] = job
await self._queues[printer_id].put(job)

Jobs werden ausschliesslich in-memory im _jobs dict des PrintQueue Singletons gehalten. Es gibt kein session.add(job) und kein await session.commit(). Die SQLite-Tabelle jobs (Spalten: id, printer_id, template_key, state, payload, result, error, created_at, updated_at, started_at, finished_at, api_key_id, source_ip) bleibt leer.

Folgen:

  1. GET /api/jobs/{id} returnt 404 — der HTTP-Handler liest aus DB, nicht aus _jobs dict
  2. Container-Restart loescht alle Jobs (history) — bekannt aus Session 26 Task ci: regular privacy audit of wiki + repo (manual + automated) #25
  3. SSE-State-Replay nicht moeglich — wenn der Client sich VERSPAETET mit dem Event-Stream verbindet (z.B. weil Hangar's Page geladen wurde nachdem der Druck schon fertig war), gibt es keinen DB-Stand zum nachholen → UI bleibt auf initialem "ausstehend"
  4. Audit-Trail unmoeglich — wer hat wann gedruckt, mit welchem Template, welcher Status?

Reproduktion

# Voraussetzung: Hub laeuft, Drucker brother-p750w konfiguriert
curl -s -X POST \
  -H "X-Label-Hub-Key: lh_pat_..." \
  -H "Content-Type: application/json" \
  -d '{"items":[{"template_id":"hangar-furniture-12mm","data":{"title":"X","primary_id":"X","qr_payload":"q"}}]}' \
  http://172.16.50.41:8000/api/print/brother-p750w/batch
# → 202 Accepted, body enthaelt {"batch_id":"...","job_ids":["<uuid>"]}

sleep 5  # Drucker druckt physisch

curl -s -H "X-Label-Hub-Key: lh_pat_..." \
  http://172.16.50.41:8000/api/jobs/<uuid>
# → 404 Not Found (obwohl Etikett physisch gedruckt wurde)

sqlite3 /data/printer-hub.db "SELECT COUNT(*) FROM jobs;"
# → 0

Loesungsweg (Vorschlag)

Phase 2.1: Jobs in DB persistieren

In submit(), submit_paused(), _resubmit_paused():

async with self._session_factory() as session:
    session.add(job)
    await session.commit()

Beim State-Transition (JobStateMachine.transition) ebenfalls session.commit() damit der aktuelle State auch nach Restart noch konsistent ist.

Phase 2.2: Worker liest aus DB (statt nur in-memory)

Beim Hub-Restart sollte der Worker queued-Jobs aus der DB lesen und ihren Pickup-State (QUEUED vs. PAUSED vs. PRINTING) sauber wiederherstellen. Faengt der Worker zwischen "ausgegeben aber nicht persistiert" Zustand ab.

Phase 2.3: Event-Replay fuer SSE

Wenn ein Client sich mit /sse/batches/{batch_id} verbindet, sollte der Hub zuerst den aktuellen Stand aus der DB ausliefern (job.state aller jobs aus der Batch), bevor er live Events anfaengt zu streamen. Damit kommt der Hangar-Result-Page auch nach Verspaetung der richtige Stand an.

Akzeptanzkriterien

  • Nach POST /api/print/{slug}/batch mit Status 202: SELECT COUNT(*) FROM jobs >= 1 (statt 0)
  • GET /api/jobs/{id} returnt korrekten Job mit aktuellem State (statt 404)
  • Nach Druck-Completion: state = COMPLETED in der DB
  • Hub-Restart wiederholt nicht erfolgreich gedruckte Jobs (Worker erkennt COMPLETED-State)
  • Hangar-Result-Page zeigt korrekt completed/error State auch wenn Page nach dem Druck geladen wird
  • TDD-Tests fuer alle drei Phasen (job persistence, worker-restart-recovery, SSE-replay)

Workaround in der Zwischenzeit

Hangar's Result-Page anzeigen, dass "Druck angestossen" reicht — keine State-Updates erwarten. SMOKE-001/SMOKE-002 sind beide physisch erfolgreich, das eigentliche Feature funktioniert. Diese Issue blockiert nur das UI-Feedback fuer Multi-Item-Batches und State-Visibility, nicht den Druck selbst.

Referenzen

Refs #88

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions