Das erste event-driven System fühlt sich wie Magie an: eine Datei landet im Bucket, eine Function feuert, ein Datensatz erscheint. Das zehnte fühlt sich wie ein Tatort an. Niemand weiß, was was auslöst, ein Duplikat hat gerade einen Kunden doppelt belastet, und eine schlechte Nachricht wiederholt sich seit Dienstag.
Event-driven auf GCP ist nicht schwer, weil die Tools schwach wären. Es entgleist, weil zwei ähnlich aussehende Dienste — Pub/Sub und Eventarc — austauschbar genutzt werden und die langweiligen Teile (Idempotenz, Dead-Letter) übersprungen werden, bis die Produktion sie lehrt.

Das ist der Feldführer: zu welchem du greifst, wie du Handler sicher hältst und wie du verhinderst, dass der Fan-out zu Spaghetti wird.
Pub/Sub vs Eventarc: einer transportiert, einer routet
Sie überlappen, also wird aus Gewohnheit gewählt. Die saubere Regel:
- Pub/Sub ist das Messaging-Rückgrat. Du publizierst Events, die dir gehören, in ein Topic; Consumer abonnieren. Du kontrollierst Schema, Ordering, Retention. Nutze es für eigene Domain-Events —
order.placed,payment.settled. - Eventarc ist ein Router darüber. Es nimmt Events, die Google bereits aussendet — Cloud-Storage-Uploads, Audit Logs, 90+ Quellen — und liefert sie als einen CloudEvents-Vertrag an ein Ziel. Nutze es, um auf die Plattform zu reagieren, ohne Glue zu schreiben.
Selten wählst du eines gegen das andere; Eventarc reitet ohnehin auf Pub/Sub. Die Frage ist „wer erzeugt dieses Event?“. Bist du es, Pub/Sub. Ist es Google, Eventarc.

# Eigenes Event → Pub/Subgcloud pubsub topics create order-placedgcloud pubsub subscriptions create fulfilment --topic=order-placed
# Ein GCP-Event (Datei hochgeladen) → Eventarc, kein Glue-Codegcloud eventarc triggers create on-upload \ --destination-run-service=thumbnailer \ --event-filters="type=google.cloud.storage.object.v1.finalized" \ --event-filters="bucket=my-uploads"At-least-once heißt: es kommt zweimal an
Beide liefern at-least-once. Das ist kein Sonderfall — plane dafür. Ein Retry, ein langsames Ack, ein Redeploy mittendrin, und dasselbe Event landet erneut. Wenn „zweimal“ eine Doppelbelastung bedeutet, ist das ein Bug, den du geschrieben hast, kein Pech.
Mache jeden Consumer idempotent: dedupliziere über eine stabile Event-ID, dann wird ein Re-Run zum No-op.
def handle(event): eid = event["id"] # stabil, von Pub/Sub/Eventarc geliefert if seen.add_if_absent(eid): # atomar: False, wenn schon vorhanden process(event) # echte Arbeit genau einmal # Duplikat → still acken, kein SeiteneffektDer Store kann Redis mit TTL, eine Unique-Spalte oder Firestore sein. Die Regel bleibt: die zweite Kopie tut nichts.
Dead-Letter: eine Gift-Nachricht darf nicht alles stoppen
Ein einzelner unparsbarer Payload wiederholt sich ewig und blockiert die Schlange dahinter. Ein Dead-Letter-Topic parkt ihn nach N Versuchen, damit echter Traffic weiterläuft.
gcloud pubsub subscriptions update fulfilment \ --dead-letter-topic=fulfilment-dead \ --max-delivery-attempts=5Dann alarmiere auf dem Dead-Letter-Topic. Eine Nachricht dort ist eine zu beantwortende Frage, kein Rauschen — aber der Rest des Flows stockt nie.
Push oder Pull: wie Subscriber ihre Events bekommen
Vor der Topologie: das Liefermodell richtig wählen. Eine Pub/Sub-Subscription ist entweder Push oder Pull, und Einsteiger wählen falsch und kämpfen dann gegen die Plattform:
- Push — Pub/Sub POSTet jede Nachricht an einen HTTPS-Endpoint (Cloud Run, Function). Kein Polling, skaliert auf null, ideal für Serverless. Der Endpoint muss schnell mit 2xx acken.
- Pull — dein Worker holt Batches und ackt sie. Am besten für Hochdurchsatz-Backends, lange Jobs oder feine Concurrency-Kontrolle.
Faustregel: Serverless-Ziel → Push; Dauer-Worker → Pull. Eventarc zu Cloud Run ist intern Push — du wählst nicht, und das ist gut so.
Ordering: meist willst du es nicht
Pub/Sub kann Reihenfolge innerhalb eines Ordering-Keys halten, aber Ordering drosselt Durchsatz und die meisten Flows brauchen es nicht. Baue Consumer so, dass die Ankunftsreihenfolge egal ist: order.placed dann order.cancelled muss in jeder Reihenfolge korrekt sein. Aktiviere Ordering-Keys nur, wenn State wirklich von Sequenz abhängt (Konto-Ledger), und keye pro Entität, nie global.
gcloud pubsub topics publish order-events --message="..." --ordering-key="acct-42"Publiziere einen Fakt, kein Blob
Ein Topic ist ein Vertrag. Events sollten versioniert, selbstbeschreibend und klein sein — IDs, keine ganzen Datensätze. Trage type, eine stabile id und ein v, damit Consumer ohne Koordination wachsen:
{ "id": "evt_9f3", "type": "order.placed", "v": 1, "orderId": "A42", "ts": "2026-06-29T10:00:00Z" }Fette Payloads koppeln Producer und Consumer; die ID lässt jeden Subscriber genau holen, was er braucht. Felder frei ergänzen, nie brechen oder umbenennen — stattdessen v erhöhen.
Wann NICHT event-driven
Events sind nicht gratis, und die schlimmste Spaghetti ist async, wo sync genügt. Greife zu einem direkten Call oder Cloud Tasks, wenn:
- Du jetzt ein Ergebnis brauchst — eine synchrone Anfrage, kein Broadcast.
- Ein bekannter Producer, ein bekannter Consumer — das ist eine Queue; nimm Cloud Tasks mit Retries und Rate-Limits, kein Topic.
- Ein Zwei-Schritt-Flow — ein direkter Call schlägt ein Topic, das niemand sonst abonniert.
Nutze Pub/Sub, wenn viele Consumer denselben Fakt brauchen und Producer nicht wissen dürfen, wer lauscht. Tut es nur einer, hast du Spaghetti umsonst gekauft.
Die Spaghetti: hör auf, Trigger blind zu verketten
Das echte Chaos sind nicht die Tools — es ist die Topologie. A feuert B, B feuert C, C feuert leise wieder A, und niemand hat die Karte gezeichnet. Drei Regeln halten es flach:
- Topic = Fakt, nicht Befehl. Publiziere
order.placed, nichtsend.email. Producer sagen, was geschah; Consumer entscheiden, was zu tun ist. Neues Verhalten = neuer Subscriber, null Producer-Änderungen. - Fan-out statt Kette. Viele Subscriber an einem Topic schlagen ein 6-Hop-Relay. Parallele Pfade sind debugbar; Ketten verstecken Zyklen.
- Eine Routing-Quelle. Halte Trigger in Terraform, nicht im Klick. Lebt der Graph nur im Kopf, ist es schon Spaghetti.
Die Feldregel
Pub/Sub, wenn du das Event erzeugst, Eventarc, wenn Google es tut. Nimm an, jedes Event kommt zweimal, und mache Handler idempotent. Dead-letter das Gift, damit eine schlechte Nachricht die Linie nicht stoppt. Publiziere Fakten, mach Fan-out statt Kette und halte Routing im Code. Das richtig gemacht, bleibt event-driven die Magie von Tag eins — statt zum Tatort von Artikel zehn zu werden.
