Doppelte Bestellungen mit bedingten Schreibvorgängen verhindern

AWSBeginner
Jetzt üben

Einführung

Eine erneut gesendete Bestellung darf einen bestehenden Betrag nicht überschreiben. Ebenso darf eine veraltete Zahlungsaktualisierung keine neuere Version überschreiben. Du sendest tatsächliche bedingte API-Anfragen, beobachtest die Ablehnung von Duplikaten und veralteten Versionen und weist nach, dass gültige neue Bestellungen weiterhin erfolgreich sind.

Schließe zuerst „Eine DynamoDB-Tabelle für Bestellungen erstellen“ ab, einschließlich typisierter Elemente und Aktualisierungsausdrücke. Diese neue Einheit verwendet keine früheren Tabellen oder Zugangsdaten erneut. Verwende /home/labex/project, die offizielle AWS CLI und AWS View.

Bezug zu Zertifizierungen

Zertifizierung Prüfungsaufgabe Übung
Developer – Associate (DVA-C02) Aufgabe 1.3, Aufgabe 1.1 Bedingte DynamoDB-Schreibvorgänge und optimistische Nebenläufigkeit.

Lab-Überblick

Konzeptdiagramm mit AWS4-Symbolen, exportiert mit draw.io: Bedingte Schreibvorgänge prüfen den aktuellen Bestellzustand atomar, erlauben gültige Neuanlagen und Updates und weisen doppelte oder veraltete Anfragen ohne Überschreiben akzeptierter Daten zurück.

Die erste Bestellerstellung absichern

In diesem Schritt erstellst du eine Bestellung nur, wenn ihr Primärschlüssel noch unbenutzt ist. Ein bedingungsloses PutItem ersetzt das gesamte Element an diesem Schlüssel. Eine Bedingung prüft den aktuellen Zustand atomar mit dem Schreibvorgang. Eine separate Prüfung durch Lesen und anschließendes Schreiben ist deshalb unnötig und würde konkurrierende Zugriffe ermöglichen.

Verwende die vorbereitete temporäre Verbindung und erstelle eine unabhängige Tabelle mit der Bestell-ID als Partitionsschlüssel vom Typ String.

cd /home/labex/project
aws sts get-caller-identity
aws dynamodb create-table \
  --table-name labex-d04-orders \
  --attribute-definitions AttributeName=order_id,AttributeType=S \
  --key-schema AttributeName=order_id,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST
aws dynamodb wait table-exists --table-name labex-d04-orders

Schreibe eine gewöhnliche Datei für die API-Anfrage. Das Heredoc mit Anführungszeichen schreibt JSON unverändert; mit file:// kann die CLI es laden, ohne dass du umfangreiche Nutzdaten für die Shell in Anführungszeichen setzen musst. attribute_not_exists(order_id) erlaubt den Schreibvorgang nur, wenn kein Element diesen Schlüssel besitzt. Das Feld version: 1 ist ein Anwendungswert, mit dem du in Schritt 3 Aktualisierungen absicherst; DynamoDB erhöht ihn nicht automatisch.

cat > create-order.json <<'JSON'
{
  "TableName": "labex-d04-orders",
  "Item": {
    "order_id": {
      "S": "O100"
    },
    "amount": {
      "N": "145"
    },
    "status": {
      "S": "NEW"
    },
    "version": {
      "N": "1"
    }
  },
  "ConditionExpression": "attribute_not_exists(order_id)"
}
JSON
aws dynamodb put-item --cli-input-json file://create-order.json
aws dynamodb get-item \
  --table-name labex-d04-orders \
  --key '{"order_id":{"S":"O100"}}' \
  --consistent-read

O100 enthält den Betrag 145, NEW und Version 1. Die Bedingung, dass der Schlüssel noch nicht existiert, ist für diesen neuen Schlüssel erfüllt. Betrachte AWS View oben und klappe O100 auf, um das tatsächliche Element zu prüfen.

Ein Duplikat ablehnen, ohne neue Bestellungen zu blockieren

In diesem Schritt sendest du eine Bestell-ID erneut mit einem anderen Betrag. Ein ungesichertes Überschreiben würde O100 auf 999 ändern; die Bedingung muss dies ablehnen und 145 erhalten. Ein abgelehntes Duplikat ist eine Fehlerantwort und nicht automatisch eine erfolgreiche idempotente Anwendungsantwort. Die Anwendung muss entscheiden, wie sie mit diesem Fehler umgeht.

Eine wiederholte Erstellungsanfrage soll den bereits akzeptierten Bestellbetrag nicht ersetzen. Die Abwesenheitsbedingung schützt diese Entscheidung beim Schreiben. Ein Lesefilter verändert die zurückgegebenen Elemente; eine Schreibbedingung entscheidet, ob die Änderung überhaupt stattfinden darf. Jede konkurrierende Anfrage benötigt ihre eigene Bedingung.

Schreibe die konkurrierende Anfrage mit demselben Schlüssel und derselben Bedingung für dessen Nichtexistenz.

cat > duplicate-order.json <<'JSON'
{
  "TableName": "labex-d04-orders",
  "Item": {
    "order_id": {
      "S": "O100"
    },
    "amount": {
      "N": "999"
    },
    "status": {
      "S": "NEW"
    },
    "version": {
      "N": "1"
    }
  },
  "ConditionExpression": "attribute_not_exists(order_id)"
}
JSON

Wiederhole den Schreibvorgang mit demselben Schlüssel. || true lässt das Terminal nach diesem erwarteten Fehler fortfahren. Prüfe, ob ConditionalCheckFailedException erscheint; ein Verbindungs- oder Autorisierungsfehler würde eine andere Diagnose erfordern.

aws dynamodb put-item --cli-input-json file://duplicate-order.json || true
aws dynamodb get-item \
  --table-name labex-d04-orders \
  --key '{"order_id":{"S":"O100"}}' \
  --consistent-read

Erwarte ConditionalCheckFailedException und anschließend den unveränderten Betrag 145 sowie Version 1. Weise nach, dass dieselbe Absicherung eine andere Bestell-ID zulässt, statt jeden Schreibvorgang zu verweigern.

cat > second-order.json <<'JSON'
{
  "TableName": "labex-d04-orders",
  "Item": {
    "order_id": {
      "S": "O101"
    },
    "amount": {
      "N": "200"
    },
    "status": {
      "S": "NEW"
    },
    "version": {
      "N": "1"
    }
  },
  "ConditionExpression": "attribute_not_exists(order_id)"
}
JSON
aws dynamodb put-item --cli-input-json file://second-order.json
aws dynamodb get-item \
  --table-name labex-d04-orders \
  --key '{"order_id":{"S":"O101"}}' \
  --consistent-read

O101 wird mit dem Betrag 200, NEW und Version 1 erstellt. AWS View zeigt beide Bestellungen, während der Betrag von O100 bei 145 bleibt. Diese Vorgänge verhindern ein unbeabsichtigtes Überschreiben dieses Datensatzes anhand seines Schlüssels; sie implementieren weder Transaktionen über mehrere Elemente noch die Deduplizierung von Zahlungen.

Eine Aktualisierung mit der erwarteten Version absichern

In diesem Schritt führst du einen Zustandswechsel nur für die Version durch, die du gelesen hast. Eine optimistische Versionsbedingung verweigert eine veraltete Anfrage, statt eine neuere Entscheidung stillschweigend zu überschreiben. Der Schreibvorgang prüft atomar Version 1, setzt PAID und erhöht die Version auf 2.

Die erste Aktualisierung passt zu Version 1; dieselbe Erwartung schlägt bei einer Wiederholung fehl, sobald das Element Version 2 hat.

Schreibe diese UpdateItem-Anfrage und führe sie aus. Aliase für Attributnamen in Ausdrücken behandeln die Namen sicher, und typisierte Werte liefern die erwartete und die nächste Version.

cat > pay-order.json <<'JSON'
{
  "TableName": "labex-d04-orders",
  "Key": {
    "order_id": {
      "S": "O100"
    }
  },
  "UpdateExpression": "SET #state = :paid, #version = :next",
  "ConditionExpression": "#version = :expected",
  "ExpressionAttributeNames": {
    "#state": "status",
    "#version": "version"
  },
  "ExpressionAttributeValues": {
    ":paid": {
      "S": "PAID"
    },
    ":next": {
      "N": "2"
    },
    ":expected": {
      "N": "1"
    }
  },
  "ReturnValues": "ALL_NEW"
}
JSON
aws dynamodb update-item --cli-input-json file://pay-order.json

ALL_NEW zeigt PAID und Version 2 mit dem Betrag 145. Wiederhole die unveränderte Anfrage, die weiterhin Version 1 erwartet.

aws dynamodb update-item --cli-input-json file://pay-order.json || true
aws dynamodb get-item \
  --table-name labex-d04-orders \
  --key '{"order_id":{"S":"O100"}}' \
  --consistent-read

Erwarte ConditionalCheckFailedException. Der abschließende Lesevorgang zeigt weiterhin PAID, Version 2 und den Betrag 145; O101 bleibt NEW/Version 1. Ein Aufrufer im Produktivbetrieb würde den aktuellen Zustand erneut lesen und entscheiden, ob eine weitere Aktualisierung angemessen ist, statt die veraltete Erwartung endlos erneut zu versuchen.

Das tatsächliche Element behält seinen Betrag nach der abgesicherten Erstellung und dokumentiert den versionierten Zahlungswechsel.

Nur die eigene Bestelltabelle entfernen

In diesem Schritt bestätigst du den Endzustand und bereinigst die eigene Tabelle. Die gespeicherten JSON-Dateien beschreiben Anfragen; die Elemente im Dienst belegen ihre tatsächlichen Auswirkungen. Verwende weder den Befehlsverlauf noch ein Fehlerprotokoll als Nachweis, dass die Tabelle entfernt wurde.

aws dynamodb scan --table-name labex-d04-orders --consistent-read

O100 hat PAID/Version 2/Betrag 145; O101 hat NEW/Version 1/Betrag 200. Lösche nur diese Tabelle und warte, bis sie nicht mehr vorhanden ist.

aws dynamodb delete-table --table-name labex-d04-orders
aws dynamodb wait table-not-exists --table-name labex-d04-orders
aws dynamodb list-tables
aws dynamodb get-item \
  --table-name labex-d04-reference \
  --key '{"id":{"S":"platform"}}' \
  --consistent-read

Nur die Referenztabelle mit der Notiz keep unchanged bleibt erhalten. Diese erfolgreichen Abfragen unterscheiden eine Löschung von einer fehlgeschlagenen Verbindung. AWS View entfernt die Bestelltabelle. Die temporäre Verbindung endet mit dieser VM.

Zusammenfassung

Du hast die Erstellung neuer Bestellungen mit einer Bedingung für die Nichtexistenz abgesichert, die Ablehnung eines Duplikats ohne Änderung des gespeicherten Betrags beobachtet und einen weiteren neuen Schlüssel zugelassen. Anschließend hast du mit einer Bedingung für die erwartete Version einen Zustandswechsel durchgeführt und eine veraltete Wiederholung abgelehnt. Du hast die tatsächlichen abschließenden Elementwerte geprüft, bevor du ausschließlich die eigene Tabelle gelöscht hast.

Weitere Informationen bietet die AWS-Dokumentation: Bedingungsausdrücke für geschützte Schreibvorgänge.