web-dev-qa-db-fra.com

Connexions MongoDB à partir d'AWS Lambda

Je cherche à créer une API RESTful à l'aide de AWS Lambda/API Gateway connectée à une base de données MongoDB. J'ai lu que les connexions à MongoDB sont relativement coûteuses, il est donc préférable de conserver une connexion pour la réutiliser une fois qu'elle est établie plutôt que de créer de nouvelles connexions pour chaque nouvelle requête.

C'est assez simple pour les applications normales, car vous pouvez établir une connexion au démarrage et la réutiliser pendant la durée de vie des applications. Mais, puisque Lambda est conçu pour être apatride, conserver cette connexion semble être moins simple.

Par conséquent, je me demande quelle serait la meilleure façon d’aborder ce problème de connexion à la base de données? Suis-je obligé de créer de nouvelles connexions chaque fois qu'une fonction Lambda est appelée ou existe-t-il un moyen de mettre en pool/cache ces connexions pour des requêtes plus efficaces?

Merci.

47
Beesknees

Restheart est un serveur basé sur REST qui fonctionne à côté de MongoDB. Il mappe la plupart des opérations CRUD dans Mongo avec les requêtes GET, POST, etc., avec un support extensible lorsque vous devez écrire un gestionnaire personnalisé (par exemple, une requête geoNear spécialisée, geoSearch).

5
user1694845

Vous devriez supposer que lambdas est apatride, mais la réalité est que la plupart du temps, la machine virtuelle est tout simplement gelée et conserve maintient certains états. Il serait stupide pour Amazon de lancer un nouveau processus pour chaque demande afin que celles-ci réutilisent souvent le même processus. Vous pouvez en tirer parti pour éviter de nuire aux connexions.

Pour éviter toute connexion à chaque demande (dans les cas où le processus lambda est réutilisé):

  1. Ecrivez le gestionnaire en supposant que le processus est réutilisé de sorte que vous vous connectiez à la base de données et que lamba réutilise le pool de connexions (la promesse db est renvoyée de MongoClient.connect).

  2. Pour que le lambda ne soit pas suspendu, attendez que vous fermiez la connexion à la base de données, db.close(), après avoir traité une requête, indiquez-lui qu'il ne faut pas attendre une boucle d'événement vide.

Exemple:

var db = MongoClient.connect(MongoURI);

module.exports.targetingSpec = (event, context, callback) => {
  context.callbackWaitsForEmptyEventLoop = false;
  db.then((db) => {
    // use db
  });
};

De la documentation sur context.callbackWaitsForEmptyEventLoop:

callbackWaitsForEmptyEventLoop La valeur par défaut est true. Cette propriété est utile uniquement pour modifier le comportement par défaut du rappel. Par défaut, le rappel attendra que la boucle d'événements d'exécution Node.js soit vide avant de geler le processus et de renvoyer les résultats à l'appelant. Vous pouvez définir cette propriété sur false pour demander à AWS Lambda de geler le processus peu de temps après l'appel du rappel, même s'il existe des événements dans la boucle d'événements. AWS Lambda gèlera le processus, toutes les données d'état et les événements de la boucle d'événements Node.js (tous les événements restants de la boucle d'événements traités lors du prochain appel de la fonction Lambda et si AWS Lambda choisit d'utiliser le processus gelé). Pour plus d'informations sur le rappel, voir Utilisation du paramètre de rappel.

3
Tyler Brock

J'ai exécuté des tests exécutant des fonctions Java Lambda se connectant à MongoDB Atlas.

Comme déjà indiqué par d'autres affiches, Amazon réutilise les instances, mais celles-ci peuvent être recyclées et le comportement exact ne peut pas être déterminé. Donc, on pourrait se retrouver avec des connexions vides. Je collecte des données toutes les 5 minutes et les transmet à la fonction Lambda toutes les 5 minutes.

Le Lambda fait essentiellement:

  • Construire ou réutiliser la connexion
  • Interroger un enregistrement
  • Écrire ou mettre à jour un enregistrement
  • fermer la connexion ou la laisser ouverte

La quantité réelle de données est assez faible. Selon l'heure de la journée, il varie de 1 à 5 ko. Je n'ai utilisé que 128 Mo.

Les Lambdas se trouvaient à N.Virgina, car c’est là que le lien free tier est lié.

Lors de l’ouverture et de la fermeture de la connexion, la plupart des appels prennent entre 4500 et 9000 ms. Lors de la réutilisation de la connexion, la plupart des appels se situent entre 300 et 900 ms. En vérifiant la console Atlas, le nombre de connexions reste stable. Dans ce cas, la réutilisation de la connexion en vaut la peine. Établir une connexion et même se déconnecter d’un ensemble de répliques est assez coûteux avec le pilote Java.

Pour un déploiement à grande échelle, il convient d'exécuter des tests plus complets.

2
Udo Held

La réponse courte est oui, vous devez créer une nouvelle connexion ET la fermer avant la fin du lambda.

La réponse longue est en fait, lors de mes tests, vous pouvez transmettre vos connexions à la base de données dans votre gestionnaire de la manière suivante (exemple avec mysql), vous ne pouvez pas compter sur cette connexion, consultez mon exemple ci-dessous, il se peut qu'une fois que votre Lambda n'ait pas été exécuté depuis des siècles, il perd l'état du gestionnaire (démarrage à froid), j'ai besoin de faire plus de tests pour le savoir, mais j'ai remarqué si un Lambda génère beaucoup de trafic. en utilisant l'exemple ci-dessous, il ne crée pas de nouvelle connexion.

// MySQL.database.js
    import * as mysql from 'mysql'

    export default mysql.createConnection({
        Host: 'mysql db instance address',
        user: 'MYSQL_USER',
        password: 'PASSWORD',
        database: 'SOMEDB',
    })

Puis, dans votre gestionnaire, importez-le et transmettez-le au lambda en cours d'exécution.

// handler.js
import MySQL from './MySQL.database.js'

const funcHandler = (func) => {
    return (event, context, callback) => {
        func(event, context, callback, MySQL)
    }
}

const handler = {
    someHandler: funcHandler(someHandler),
}

export default handler

Maintenant, dans votre Lambda, vous faites ...

export default (event, context, callback, MySQL) => {
  context.callbackWaitsForEmptyEventLoop = false
  // Check if their is a MySQL connection if not, then open one.

 // Do ya thing, query away etc etc

  callback(null, responder.success()) 


}

L’exemple de répondeur peut-il être trouvé ici. désolé c'est ES5 parce que c'est là que la question a été posée. 

J'espère que cela t'aides!

0
Mrk Fldig

Malheureusement, vous devrez peut-être créer votre propre API RESTful pour répondre aux requêtes MongoDB jusqu'à ce que AWS en présente une. Jusqu'à présent, ils n'ont que ce dont vous avez besoin pour leur propre Dynamo DB.

0
Marin