2
votes

Interrogation de DB2 toutes les 15 secondes provoquant une fuite de mémoire dans NodeJS

J'ai une application qui vérifie les nouvelles entrées dans DB2 toutes les 15 secondes sur l'iSeries à l'aide de d'IBM connecteur idb . J'ai des fonctions asynchrones qui renvoient le résultat de la requête à socket.io qui émet un événement avec les données incluses au front-end. J'ai réduit la fuite de mémoire aux fonctions asynchrones. J'ai lu plusieurs articles sur les causes courantes des fuites de mémoire et comment les diagnostiquer.

MDN: gestion de la mémoire

Rising Stack: explication du ramasse-miettes a >

Marmelab: Trouver et réparer les fuites de mémoire Node.js: un guide pratique

Mais je ne vois toujours pas où est le problème. De plus, je ne parviens pas à obtenir la permission d'installer node-gyp sur le système, ce qui signifie que la plupart des outils de gestion de la mémoire sont interdits, car memwatch, heapdump et autres ont besoin de node-gyp pour être installés. Voici un exemple de la structure de base des fonctions.

    const { dbconn, dbstmt } = require('idb-connector');// require idb-connector

    async function queryDB() {
        const sSql = `SELECT * FROM LIBNAME.TABLE LIMIT 500`;

        // create new promise
        let promise = new Promise ( function(resolve, reject) {
            // create new connection 
            const connection = new dbconn();
            connection.conn("*LOCAL");
            const statement = new dbstmt(connection);
            statement.exec(sSql, (rows, err) => {
              if (err) {
                throw err;
              }
              let ticks = rows;
              statement.close();
              connection.disconn();
              connection.close();

              resolve(ticks.length);// resolve promise with varying data
            })
        });

        let result = await promise;// await promise
        return result;
    };

    async function getNewData() {
        const data = await queryDB();// get new data
        io.emit('newData', data)// push to front end
        setTimeout(getNewData, 2000);// check again in 2 seconds
    };

Des idées pour savoir où se trouve la fuite? Est-ce que j'utilise async / await de manière incorrecte? Ou bien est-ce que je crée / détruit des connexions DB de manière incorrecte? Toute aide pour comprendre pourquoi ce code fuit serait très appréciée !!

Edit: J'ai oublié de mentionner que j'ai un contrôle limité sur les processus backend car ils sont gérés par une autre équipe. Je récupère uniquement les données avec lesquelles ils peuplent la base de données et les ajoute à une page Web.

Edit 2: Je pense que je l'ai réduit aux connexions DB qui n'étaient pas nettoyées correctement. Mais, pour autant que je sache, j'ai suivi les instructions suggérées sur leur github repo a >.


1 commentaires

Où est défini io ? Ce serait bien de voir la portée supérieure de ce morceau de code pour mieux le comprendre.


4 Réponses :


2
votes

Je ne connais pas la réponse à votre question précise, mais au lieu d'émettre une requête toutes les 15 secondes, je pourrais procéder d'une manière différente. La raison étant que je n'aime généralement pas les expéditions de pêche lorsque l'environnement peut me dire qu'un événement s'est produit.

Donc, dans cette veine, vous voudrez peut-être essayer un déclencheur de base de données qui charge la clé de la ligne dans une file d'attente de données lors de l'ajout, ou même la modifier ou la supprimer si nécessaire. Ensuite, vous pouvez simplement passer un appel asynchrone pour attendre un enregistrement dans la file d'attente de données. C'est plus en temps réel et le gestionnaire d'événements n'est appelé que lorsqu'un enregistrement apparaît. Le gestionnaire peut obtenir l'enregistrement spécifique de la base de données puisque vous connaissez sa clé. Les files d'attente de données sont beaucoup plus rapides que les E / S de la base de données et placent peu de temps système sur le déclencheur.

Je vois quelques avantages potentiels avec cette méthode:

  1. Vous n'émettez pas des dizaines de requêtes susceptibles de renvoyer ou non des données.
  2. L'événement se déclencherait à l'instant où un enregistrement est ajouté à la table, plutôt que 15 secondes plus tard.
  3. Vous n'avez pas à coder pour la possibilité d'un ou plusieurs nouveaux enregistrements, ce sera toujours 1, celui mentionné dans la file d'attente de données.

3 commentaires

Connaissez-vous des modules de nœuds qui fourniraient cette fonctionnalité? Je ne connais pas un moyen d'accomplir cela dans node.


De plus, je ne suis pas en mesure de modifier le flux de travail de la base de données car il est géré par une autre équipe. Je tire uniquement les données à afficher sur une page Web.


Vous pouvez essayer node-jt400 . Vous pouvez également essayer de travailler avec cette autre équipe pour exécuter la file d'attente de données. Mais si vous avez un accès jdbc, vous pouvez créer votre propre déclencheur.



0
votes

La première chose à noter est la possibilité d'une connexion ouverte à la base de données en cas d'erreur.

if (err) {
   throw err;
}

Également en cas de succès connection.disconn (); et connection.close (); renvoie des valeurs booléennes indiquant que l'opération a réussi ( selon la documentation ) Le scénario toujours possible consiste à empiler des objets de connexion dans une bibliothèque tierce. Je les vérifierais.


1 commentaires

Bon point sur le cas d'erreur. J'ajouterai des instructions disconn et close dans cette instruction et vérifierai si ces objets s'accumulent. Merci!



1
votes

oui, vous devez fermer la connexion. Ne créez pas de données const. vous n'avez pas besoin de promesse par défaut. statement.exec est asynchrone et le gère via le résultat de retour; keep setTimeout (getNewData, 2000); // vérifier à nouveau dans 2 secondes ligne en dehors de getNewData sinon il devient une boucle infinie récursive. Exemple de code

 *async function getNewData() {
        const data = await queryDB();// get new data
        io.emit('newData', data)// push to front end
        setTimeout(getNewData, 2000);// check again in 2 seconds
    };*
change to 
   **async function getNewData() {
        const data = await queryDB();// get new data
        io.emit('newData', data)// push to front end

    };
setTimeout(getNewData, 2000);// check again in 2 seconds**

return result; });

const {dbconn, dbstmt} = require('idb-connector');

const sql = 'SELECT * FROM QIWS.QCUSTCDT';
const connection = new dbconn(); // Create a connection object.

connection.conn('*LOCAL'); // Connect to a database.

const statement = new dbstmt(dbconn); // Create a statement object of the connection.

statement.exec(sql, (result, error) => {
  if (error) {
    throw error;
  }
  console.log(`Result Set: ${JSON.stringify(result)}`);

  statement.close(); // Clean up the statement object.
  connection.disconn(); // Disconnect from the database.
  connection.close(); // Clean up the connection object.


0 commentaires

0
votes

Il a été confirmé qu'il s'agissait d'une fuite de mémoire dans la bibliothèque idb-connector que j'utilisais. Lien vers le problème Github Ici . Fondamentalement, il y avait un tableau C ++ dont la mémoire n'a jamais été libérée. Une nouvelle version a été ajoutée et le commit peut être consulté sur .


0 commentaires