Rawpixel - Fotolia

Wie AWS DynamoDB Streams und Lambda-Funktionen Datenbank synchron halten

Lokalisierte Anwendungen in unterschiedlichen Regionen erfordern synchrone Datenbanken. AWS DynamoDB und Lambda können die Tabellen aktuell halten.

Amazon DynamoDB ist eine nicht-relationale, schemalose Datenbank, die sich praktisch unbegrenzt skalieren lässt. DynamoDB ist ein einfach zu nutzender Key-Value Store, der automatisch innerhalb von AWS verwaltet wird. Entwickler können DynamoDB einsetzen, um jede Durchsatzanforderung einer Anwendung zu bewältigen. Der Service ist schnell und liefert die meisten Abfragen in unter zehn Millisekunden zurück.

Doch DynamoDB ist nur schnell, wenn es lokal abgerufen werden kann. Wenn ein Entwickler lokalisierte Versionen einer Anwendung in verschiedenen Regionen bereitstellen möchte, muss er einen Weg finden, Tabellen synchron zu halten, auf die häufig zugegriffen wird. AWS Lambda und DynamoDB Streams können bei diesem Problem helfen.

DynamoDB Streams ermöglichen es Entwicklern, Ereignisse auszulösen, wenn eine DynamoDB-Tabelle geändert wird, einschließlich jede Form von Schreiboperationen. Ein Stream Event kann eine Lambda-Funktion auslösen, die ausgeführt wird, wenn die Operation läuft. Dieses Event bezieht sich auf eine Tabelle, daher ist es relativ einfach, das Event für andere Tabellen in einer anderen Region zu replizieren.

Die Tabellen in eine Richtung spiegeln

Die Datenbankspiegelung in eine Richtung (Ein-Weg-Spiegelung) ist eine Kopie eines Datensatzes, der Verfügbarkeit und Redundanz verbessert. Wenn man eine lokalisierte Anwendung einrichtet, spiegeln Entwickler eine Datenbanktabelle zu einer anderen, um die Funktionsweise der Anwendung auf einem hohen Niveau zu halten. Um eine Datenbankspiegelung in eine Richtung von einer DynamoDB-Tabelle zu einer anderen einzurichten, müssen Entwickler eine Lambda-Funktion erstellen, die Schreibzugriff auf eine DynamoDB-Tabelle in einer Region hat.

Nachdem der Entwickler eine DynamoDB-Tabelle in der Zielregion hat, erstellt er eine einfache Lambda-Funktion, die Events vom DynamoDB Stream aus der Quelltabelle zur DynamoDB-Zieltabelle kopiert. Dies erzeugt einen Ein-Weg-DynamoDB-Stream – die Funktion schreibt also nur von der Quelltabelle zur Zieltabelle, aber nicht umgekehrt. Dabei kann man sich die Zieltabelle als schreibgeschützte Kopie vorstellen. Schreibprozesse können technisch in der Zieltabelle ausgeführt werden, doch sie werden nicht in der Master-Tabelle gespeichert.

Die Funktion einrichten

Zunächst müssen Entwickler eine neue AWS IAM-Rolle (Identity Access and Management) für die benutzerdefinierte Lambda-Funktion erstellen. Anschließend verknüpft man die AWSLambdaBasicExecutionRole Policy mit der Rolle, um einen grundlegenden Lambda-Zugriff zu den CloudWatch Logs zu erlauben. Im nächsten Schritt konfiguriert man den Lesezugriff für die Quelltabelle und den Schreibzugriff für die Zieltabelle. Die Funktion erhält Zugriff zu allen DynamoDB-Tabellen, so dass sie bei Bedarf Daten zu und von den Tabellen kopieren kann.

Wenn man die Lambda-Funktion erstellt, sollte das vorgegebene Setup geändert werden. Ein DynamoDB Trigger sollte hinzugefügt und für die Tabelle konfiguriert werden. Als Ausgangsposition (starting position) stellen wir im Beispiel Trim Horizon ein (siehe Abbildung 1). Dies stellt sicher, dass die Lambda-Funktion die empfangenen Ereignisse immer in der richtigen Reihenfolge verarbeitet.

Abbildung 1: DynamoDB Streams können Lambda-Funktionen nach konfigurierter Größe (batch size) und Ausgangsposition (starting position) auslösen.

Auf der nächsten Seite verwendet man die Rolle, die im ersten Schritt erstellt wurde. Der folgende Code kann eingesetzt werden, um einen einfachen Kopiervorgang einzurichten. Dabei sollte INSERT_REGION_HERE durch die Region mit der Zieltabelle ausgetauscht werden.

var AWS = require('aws-sdk');

var dynamodb = new AWS.DynamoDB({ region: 'INSERT_REGION' });

/**

 * Handle INSERT and MODIFY event types

 */

function put(record, tableName){

    dynamodb.putItem({

        TableName: tableName,

        Item: record.NewImage,

    }, function putRecordCallback(err){

        if(err){

            console.error('ERROR Saving record', err);

        }

    });

}

/**

 * Handle a DELETE event type

 */

function remove(record, tableName){

    dynamodb.deleteItem({

        TableName: tableName,

        Key: record.Keys,

    }, function putRecordCallback(err){

        if(err){

            console.error('ERROR Deleting record', err);

        }

    });

exports.handler = function handler(event, context, callback){

    if(event && event.Records){

        event.Records.forEach(function(record){

            // Parse the table name out of the event source ARN

            var tableName = record.eventSourceARN.split('/')[1];

 

            if(record.eventName === 'MODIFY' || record.eventName === 'INSERT'){

                put(record, tableName);

            } else if(record.eventName === 'DELETE'){

                remove(record, tableName);

            } else {

                console.error('Ignoring Unkonwn Event Type:', record.eventName);

            }

        })

        callback(null, 'Processing Events');

    } else {

        console.error('ERROR', JSON.stringify(event));

        callback('Malformed event');

    }

};

Die Tabellen in zwei Richtungen spiegeln

Die Spiegelung in eine Richtung funktioniert nur für Tabellen, deren Schreiboperationen auf eine einzelne Region beschränkt sind. Das funktioniert perfekt für Stories, aber nicht so gut für Sessions oder andere schreibintensive Prozesse, die lokal beim Anwender ausgeführt werden. Webanwendungen, die häufig schreiben – und nicht nur Backend-Komponenten – profitieren von einer Zwei-Wege-Spiegelung.

Eine Zwei-Wege-Spiegelung verursacht allerdings mehrere Bedingungsprobleme. Wird ein Konto zum Beispiel in zwei verschiedenen Regionen zur gleichen Zeit bearbeitet, stellt sich die Frage: welche Operation war richtig? Es ist wichtig für diese Art der Konfiguration, dass die Zwei-Wege-Spiegelung nur eingesetzt wird, wenn ein bestimmtes Element nur aktiv in einer Region zu einem Zeitpunkt bearbeitet wird. Ist eine starke Konsistenz Voraussetzung, ist es daher ratsam, Tabellen nur in eine Richtung zu spiegeln.

Bei der Zwei-Wege-Spiegelung hat jede Zieltabelle auch ein Stream Event, dass eingerichtet wird, um Änderungsereignisse zurück zur Quelltabelle zu spiegeln. Es ist allerdings wichtig zu beachten, dass nur Events von außerhalb der originalen Synchronisationsfunktion gesendet werden können. In diesem Fall muss jedes Element eine Versionsnummer haben. Nur Ereignisse, welche die Versionsnummer erhöhen, werden zwischen den Tabellen gespeichert. Entwickler können folgenden Beispielcode verwenden, um eine Endlosschleife zwischen den Synchronisationsfunktionen zu verhindern:

dynamodb.getItem({

  TableName: tableName,

  Key: record.Keys,

}, function checkVersionNumber(err, resp){

  if(!resp || !resp.Item ||

    parseInt(record.Item.version.N) > parseInt(resp.Item.version.N)){

      // Do processing

  }

});

Zusätzlich muss für jede gespeicherte Operation innerhalb der Anwendung die Versionsnummer erhöht werden, indem DynamoDB ADD Syntax für jedes Update verwendet wird.

Geolocation Routing innerhalb von Route 53 verwenden

Um Nutzer zu ihrem nächstgelegenen Endpunkt zu führen, sollten Entwickler Amazon Route 53 verwenden. Dieser Service erlaubt es Entwicklern, Endanwender zur nächstgelegenen Anwendungsebene zu leiten.

Hierfür kann die Geolocation-Routing-Technik innerhalb von Amazon Route 53 verwendet werden. Diese lässt sich einsetzen, um Traffic zu bestimmten Endpunkten, basierend auf dem Standort des Nutzers, umzuleiten.

Weitere Optimierungen

Die hier gezeigten Funktionen behandeln nicht jeden DynamoDB-Fehler, der auftauchen kann. Entwickler können diese Fehler aber mit der neuen Promise Syntax behandeln, die Node.js 4.3 unterstützt – der letzten von AWS Lambda unterstützten Version.

Wenn mehrere Ereignisse innerhalb einer kurzen Zeitspanne am gleichen Element auftreten, kann eine Race Condition auftreten, bei der ein Event vor den anderen Prozessen stattfindet. Entwickler können die Sequenznummer (SequenceNumber) verwenden, die in jedem Datensatz verfügbar ist, um dieses Problem zu behandeln. Das stellt sicher, dass Updates nur für die Zieltabelle verarbeitet werden, wenn die Sequenznummer größer als die ist, die bereits für dieses Element verarbeitet wurde. Diese kann für das Element selbst oder in einer separaten Tabelle gespeichert werden. Wenn ein Entwickler eine Zwei-Wege-Spiegelung einrichtet, muss er die Sequenznummer in einer separaten Tabelle speichern, um eine Schleife bei Updates in einer Tabelle im Vergleich zur anderen zu verhindern.

Folgen Sie SearchEnterpriseSoftware.de auch auf Twitter, Google+, Xing und Facebook!

Artikel wurde zuletzt im Oktober 2016 aktualisiert

Pro+

Premium-Inhalte

Weitere Pro+ Premium-Inhalte und andere Mitglieder-Angebote, finden Sie hier.

Erfahren Sie mehr über Software-Entwicklung

Diskussion starten

Schicken Sie mir eine Nachricht bei Kommentaren anderer Mitglieder.

Mit dem Absenden dieser Daten erklären Sie sich bereit, E-Mails von TechTarget und seinen Partnern zu erhalten. Wenn Ihr Wohnsitz außerhalb der Vereinigten Staaten ist, geben Sie uns hiermit Ihre Erlaubnis, Ihre persönlichen Daten zu übertragen und in den Vereinigten Staaten zu verarbeiten. Datenschutz

Bitte erstellen Sie einen Usernamen, um einen Kommentar abzugeben.

- GOOGLE-ANZEIGEN

SearchSecurity.de

SearchStorage.de

SearchNetworking.de

SearchDataCenter.de

Close