Ticketaufträge in der Warteschlange sicher abschließen

SQLiteIntermediate
Jetzt üben

Einführung

Nightjar Print Studio nimmt Workshop-Anmeldungen an und erstellt herunterladbare Eintrittskarten. Während ein Worker gestoppt ist, kann eine Anmeldung sicher warten. Der aktuelle Worker bestätigt die Zustellung jedoch zu früh. Später können Wiederholungen bereits abgeschlossener Arbeit doppelte Karten erzeugen.

Du brauchst grundlegende Terminalkenntnisse und musst eine kleine Python-Funktion, Einrückung und einen Wörterbuchzugriff lesen können. Die kurze Brücke unten wiederholt diese Elemente und erklärt SQL sowie Transaktionen. RabbitMQ, Python, die SQLite-Datenbank und die vollständige Website sind vorbereitet; du bearbeitest nur handler.py. Installation, externes Konto, Zahlung oder E-Mail sind nicht nötig.

Nutze Nightjar für echte Anmeldungs- und Kartenseiten und Workstation für Befehle. Jede Einheit beginnt unabhängig. Website und genaue Beschriftungen bleiben Englisch. Dies ist eine Worker-Unterbrechungsübung auf einer Maschine und keine Zusage von Hochverfügbarkeit oder genau einmaliger Zustellung.

Eine Anmeldung bis zur Karte verfolgen

Beginne diesen Schritt in Nightjar und lerne den normalen Geschäftsablauf kennen, bevor du den Worker änderst.

Öffne Explore the evening, lies die Workshopdetails und folge Plan your visit. Kehre mit The evening zurück. Gib unter Guest name Normal guest ein und wähle Register for the evening. Die neue Anmeldeseite zeigt eine ID wie R-0002 und Your ticket is being prepared. Eine gespeicherte Anmeldung ist noch keine ausgestellte Karte. Unter Your registrations liegt auch Mina Patels bestehende Karte; erhalte sie.

Das echte Nightjar-Anmeldeformular

Das Formular Guest name gehört zur Workshopseite; Anmeldestatus und Karte haben eigene Seiten.

Wechsle zu Workstation. pwd zeigt dein Arbeitsverzeichnis, cat eine Textdatei. Das Verzeichnis sollte /home/labex/project/ticket-desk sein:

pwd
cat handler.py

Eine Warteschlange speichert noch zu verarbeitende Nachrichten. RabbitMQ ist der echte Broker. Ein Worker empfängt die Nachricht mit Anmelde-ID und Name, schreibt die Karte und bestätigt die Zustellung. Eine Bestätigung sagt RabbitMQ, dass es diese Zustellung entfernen darf. Sie erstellt selbst keine Karte.

In Python definiert def handle(...) eine Funktion; die eingerückten Zeilen bilden ihren Körper. Der vorbereitete Worker ruft sie für jede Nachricht auf. job ist ein Wörterbuch: job['id'] und job['name'] lesen seine Werte. ch.basic_ack(delivery.delivery_tag) bestätigt diese Zustellung im empfangenden Kanal. checkpoint ist eine vorbereitete Lernpause: normalerweise tut sie nichts; eine passende Option --pause wartet auf Enter oder Ctrl+C. Sie täuscht weder Arbeit noch Broker-Ereignisse vor.

Die Datenbank enthält Tabellen, also Sammlungen von Zeilen. db.execute(...) führt SQL aus. INSERT INTO tickets... fügt eine Karte hinzu. Jedes ? erhält einen Wert aus dem folgenden Tupel, sodass Namen Daten und keine SQL-Befehle sind. with db: bestätigt die Schreibvorgänge beim normalen Ende des eingerückten Blocks oder rollt sie bei einer nach außen dringenden Ausnahme zurück. Ein Commit speichert sie dauerhaft. Der bestehende Code bestätigt vor der Arbeit; als Nächstes korrigierst du die Reihenfolge.

python3 worker.py führt den vorbereiteten Worker einmal aus: Er empfängt eine bereite Nachricht, ruft den Handler auf und endet. Starte ihn ohne Unterbrechung:

python3 worker.py

Erwarte Received R-0002 redelivered=False und Handler returned.. Zusätzliche Anmeldungen können die IDs ändern. redelivered kommt von RabbitMQ, nicht aus der Kartendatenbank. Ein normaler Lauf erstellt trotz unsicherer Reihenfolge eine echte Karte.

Kehre zu Nightjar zurück, öffne Your registrations, Normal guest und dann Open ticket. Wähle Download ticket und lies die Textdatei: ID und Name müssen übereinstimmen. Folge Back to registration. Mina Patels Karte muss ebenfalls aufgehen. Diesen Kundenweg muss deine Korrektur erhalten.

Bei No ready registrations prüfe vor einem erneuten Versuch die Liste: Ein früherer Lauf hat die Nachricht vielleicht schon verarbeitet. Bei einem falsch geschriebenen Namen kannst du den geforderten Namen erneut anmelden; jede ID bleibt unabhängig. Bearbeite keine Datenbankzeilen, um ein Ergebnis zu erzwingen.

Erst abgeschlossene Arbeit bestätigen

Beginne diesen Schritt in Workstation. Verschiebe die Bestätigung hinter die Transaktion und unterbrich einen unfertigen Auftrag, damit RabbitMQ ihn zurückgibt.

Wenn der Worker zuerst bestätigt und dann ausfällt, hat RabbitMQ keine offene Zustellung mehr zum Wiederholen. Die Bestätigung nach dem Commit lässt die Zustellung offen, bis die Karte existiert. Das schützt gegen Worker-Unterbrechungen; RabbitMQ und SQLite werden dadurch nicht zu einer gemeinsamen Transaktion.

Ersetze nur handler.py mit folgendem Block. cat > handler.py überschreibt die Datei; <<'PYTHON' beginnt ein wörtliches Here-Dokument. Füge den gesamten Block einschließlich des letzten PYTHON in einer eigenen Zeile ein. Behalte die Einrückung mit vier Leerzeichen. Die Transaktion bleibt gleich; nur die Bestätigung wandert dahinter:

cat > handler.py <<'PYTHON'
from common import checkpoint

def handle(ch, delivery, job, db, pause):
    checkpoint(pause, 'before-write')
    with db:
        db.execute('INSERT INTO tickets(registration_id,name) VALUES (?,?)',
                   (job['id'], job['name']))
    checkpoint(pause, 'after-commit')
    ch.basic_ack(delivery.delivery_tag)
PYTHON

Öffne in Nightjar The evening, melde Retry guest an und lass dessen Seite offen. Kehre zu Workstation zurück. Die Option --pause before-write hält direkt vor jedem Datenbankschreiben an:

python3 worker.py --pause before-write

Warte auf Paused before-write. und drücke im Terminal Ctrl+C. Diese absichtliche Unterbrechung endet mit Status 130. Gib unmittelbar den Status des vorherigen Befehls mit $? aus:

echo $?

Erwarte 130. Aktualisiere in Nightjar die Anmeldung Retry guest: Noch gibt es keine Karte. Starte in Workstation den Worker ohne Pause erneut:

python3 worker.py

Erwarte nun redelivered=True. RabbitMQ hat die unbestätigte Zustellung beim Schließen der vorherigen Verbindung tatsächlich zurückgegeben. Aktualisiere die Anmeldung in Nightjar, öffne die Karte und lade sie herunter. Sie gehört weiterhin zur selben ID.

Falls die Pause fehlt, prüfe die Option und cat handler.py. Hast du versehentlich Enter gedrückt, wurde der Auftrag normal abgeschlossen; melde einen weiteren Retry guest an und wiederhole die Unterbrechung. Wenn alter Code schon vor dem Abbruch bestätigt hat, zeigt python3 desk.py status die ID, und python3 desk.py retry R-0003 fordert eine weitere Zustellung dieser Anmeldung an; verwende die echte ID. Wiederholen ist eine ausdrückliche Wiederherstellungsaktion des Lernenden, niemals eine Bewertungsaktion. Wiederhole fertige Karten erst nach der Deduplizierung im nächsten Schritt.

Das Datenbankergebnis sicher wiederholbar machen

Beginne diesen Schritt in Workstation. Ein zweites Fehlerfenster bleibt: SQLite kann die Karte committen; anschließend kann der Worker vor der RabbitMQ-Bestätigung stoppen. Der aktuelle Code würde bei erneuter Zustellung eine weitere Karte erstellen.

Idempotenz bedeutet hier, dass dieselbe Anmelde-ID nach erneuter Verarbeitung nur ein Geschäftsergebnis hinterlässt. Namen identifizieren keine Anmeldungen: Zwei Personen namens Same name brauchen eigene Karten. Die vorbereitete Tabelle completed hat den Primärschlüssel id, der zwei Zeilen derselben ID verhindert.

INSERT OR IGNORE versucht, die ID einzufügen. rowcount ist bei einer neuen Zeile 1 und bei einer vorhandenen ID 0. (job['id'],) ist ein Tupel mit einem Wert; das Komma ist wichtig. if added: führt die eingerückte Karteneinfügung nur bei einer neuen ID aus. Beide Schreibvorgänge müssen im selben Block with db: bleiben. Sonst könnte ein Ausfall die Abschlusskennung ohne Karte speichern und eine berechtigte Anmeldung dauerhaft überspringen.

Ersetze die Funktion durch diese vollständige kleine Implementierung. Die Pause innerhalb der Transaktion liegt bewusst zwischen den Schreibvorgängen, damit du das Rollback beobachten kannst:

cat > handler.py <<'PYTHON'
from common import checkpoint

def handle(ch, delivery, job, db, pause):
    checkpoint(pause, 'before-write')
    with db:
        added = db.execute('INSERT OR IGNORE INTO completed VALUES (?)',
                           (job['id'],)).rowcount
        checkpoint(pause, 'inside-transaction')
        if added:
            db.execute('INSERT INTO tickets(registration_id,name) VALUES (?,?)',
                       (job['id'], job['name']))
    checkpoint(pause, 'after-commit')
    ch.basic_ack(delivery.delivery_tag)
PYTHON

Melde in Nightjar zweimal Same name an. Sie erhalten getrennte IDs, normalerweise R-0004 und R-0005. Stoppe in Workstation den ersten Auftrag innerhalb seiner Transaktion:

python3 worker.py --pause inside-transaction

Drücke bei Paused inside-transaction. Ctrl+C. Aktualisiere diese Anmeldung in Nightjar: Es gibt keine Karte, und auch die Abschlusskennung darf nicht überleben. Wiederhole in Workstation dieselbe ID, diesmal mit einer Pause nach dem Commit:

python3 worker.py --pause after-commit

Öffne bei Paused after-commit. die Anmeldung in Nightjar: Eine Karte existiert bereits. Kehre zu Workstation zurück, drücke Ctrl+C und verarbeite die unbestätigte Zustellung erneut:

python3 worker.py

Erwarte redelivered=True; nach dem Aktualisieren in Nightjar muss es weiterhin genau eine Karte geben. Die doppelte Einfügung der Abschlusskennung ergab 0, daher übersprang der Handler eine zweite Karte und bestätigte trotzdem die Zustellung.

Kehre zu Workstation zurück. --drain wiederholt denselben Einzelnachrichtenablauf, bis keine bereiten Nachrichten mehr vorliegen:

python3 worker.py --drain

Die zweite Anmeldung Same name erhält nun eine eigene Karte. desk.py status liest die echten Zeilen:

python3 desk.py status

Für einen ausdrücklichen Wiederholungstest verwende die erste angezeigte Same-name-ID. Normalerweise ist es R-0004; ersetze sie bei abweichender Reihenfolge:

python3 desk.py retry R-0004
python3 worker.py --drain

In Nightjar hat nun jede Same-name-ID eine herunterladbare Karte; Mina Patel, Normal guest und Retry guest bleiben erhalten. Keine Anmeldung darf warten. No ready registrations allein beweist das nicht: Auch verlorene Arbeit kann eine leere Schlange hinterlassen.

Bei Python-Syntax- oder Einrückungsfehlern kopiere die Funktion erneut und wiederhole; unbestätigte Arbeit kehrt zu RabbitMQ zurück. Halte beide Schreibvorgänge zusammen. Lösche keine Karten oder Abschlusskennungen, um einen Fehler zu verdecken.

Falls du eine fertige ID mit dem früheren nicht idempotenten Code verarbeitet hast, korrigiere zuerst handler.py. Die vorbereitete Aktion repair ermöglicht eine lokale Wiederherstellung ohne Zurücksetzen anderer Gäste. Finde die betroffene ID mit python3 desk.py status. Ist Normal guest beispielsweise R-0002, sieh dir die Vorschau an:

python3 desk.py repair R-0002

Sie nennt die älteste Originalkarte, die erhalten bleibt, zusätzliche zu löschende Nummern und den Abschluss für nur diese ID. Noch ändert sich nichts. --apply wendet diesen Plan ausdrücklich in einer Datenbanktransaktion an:

python3 desk.py repair R-0002 --apply

Setze die betroffene ID ein. Das Werkzeug verweigert unbekannte IDs, IDs ohne Karte sowie nicht zur Anmeldung passende Namen. Es bewahrt Nummer und Inhalt der ältesten Karte, andere Anmeldungen und deren Karten. Es markiert die reparierte ID als abgeschlossen, damit der korrigierte Handler sie sicher überspringen kann. Es konsumiert oder bestätigt niemals Nachrichten. Öffne die erhaltene Karte in Nightjar und verarbeite mit python3 worker.py --drain offene Zustellungen mit dem korrigierten Code. Eine Reparatur lässt einen unsicheren Handler die isolierten Prüfungen nicht bestehen.

Die Bewertung liest echte Kundenergebnisse und führt deinen unveränderten Handler gegen einen separaten RabbitMQ-Broker und Wegwerf-Datenbanken aus. Sie prüft Unterbrechung, echte erneute Zustellung und Rollback, ohne Live-Aufträge zu konsumieren, zu bestätigen oder zu reparieren. Das sind sicher wiederholbare Datenbankeffekte, keine Garantie genau einmaliger Zustellung. Externe E-Mails oder Zahlungen brauchen ein eigenes Konzept.

Zusammenfassung

Du hast Ticketaufträge wiederhergestellt, Zustellung von Geschäftsabschluss unterschieden und eigenständige Anmeldungen mit gleichen Namen erhalten. Eine Bestätigung nach dem Commit ermöglicht Wiederholungen; Abschlusskennung und Karte in einer Transaktion machen diese für das Datenbankergebnis sicher. Das garantiert keine genau einmalige Zustellung und deckt weder E-Mails, Zahlungen, Broker-Ausfälle noch jeden Produktionsfehler ab.