· Daniel Schleipfer · Cloud Architecture  · 7 min read

Echtzeit-Alerts und kein Datenverlust: Das AWS-Lambda-Pattern mit eingebautem Monitoring

Wie sich mit AWS Lambda ein zuverlässiges, überwachtes System für asynchrone Verarbeitung umsetzen lässt. Das Pattern kombiniert SQS-Queues, Dead Letter Queues (DLQs) und CloudWatch-Alarms, um Datenverlust zu verhindern und in Echtzeit zu alarmieren.

Wie sich mit AWS Lambda ein zuverlässiges, überwachtes System für asynchrone Verarbeitung umsetzen lässt. Das Pattern kombiniert SQS-Queues, Dead Letter Queues (DLQs) und CloudWatch-Alarms, um Datenverlust zu verhindern und in Echtzeit zu alarmieren.

Es gibt diesen einen Morgen, an dem eine Flut von Fehlermeldungen wartet und sich herausstellt, dass die Anwendung über Nacht kritische Daten verloren hat. Wer ihn noch nicht erlebt hat, kennt zumindest den Albtraum davon.

Ein Online-Shop im saisonalen Sale ist ein gutes Beispiel: Eine Bestellverarbeitung, die still und leise fehlschlägt, ist genau das, was hier nicht passieren darf. Das vorgestellte Pattern stellt sicher, dass jede Bestellung entweder verarbeitet oder sofort zur Behebung markiert wird.

Ähnlich in ereignisgesteuerten Architekturen: Geht ein einzelnes Event verloren, kann das den gesamten Workflow stören. Mit automatisierten Alarms lassen sich Probleme erkennen, bevor sie sich durch das System fortpflanzen.

Es gibt einen unkomplizierten Weg, sich gegen dieses Risiko abzusichern. Ein Pattern, das nicht nur Datenverlust verhindert, sondern auch in Echtzeit über Probleme informiert. Es kombiniert SQS-Queues, Dead Letter Queues (DLQs) und CloudWatch-Alarms und ist das von uns am häufigsten eingesetzte Layer-3-Construct.

Im Folgenden geht es darum, asynchrone Verarbeitung ausfallsicher zu machen.

Die Ausgangslage

Asynchrone Verarbeitung mit Lambda

Asynchrone Verarbeitung mit Lambda-Funktionen ist wie eine Essensbestellung, bei der man nicht an der Tür warten muss. Man macht in der Zwischenzeit etwas anderes, während das Essen unterwegs ist. Für Performance und Skalierbarkeit ist das hervorragend. Geht allerdings etwas mit der Bestellung schief, fällt das unter Umständen erst auf, wenn der Hunger längst da ist.

Technisch gesehen warten asynchrone Invocations nicht ab, ob alles glattgegangen ist. Schlägt eine Funktion fehl, wird sie womöglich kein Retry ausführen, und das Event kann komplett verloren gehen. Schlimmer noch: Oft fällt das erst auf, wenn es bereits mehrfach passiert ist. Keine Fehler, keine Alerts. Das Event ist einfach weg.

Datenverlust beschädigt nicht nur die Integrität der Anwendung, er untergräbt auch das Vertrauen von Nutzern und Stakeholdern. Daten sind zentral, und dieser Preis ist zu hoch.

Dead Letter Queues (DLQs)

Lässt sich eine Nachricht nach einer bestimmten Anzahl von Retries nicht verarbeiten, wandert sie in die DLQ. So gehen fehlgeschlagene Nachrichten nicht ins Leere verloren. Ist die Ursache behoben, lassen sich die Nachrichten mit wenigen Klicks oder Befehlen erneut verarbeiten.

Der Haken: Eine DLQ macht von sich aus nicht auf sich aufmerksam. Ohne Monitoring bleibt sie unbemerkt im Hintergrund. Von einem Problem erfährt man nur, wenn man manuell nachsieht.

Ein Infrastruktur-Pattern als Lösung

Wie also wird aus der stillen DLQ ein zuverlässiger Frühwarnmelder? Indem sie mit CloudWatch-Alarms und SNS-Benachrichtigungen verbunden wird. Das Pattern macht aus der DLQ statt eines passiven Ablageorts ein aktives Monitoring-System.

Monitored Lambda Queue Architecture

Die Beteiligten

  • AWS-SQS-Queue: Der zuverlässige Übermittler, der dafür sorgt, dass Events in der Queue liegen und zur Verarbeitung bereitstehen.
  • Lambda-Funktion: Der Worker, der die Nachrichten aus der Queue verarbeitet.
  • CloudWatch-Alarms: Behalten die DLQ im Blick und alarmieren in Echtzeit.
  • SNS-Topic: Der Verteiler, der Benachrichtigungen an die angebundenen Kanäle weitergibt.
  • Simple Email Service: Verschickt E-Mail-Benachrichtigungen. Lässt sich durch andere Kanäle wie Slack oder SMS ersetzen oder ergänzen.

Was das Pattern leistet

  • Kein Datenverlust: Jede Nachricht ist erfasst, entweder erfolgreich verarbeitet oder zur Prüfung festgehalten.
  • Monitoring in Echtzeit: SofortigeAlerts ermöglichen es, Probleme anzugehen, bevor sie eskalieren.
  • Skalierbarkeit: Verarbeitet auch Anwendungen mit hohem Durchsatz zuverlässig.
  • Kosteneffizienz: Senkt die Kosten, die durch Datenverlust und Wiederherstellung entstehen.

Die Umsetzung

Hier das Pattern als CDK-Code, einsatzbereit. (Die vollständige Implementierung findet sich in unserem GitHub-Repository.)

1. Der Verteiler für Benachrichtigungen

Fehlerbenachrichtigungen werden über AWS SNS (Simple Notification Service) verteilt. Mit diesem Managed Service lassen sich mehrere unterschiedliche Subscriber anbinden.

const errorTopic = new Topic(this, 'ErrorNotificationTopic', {
  topicName: 'ErrorNotificationTopic',
});

2. Die Dead Letter Queue aufsetzen

Den Anfang macht das Sicherheitsnetz selbst. Eine DLQ ist im Grunde nichts anderes als eine normale SQS-Queue. Für unseren Zweck kombinieren wir sie mit einem Alarm. Dieses Construct nennen wir MonitoredDeadLetterQueue.

export class MonitoredDeadLetterQueue extends Queue {
  readonly alarm: Alarm;

  constructor(
    scope: Construct,
    id: string,
    errorTopic: ITopic,
    props: QueueProps = {
      retentionPeriod: Duration.days(14),
      encryption: QueueEncryption.SQS_MANAGED,
    }
  ) {
    super(scope, id, props);

    this.alarm = new Alarm(this, 'Alarm', {
      alarmDescription: 'There are messages in the Dead Letter Queue',
      evaluationPeriods: 1,
      threshold: 1,
      metric: this.metricApproximateNumberOfMessagesVisible(),
    });

    this.alarm.addAlarmAction(new SnsAction(errorTopic));
  }
}

3. Die Message-Queue erstellen

Als Nächstes folgt die Message-Queue, die unsere Lambda-Funktion verarbeitet.

const monitoredDeadLetterQueue = new MonitoredDeadLetterQueue(
  this,
  'MonitoredDeadLetterQueue',
  errorTopic,
);

const messageQueue = new Queue(this, `MessageQueue`, {
    queueName: `MessageQueue`,
    encryption: QueueEncryption.SQS_MANAGED,
    deadLetterQueue: {
      queue: monitoredDeadLetterQueue,
      maxReceiveCount: 3,
    },
  });

4. Die Lambda-Funktion schreiben

Diese wird später durch die Lambda-Funktion ersetzt, die überwacht und gegen Datenverlust abgesichert werden soll. Als Platzhalter dient eine einfache Funktion, die bei der Ausführung fehlschlägt. Damit lässt sich das Construct nach dem Deployment testen.

const exampleLambda = new Function(this, 'ExampleFunction', {
    runtime: Runtime.NODEJS_18_X,
    handler: 'index.handler',
    code: Code.fromInline(`
      exports.handler = async function(event) {
        throw new Error('Not today, son'); 
      }`),
  });

5. Die DLQ mit der Lambda verbinden

Die Message-Queue wird an die Lambda-Funktion angebunden, damit diese die anstehenden Nachrichten verarbeitet. Die DLQ ist an der Message-Queue konfiguriert und übernimmt fehlgeschlagene Verarbeitungen.

messageQueue.grantConsumeMessages(exampleLambda);
exampleLambda.addEventSource(new SqsEventSource(messageQueue));

6. Das finale Construct zusammensetzen

Die Message-Queue, kombiniert mit der überwachten DLQ und angebunden an die Lambda-Funktion, ergibt ein zusammengesetztes Construct. Wir nennen es MonitoredLambdaQueue.

export class MonitoredLambdaQueue extends Construct {
  constructor(
    scope: Construct,
    id: string,
    lambdaFunction: IFunction,
    errorTopic: ITopic
  ) {
    super(scope, id);

    const monitoredDeadLetterQueue = new MonitoredDeadLetterQueue(
      this,
      'MonitoredDeadLetterQueue',
      errorTopic
    );

    const messageQueue = new Queue(this, 'MessageQueue', {
      queueName: 'MessageQueue',
      encryption: QueueEncryption.SQS_MANAGED,
      deadLetterQueue: {
        queue: monitoredDeadLetterQueue,
        maxReceiveCount: 3,
      },
    });

    messageQueue.grantConsumeMessages(lambdaFunction);
    lambdaFunction.addEventSource(new SqsEventSource(messageQueue));
  }
}

7. Testen

Zeit, das Setup auf die Probe zu stellen. Wir simulieren einen Fehler in der Lambda-Funktion, um zu prüfen, ob das Monitoring funktioniert.

Stack deployen, ein paar Nachrichten auslösen und beobachten, wie der CloudWatch-Alarm über den Fehler informiert. Geschafft.

So sieht die standardmäßige E-Mail-Benachrichtigung aus.

You are receiving this email because your Amazon CloudWatch Alarm "MonitoredLambdaQueueStack-MonitoredQueueMonitoredQueueDLQAlarm32E97FE8-7tCe1ZLyO61j" in the EU (Frankfurt) region has entered the ALARM state, because "Threshold Crossed: 1 datapoint [1.0 (18/11/24 08:33:00)] was greater than or equal to the threshold (1.0)." at "Monday 18 November, 2024 08:39:18 UTC".

View this alarm in the AWS Management Console:
https://eu-central-1.console.aws.amazon.com/cloudwatch/deeplink.js?region=eu-central-1#alarmsV2:alarm/MonitoredLambdaQueueStack-MonitoredQueueMonitoredQueueDLQAlarm32E97FE8-7tCe1ZLyO61j

Alarm Details:
- Name:                       MonitoredLambdaQueueStack-MonitoredQueueMonitoredQueueDLQAlarm32E97FE8-7tCe1ZLyO61j
- Description:                There are messages in the Dead Letter Queue
- State Change:               INSUFFICIENT_DATA -> ALARM
- Reason for State Change:    Threshold Crossed: 1 datapoint [1.0 (18/11/24 08:33:00)] was greater than or equal to the threshold (1.0).
- Timestamp:                  Monday 18 November, 2024 08:39:18 UTC
- AWS Account:                012345678910
- Alarm Arn:                  arn:aws:cloudwatch:eu-central-1:012345678910:alarm:MonitoredLambdaQueueStack-MonitoredQueueMonitoredQueueDLQAlarm32E97FE8-7tCe1ZLyO61j

Threshold:
- The alarm is in the ALARM state when the metric is GreaterThanOrEqualToThreshold 1.0 for at least 1 of the last 1 period(s) of 300 seconds.

Monitored Metric:
- MetricNamespace:                     AWS/SQS
- MetricName:                          ApproximateNumberOfMessagesVisible
- Dimensions:                          [QueueName = MonitoredQueue-DLQ]
- Period:                              300 seconds
- Statistic:                           Maximum
- Unit:                                not specified

`

State Change Actions:
- OK:
- ALARM: [arn:aws:sns:eu-central-1:012345678910:DLQEmailTopic]
- INSUFFICIENT_DATA:

Das Fazit

Die MonitoredLambdaQueue ist der Schutz vor stillen Ausfällen asynchroner Lambda-Funktionen.

Warum sich das lohnt

  • Retention Period: Hält fehlgeschlagene Nachrichten für einen konfigurierbaren Zeitraum vor, sodass genug Zeit zur Analyse bleibt.
  • Verschlüsselung: Sichert die Daten über SQS-managed Encryption.
  • Anpassbare Alerts: Alarm-Aktionen lassen sich einfach an die eigenen Anforderungen anpassen.

Wiederverwendbarkeit

Das Construct lässt sich über mehrere Projekte hinweg wiederverwenden, was für Konsistenz sorgt und Entwicklungszeit spart. Ein einfaches, zuverlässiges Standard-Pattern mit hohem Mehrwert für die Architektur.

Tipps aus der Praxis

  • Retry-Logik umsetzen: Exponential Backoff und Jitter in den Lambda-Funktionen helfen, transiente Fehler sauber abzufangen.
  • Sinnvolle Thresholds setzen: Passende Schwellenwerte beugen Alert-Fatigue vor.
  • Mehrere Benachrichtigungskanäle: Verschiedene Kanäle stellen sicher, dass kein Alert übersehen wird.
  • Regelmäßig testen: Alarms in regelmäßigen Abständen prüfen, um die Funktion zu bestätigen.
  • Sauberes Error Handling: Ausführliches Logging in den Lambda-Funktionen erleichtert die Fehlersuche.
  • Sicherheit beachten: IAM-Rollen und -Policies durchsetzen, um dem Least-Privilege-Prinzip zu folgen.
  • Kosten im Blick behalten: DLQs überwachen und verarbeitete oder obsolete Nachrichten aufräumen, um Kosten gering zu halten.

Jetzt umsetzen, nicht auf den Ausfall warten

Wer dieses AWS-Lambda-Pattern in seine Architektur integriert, verhindert nicht nur Datenverlust. Es erhöht die Zuverlässigkeit und Transparenz des gesamten Systems. Echtzeit-Alerts ermöglichen schnelles Handeln und halten Anwendungen am Laufen.

Es lohnt sich, nicht auf eine Datenverlust-Katastrophe zu warten. Wer das Pattern heute umsetzt, gewinnt die Sicherheit, die Lage im Griff zu haben.

Im Gespräch bleiben

Wurde dieses Pattern schon einmal eingesetzt, oder gibt es Fragen zur Umsetzung? Erinnert es an ein eigenes, regelmäßig genutztes Pattern? Über Gedanken und Erfahrungen dazu freuen wir uns. Gerne über unser Kontaktformular oder direkt auf X.

Weiterführende Ressourcen

Back to Blog

Related Posts

View All Posts »