Klaus1: Problem mit node.js und promise-mysql

Hallo,

ich habe im Moment das Problem, dass mir irgendwann immer die verfügbaren Verbindungen zum mysql-Pool verloren gehen. D.h. das Script baut dann einfach keine mysql-Verbindungen mehr auf, oder wartet endlos darauf, dass eine wieder frei wird.

Alle meine Abfragen sind nahezu identisch aufgebaut:

		strQuery="update tabelle set wert = 1";
		pool.getConnection()
		.then(function(conn){
			connection = conn;
			var result = connection.query(strQuery);
			return result;
		}).then(function(rows){
			if (rows.changedRows == 1) {
				consolelog("update successfull");
			} else {
				consolelog('error occurred!');
			}
			connection.release();
			return;
		}).catch(function(error){
			connection.release();
			//logs out the error
			consolelog("Fehler bei: "+strQuery);
			consolelog(error);
		});	

Wie man sehen kann, gebe ich die Verbindung in jedem Fall zurück, aber trotzdem ist nach einer gewissen Laufzeit Schluss. Das Script läuft noch, es werden lediglich keine mysql-Kommandos mehr ausgeführt.

Den Pool baue ich wie folgt auf:


mysqlConnData = {
	host: "localhost",
	user: "user",
	password: "dasistganzfurchtbargeheim",
	database: "service",
	connectionLimit: 100
}

pool = mysql.createPool(mysqlConnData);

Daher meine Fragen: Wie kann ich sicherstellen, dass nicht mehr genutzte Verbindungen wirklich wieder verfügbar werden? Kann ich im Falle dessen, dass keine Verbindungen mehr verfügbar sind, wenigstens eine Fehlermeldung erzeugen oder vielleicht dann gar alle (obsoleten) Verbindungen auf einen Schlag freigeben?

Ich vermute mal, dass der Errorhandler beim getConnection nicht auslöst, weil er sich dann einfach in eine Warte-Queue einreiht?

Ich habe es auch anstelle des connection.release() mit einem

pool.releaseConnection(connection);

versucht, aber das brachte keinen Unterschied.

Würde es helfen, wenn ich in der Verbindung das QueueLimit auf z.B. 0 setzen würde? Würde dann das Errorhandling anspringen? Ich habe auch noch den Eintrag "WaitforConnections" gefunden, denn ich auf false setzen könnte oder "acquireTimeout". Würde dieser die Verbindung automatisch wieder freigeben?

LG Klaus

  1. Moin,

    ich weiß, dass ich ein ähnliches, wenn nicht sogar das selbe Problem hatte. Das ist aber auch schon wieder etwa 1 Jahr her und ich bin mir nicht mehr sicher ob ich es gelöst bekommen habe.

    Versuch mal bei jedem Query dir anzeigen zu lassen, wie viele Verbindungen gerade verfügbar sind.

    		strQuery="update tabelle set wert = 1";
    		pool.getConnection()
    		.then(function(conn){
    				console.log(pool.connections.length)
    				// so heißt glaube ich das Array wo alle Verbindungen stehen
    		});	
    

    Ansonsten mal das ganze pool Object durchsuchen.

    Deine ganzen Fragen kann ich leider nicht mehr beantworten. Aber ich habe einen Rat für dich. Falls die Datenbank nicht feststeht und noch in anderen Anwendungen gebraucht wird, lohnt sich ein wechsel auf eine andere Datenbank. MongoDB eignet sich für Node.js gerade zu perfekt. Mit einer erhöhten Performance und einem leichteren (wie ich finde) Umgang mit komplexen Datenstrukturen bietet es einige Vorteile zu MYSQL

    Gruß
    Jo

    1. Hallo Jo,

      laut Quellcode gibt es wohl die folgenden Werte zum Abfragen:

      pool.config.connectionLimit     // passed in max size of the pool
      pool._freeConnections.length    // number of free connections awaiting use
      pool._allConnections.length     // number of connections currently created, including ones in use
      pool._acquiringConnections.length // number of connections in the process of being acquired
      

      Aber das hilft mir nicht so wirklich weiter, denn ich möchte ja nicht vor jedem GetConnection manuell prüfen müssen, ob noch eine freie Connection verfügbar ist.

      Am Besten wäre, wenn ich herausfinden könnte, warum keine Verbindungen mehr verfügbar sind.

      LG Klaus

      1. Hey,

        pool.config.connectionLimit     // passed in max size of the pool
        pool._freeConnections.length    // number of free connections awaiting use
        pool._allConnections.length     // number of connections currently created, including ones in use
        pool._acquiringConnections.length // number of connections in the process of being acquired
        

        Aber das hilft mir nicht so wirklich weiter, denn ich möchte ja nicht vor jedem GetConnection manuell prüfen müssen, ob noch eine freie Connection verfügbar ist.

        Am Besten wäre, wenn ich herausfinden könnte, warum keine Verbindungen mehr verfügbar sind.

        Richtig. Aber dazu wäre es schon mal gut zu wissen ob wirklich keine Verbindungen mehr verfügbar sind oder ob das vielleicht irgendwo anders herkommt.

        Könnte nämlich auch sein das der Pool schon längst geschlossen wurde. "connectTimeout, because acquiring a pool connection does not always involve making a connection. (Default: 10000)"

        Dann müsstes du prüfen ob der Pool gerade verfügbar ist und gegebenenfalls einen neuen aufbauen.

        Gruß
        Jo

        1. Ich bin mir ziemlich sicher, dass es an der Anzahl der möglichen Verbindungen liegt. Ich habe sie mal bewusst auf nur 10 heruntergesetzt und die mysql-Abfragen wurden wesentlich früher eingestellt.

          Ich habe jetzt versucht mir die Anzahl anzeigen zu lassen, aber bekomme bei allen immer nur Scriptfehler: Cannot read property "length" of undefined.

          LG Klaus

          1. Ich bin mir ziemlich sicher, dass es an der Anzahl der möglichen Verbindungen liegt. Ich habe sie mal bewusst auf nur 10 heruntergesetzt und die mysql-Abfragen wurden wesentlich früher eingestellt.

            Ich habe jetzt versucht mir die Anzahl anzeigen zu lassen, aber bekomme bei allen immer nur Scriptfehler: Cannot read property "length" of undefined.

            Dann lass dir mal das gesamte Pool Object ausgeben und schau ob da irgendein Connection Array auffällt.

            Gruß
            Jo

  2. Tach!

    Wie man sehen kann, gebe ich die Verbindung in jedem Fall zurück,

    Wo und wie ist denn die Variable connection definiert? Ist die eindeutig einer Verbindung zugeordnet und wird auch nicht durch andere Vorgänge überschrieben?

    dedlfix.

    1. Hey,

      Wo und wie ist denn die Variable connection definiert? Ist die eindeutig einer Verbindung zugeordnet und wird auch nicht durch andere Vorgänge überschrieben?

      Natürlich, connection dürfte in der .then(function(){connection.release()}) nicht mehr die connection enthalten da es so aussieht das die global mit conn überschrieben wird. Dann müsste aber immer eine zur Verfügung stehen oder?

      Gruß
      Jo

      1. Hallo J o,

        dsa Problem ist: Wenn mehrere Connections parallel laufen, wird die Connection-Variable überschrieben. D.h. die zuerst dort abgelegte Connection wird nicht released, dafür die zuletzt abgelegte Connection aber mehrfach.

        Sofern man nicht mehrere Queries auf einer Connection hintereinander ausführen will, ist da aber alles kein Problem. Man kann die Connection mit .release() freigeben

        Die Lösung ist, das Ganze in eine function zu packen, damit connection nicht global ist sondern in einer Closure liegt. Mein Konzept - was ich aber nicht testen kann weil ich kein node mit SQL habe - wäre, dass diese function sich NUR um das Handling der Connection kümmert und ein bisschen Error-Logging macht. Ansonsten sollte sie ein Promise zurückgeben, mit dem die Fachliche Weiterverarbeitung erfolgt.

        Wenn man auf einer Connection unbedingt mehr als eine Query ausführen will, dann wird die Sache komplexer, aber bei Verwendung des Pools besteht dieser Bedarf eigentlich nicht.

        // An der Stelle wo der SQL Befehl abgesetzt werden muss
        sqlQuery("update tabelle set wert = 1")
        .then(function(rows) {
           if (rows.changedRows == 1) {
              consolelog("update successful");
           } else {
              consolelog('error occurred!');
           }
        })
        .catch(function(err) { });
        
        // -----
        
        // somewhere else
        function sqlQuery(strQuery) {
           let connection;
           return pool.getConnection()           // Das letzte Promise der Kette zurückgeben!
                  .then(function(conn)
                  {
                     let connection = conn;
                     return conn.query(strQuery);
                  })
                  .then(function(rows)
                  {
                     connection.release();
                     return rows;                // Query-Result durchreichen
                  })
                  .catch(function(error)
                  {
        			       connection.release();
                     // logs out the error
                     consolelog("Fehler bei: "+strQuery);
        			       consolelog(error);
                     throw error;                // throw, damit .catch() beim Aufrufer greift
              		})
        }
        

        Die mysql-Doku ist unklar, ob connection.release() direkt nach .query abgesetzt werden darf oder nicht. Bei dem .end() Aufruf bei ungepoolten Queries DARF man das. Das müsste man ausprobieren - damit würde man sich eine .then-Stufe sparen können.

        Unter Verwendung eines .finally-Handlers kann man die ganze Sache noch zusammenfassen, aber das muss unter node.js nicht funktionieren, ich kann es nicht testen.

        function sqlQuery(strQuery) {
           let connection;
           return pool.getConnection()           // Das letzte Promise der Kette zurückgeben!
                  .then(function(conn)
                  {
                     let connection = conn;
                     return conn.query(strQuery);
                  })
                  .catch(function(error)
                  {
                     // logs out the error
                     consolelog("Fehler bei: "+strQuery);
        			       consolelog(error);
                     throw error;                // throw, damit .catch() beim Aufrufer greift
              		})
                  .finally(function() {
                     connection.release();
                  });
        }
        

        Rolf

        --
        sumpsi - posui - clusi
        1. Hey,

          dsa Problem ist: Wenn mehrere Connections parallel laufen, wird die Connection-Variable überschrieben. D.h. die zuerst dort abgelegte Connection wird nicht released, dafür die zuletzt abgelegte Connection aber mehrfach.

          Ja das ist mir klar, mir ist es aber erst aufgefallen als dedlfix das gesagt hat. Wäre der Code davor zu sehen gewesen, hätte ich das früher sehen können. Darum war mein erster Ansatz, erstmal gucken ob das getan wird was man will/vermutet. Wenn nicht ist irgendwo ein Fehler.

          Gruß
          Jo