1
votes

Pourquoi ma requête mysql AWS Lambda node.js ne retourne-t-elle pas?

J'essaye d'écrire des données externes dans certaines tables locales. Nous allons parcourir un tableau en boucle, écrire la plupart des données de chaque élément du tableau dans la table principale et le reste dans les tables associées, en remplaçant toutes les données à chaque fois.

J'ai réduit le code à l'essentiel pour montrer le problème que j'ai. Le DELETE fonctionne bien, mais INSERT ne s'exécute qu'une seule fois, et ne revient même pas.

J'ai une capture d'écran de la sortie à https://imgur.com/a/zA6Hz8g . Dans celui-ci, vous pouvez voir que le code pour DELETE fonctionne correctement (ComQueryPacket envoyé, OkPacket renvoyé) mais quand il arrive à INSERT, le ComQueryPacket est envoyé mais rien n'est retourné. Et puis le code tombe juste.

Cela aboutit à l'écriture réussie de la première ligne, mais aucune ligne suivante n'est écrite.

J'ai essayé de changer la connexion pour utiliser des pools, mais cela n'a pas aidé non plus.

Des idées?

var mysql = require('mysql');
var promise = require('promise');

const con = mysql.createConnection({
    <connectionInfo>,
    debug: true
});

function connectToDB() {
    return new promise((resolve, reject) => {
        console.log("IN connectToDB");
        con.connect(function(err) {
            if (err) {
                console.log("ERROR: Could not connect -- " + err);
                reject;
            }
            console.log("Connected!");
            resolve();
        });
    });
}

function deleteExistingMainRow() {
    return new promise((resolve, reject) => {
        var query = "DELETE FROM my_table";
        con.query(query, [],
            function(err, result) {
                if (err) {
                    console.log("ERROR in deleteExistingMainRow: " + err);
                    reject;
                }
                else {
                    console.log("DEBUG: Successful delete of main row");
                    resolve();
                }
            });
    });
}

function writeMainRow(data_row) {
    return new promise((resolve, reject) => {
        console.log("IN writeMainRow");
        var query = 'INSERT INTO my_table SET id = ?';

        con.query(query, [data_row.id],
            function(err, result) {
                console.log("YES we tried to query");
                if (err) {
                    console.log("ERROR in writeMainRow: " + err);
                    reject(err);
                }
                else {
                    console.log("DEBUG: Successful write of main row");
                    resolve();
                }
            });
    });
}

exports.handler = function(event, context) {
    connectToDB().then(function(script) {
        deleteExistingMainRow().then(function(script) {
            var data = [{ "id": 1 }, { "id": 2 }, { "id": 3 }];
            data.forEach(data_row => {
                writeMainRow(data_row).then(function(script) {
                        console.log("DEBUG: Main row written in forEach");
                    },
                    function(err) {
                        if (err) { console.log("ERR"); } process.exit(0);
                    }());
            });
            console.log("DEBUG: Hey we're exiting now");
            con.commit;
            con.end(function(err) {
                console.log("Error on con end: " + err);
            });
            context.done(null, "DONE");
            process.exit(0);
        });
});

};


0 commentaires

3 Réponses :


2
votes

Il y a quelques mois à peine AWS a rendu l'environnement d'exécution Node.js v 8.10 disponible dans lambda.
Ce qui signifie que vous pouvez utiliser async / await et Promises .
Nous pouvons donc réorganiser le code en quelque chose comme ceci:

exports.handler = async (event, context) => {
     const dbConnection = await connectToDB();

     await deleteExistingMainRow();

     const data = [{ "id": 1 }, { "id": 2 }, { "id": 3 }];

     // use here for...of loop to keep working with async/await behaviour
     for(const data_row of data){
       await writeMainRow(data_row);
     }
}

Vous pouvez également réécrire votre code pour utiliser les fonctions natives Promises ou async / wait .
Et bien sûr, couvrez la logique du bloc try / catch , je les ai ignorés pour plus de simplicité.


7 commentaires

Bien que cela semble être une réponse raisonnable, cela n'explique pas pourquoi le code d'origine échoue. Plus précisément, vous ne devez pas appeler de code asynchrone (comme l'appel à writeMainRow) dans une boucle forEach car la boucle se termine avant que les appels async ne soient terminés et que le code se termine avant que toutes les écritures ne se produisent.


Jarmod, j'avais à l'origine le code pour writeMainRow dans la boucle, mais je trouvais que la boucle se terminait avant que toutes les lignes ne soient écrites en raison de la nature asynchrone de JS. C'est pourquoi je suis passé à la promesse. Avez-vous un exemple de comment faire ce dont j'ai besoin (écrire plusieurs enregistrements pour chaque point de données entrant) et attendre que ce soit fait avant de quitter?


@jarmod Je suis totalement d'accord avec vous, c'est pourquoi j'utilise for ... of à la place de forEach . Je l'ai mentionné dans ma réponse.


@Saul à> (écrire plusieurs enregistrements pour chaque point de données entrant) et attendre que ce soit fait avant de quitter? vous pouvez utiliser pour ... sur comme dans ma réponse


@jarmod, s'il vous plaît, regardez cette explication lavrton.com/ …


@Grynets Je ne suis pas en désaccord avec votre réponse. Je dis simplement que votre réponse dit "Pour résoudre ce problème, faites C." et ce serait encore mieux s'il disait "Votre code fait A, ce qui échoue pour la raison B. Pour résoudre ce problème, faites C."


Merci beaucoup, Grynets! En apportant vos suggestions de modifications, je vois mes résultats attendus. Je peux maintenant terminer ce projet.



0
votes

essayez d'utiliser INSERT INTO nom_table (id) VALUES (?); Je sais que votre requête et la requête ci-dessus fonctionnent de la même manière. Essayez-le.

Et assurez-vous simplement que votre boucle for fonctionne correctement en envoyant des valeurs à writeMainRow (fonction). Cela ne montrerait pas d'erreur si vous transmettez une valeur vide et assurez-vous que vous ne transmettez pas les mêmes valeurs dans la boucle for. Et je pense que vous devez passer writeMainRow (data_row.id) plutôt que writeMainRow (data_row) .

J'espère que cela vous aidera. Et une autre suggestion si vous mettez à jour plusieurs lignes, il y a des options dans la bibliothèque de nœuds mysql comme les transactions. L'utilisation de ces fonctions sera plus efficace et vous pourrez annuler le résultat si vous rencontrez une erreur. Une autre option est d'écrire des procédures, auquel cas votre serveur mysql prendra en charge le calcul.


2 commentaires

Merci Walter. J'avais VALUES à l'origine, mais cela fonctionnait de la même manière et je l'ai changé pour cela dans l'une de mes itérations pour le faire fonctionner. Et je sais que les valeurs étaient correctement envoyées parce que j'avais des commentaires que j'avais retirés qui consignaient les valeurs sur la console. De plus, j'ai besoin de data_row, pas seulement d'id, car il y a d'autres champs en cours d'écriture à partir de data_row; Je l'ai seulement réduit au strict minimum pour ce poste.


Vous con.commit () ne fait pas partie du retour promisfy. Avant que votre requête ne soit mise à jour, vous invoquez déjà la fonction con.comit (). Et je pense que con.commit () n'est pas nécessaire si vous exécutez mysql en mode de mise à jour sans échec. Une autre façon est de passer le tableau entier à la fonction writeMainRow (), pour boucler toutes les données et retourner une promesse. une fois la promesse reçue, validez la connexion, s'il y a une annulation d'erreur. J'espère que cela t'aides



2
votes

La raison pour laquelle votre code ne se comporte pas comme prévu est à cause de la nature asynchrone de NodeJS.

Votre boucle for_each génère plusieurs threads qui vont INSÉRER les données dans votre base de données.
Dès que ces threads seront lancés, le reste du code s'exécutera, en commençant par console.log ("DEBUG: Hey we exit now");

La validation se produit donc avant que tous les appels INSERT ne soient effectués et, plus important encore, vous appelez Process.exit () dans votre code. Cela met fin à l'exécution, avant même que l'INSERT puisse se terminer. Appelez callback () à la place selon https: / /docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html

La gestion de vos multiples écritures asynchrones peut être effectuée différemment. Tout d'abord, comme les grynets l'ont commenté avant moi, je suggère fortement de réécrire votre code en utilisant async / await pour rendre l'appel plus facile à lire. Ensuite, vous devez comprendre que chaque appel à writeMainRow renverra sa propre promesse et votre code doit attendre que TOUTES les promesses se terminent avant de commit () et de rappel ()

Promise.all (...) le fera pour vous. Consultez la documentation sur https: // developer. mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all


0 commentaires