mysqljs / mysql

A pure node.js JavaScript Client implementing the MySQL protocol.
MIT License
18.3k stars 2.52k forks source link

Cannot enqueue Query after invoking quit. #1296

Closed icphoton closed 8 years ago

icphoton commented 8 years ago

Hi I keep getting this error on my code and I don't know how to fix it I've tried a lot of things. I don't know but I think its a problem with connection.end() even with it I still get this error. If anyone could help me fix this that would be great.

events.js:85
      throw er; // Unhandled 'error' event
            ^
Error: Cannot enqueue Query after invoking quit.
    at Protocol._validateEnqueue (C:\Users\Iain\Steambot\node_modules\mysql\lib\protocol\Protocol.js:202:16)
    at Protocol._enqueue (C:\Users\Iain\Steambot\node_modules\mysql\lib\protocol\Protocol.js:135:13)
    at Connection.query (C:\Users\Iain\Steambot\node_modules\mysql\lib\Connection.js:201:25)
    at C:\Users\Iain\Steambot\EditedBotJS.js:139:17
    at Request._callback (C:\Users\Iain\Steambot\node_modules\steam-market-pricing\index.js:32:13)
    at Request.self.callback (C:\Users\Iain\Steambot\node_modules\request\request.js:198:22)
    at Request.emit (events.js:110:17)
    at Request.<anonymous> (C:\Users\Iain\Steambot\node_modules\request\request.js:1035:10)
    at Request.emit (events.js:129:20)
    at IncomingMessage.<anonymous> (C:\Users\Iain\Steambot\node_modules\request\request.js:962:12)

This is the code:

  connection.query('SELECT COUNT(*) AS Total FROM incomingPlayers WHERE accountID=' + steamAccountID64, function(err, rows, fields) {

    if (rows[0].Total == 0) {

      if (offer.partner.getSteamID64() === config.admin || offer.itemsToGive.length === 0) {
        console.log(steamAccountID64);
        offer.accept(function(err) {
          if (err) {
            logger.error("Unable to accept offer " + offer.id + ": " + err.message);
          } else {
            logger.info("Offer accepted");
          }
        });

        var numberofitems = offer.itemsToReceive.length;

        for (var i = 0; i < numberofitems; i++) {

          if (offer.itemsToReceive[i].appid == "730") {

            var itemInfo = {
              itemName: offer.itemsToReceive[i].market_hash_name,
              assetID: offer.itemsToReceive[i].assetid,
              iconURL: offer.itemsToReceive[i].icon_url,
              accountID: steamAccountID64
            };

            connection.query('INSERT INTO incomingOffers SET ?', itemInfo);

            market.getItemPrice(730, 'MP9 | Storm (Minimal Wear)', function(err, data) {
                    connection.query("UPDATE incomingOffers SET itemValue='" + data.lowest_price.replace('$','') + "' WHERE assetID='4361351533'");
            });

            console.log(itemInfo.itemName + " of ID:" + itemInfo.assetID + " was accepted for trade by user : " + steamAccountID64);

          } else {
            console.log("Attempted to add item of appid: " + offer.itemsToReceive[i].appid);
          };

        };

        connection.query('INSERT INTO incomingPlayers SET ?', {
          accountID: steamAccountID64,
          itemCount: numberofitems
        });

      } else {

        offer.decline(function(err) {
          if (err) {
            logger.error("Unable to decline offer " + offer.id + ": " + err.message);
          } else {
            console.log("Trade was declined from " + offer.partner.getSteamID64() + ", user is not admin or sent dodgy offer");
          };
        });
      };
    } else {
      offer.decline(function(err) {
        if (err) {
          logger.error("Unable to decline offer " + offer.id + ": " + err.message);
        } else {
          console.log("Trade was declined from " + offer.partner.getSteamID64() + ", user already went in");
        };
      });

    };

  connection.end();
  });
dougwilson commented 8 years ago

Hi! The error you provided is because you are chronologically calling connection.query() after you called connection.end(). I see that from the error, the particular call happening after the .end() call is coming from file C:\Users\Iain\Steambot\EditedBotJS.js, line 139. The code you provided is much shorter than 139 lines, so I don't know which one is line 139 to give any pointers? Would you be willing to provide your entire, complete code, or point out which one line line 139?

icphoton commented 8 years ago

Hi there! Thanks for the fast reply, this is line 139 from the code:

 connection.query("UPDATE incomingOffers SET itemValue='" + data.lowest_price.replace('$','') + "' WHERE assetID='4361351533'");

Thanks, any helpers would be much appreciated

dougwilson commented 8 years ago

Thanks for what the line is! In order to help at all, I would need to see the entire contents on the file, with the line pointed out on it, rather than just the single line out of context. Without fully understanding your code, there isn't any guidance I can provide besides the explanation I provided above as to what the error means.

icphoton commented 8 years ago
var Winston           = require('winston'); // For logging
var SteamUser         = require('steam-user'); // The heart of the bot.  We'll write the soul ourselves.
var TradeOfferManager = require('steam-tradeoffer-manager'); // Only required if you're using trade offers
var config            = require('./config.js');
var fs                = require('fs'); // For writing a dope-ass file for TradeOfferManager
var mysql             = require('mysql');
var market            = require('steam-market-pricing');

var appid = {
    TF2:   440,
    DOTA2: 570,
    CSGO:  730,
    Steam: 753
};

var contextid = {
    TF2:   2,
    DOTA2: 2,
    CSGO:  2,
    Steam: 6
};

var logger = new (Winston.Logger)({
        transports: [
            new (Winston.transports.Console)({
                colorize: true, 
                level: 'debug'
            }),
            new (Winston.transports.File)({
                level: 'info', 
                timestamp: true, 
                filename: 'cratedump.log', 
                json: false
            })
        ]
});

// Initialize the Steam client and our trading library
var client = new SteamUser();
var offers = new TradeOfferManager({
    steam:        client,
    domain:       config.domain, 
    language:     "en", // English item descriptions
    pollInterval: 10000, // (Poll every 10 seconds (10,000 ms)
    cancelTime:   300000 // Expire any outgoing trade offers that have been up for 5+ minutes (300,000 ms)
});

client.logOn({
    accountName: config.username,
    password: config.password
});

client.on('loggedOn', function (details) {
    logger.info("Logged into Steam as " + client.steamID.getSteamID64());
});

client.on('error', function (e) {
    logger.error(e);
    process.exit(1);
});

client.on('webSession', function (sessionID, cookies) {
    logger.debug("Got web session");
    // Set our status to "Online" (otherwise we always appear offline)
    client.friends.setPersonaState(SteamUser.Steam.EPersonaState.Online);
    offers.setCookies(cookies, function (err){
        if (err) {
            logger.error('Unable to set trade offer cookies: '+err);
            process.exit(1); // No point in staying up if we can't use trade offers
        }
        logger.debug("Trade offer cookies set.  Got API Key: "+offers.apiKey);
    });
});

client.on('accountLimitations', function (limited, communityBanned, locked, canInviteFriends) {
    if (limited) {
        logger.warn("Our account is limited. We cannot send friend invites, use the market, open group chat, or access the web API.");
    }
    if (communityBanned){
        logger.warn("Our account is banned from Steam Community");
    }
    if (locked){
        logger.error("Our account is locked. We cannot trade/gift/purchase items, play on VAC servers, or access Steam Community.  Shutting down.");
        process.exit(1);
    }
    if (!canInviteFriends){
        logger.warn("Our account is unable to send friend requests.");
    }
});

offers.on('newOffer', function(offer) {
  var steamAccountID64 = offer.partner.getSteamID64();

  var connection = mysql.createConnection({
    host: '***********',
    user: '***********',
    password: '**********',
    database: '*************'
  });

    connection.query('SELECT COUNT(*) AS Total FROM incomingPlayers WHERE accountID=' + steamAccountID64, function(err, rows, fields) {

    if (rows[0].Total == 0) {

      if (offer.partner.getSteamID64() === config.admin || offer.itemsToGive.length === 0) {
        console.log(steamAccountID64);
        offer.accept(function(err) {
          if (err) {
            logger.error("Unable to accept offer " + offer.id + ": " + err.message);
          } else {
            logger.info("Offer accepted");
          }
        });

        var numberofitems = offer.itemsToReceive.length;

        for (var i = 0; i < numberofitems; i++) {

          if (offer.itemsToReceive[i].appid == "730") {

            var itemInfo = {
              itemName: offer.itemsToReceive[i].market_hash_name,
              assetID: offer.itemsToReceive[i].assetid,
              iconURL: offer.itemsToReceive[i].icon_url,
              accountID: steamAccountID64
            };

            connection.query('INSERT INTO incomingOffers SET ?', itemInfo);

            market.getItemPrice(730, 'MP9 | Storm (Minimal Wear)', function(err, data) {
                    connection.query("UPDATE incomingOffers SET itemValue='" + data.lowest_price.replace('$','') + "' WHERE assetID='4361351533'");
            });

            console.log(itemInfo.itemName + " of ID:" + itemInfo.assetID + " was accepted for trade by user : " + steamAccountID64);

          } else {
            console.log("Attempted to add item of appid: " + offer.itemsToReceive[i].appid);
          };

        };

        connection.query('INSERT INTO incomingPlayers SET ?', {
          accountID: steamAccountID64,
          itemCount: numberofitems
        });

      } else {

        offer.decline(function(err) {
          if (err) {
            logger.error("Unable to decline offer " + offer.id + ": " + err.message);
          } else {
            console.log("Trade was declined from " + offer.partner.getSteamID64() + ", user is not admin or sent dodgy offer");
          };
        });
      };
    } else {
      offer.decline(function(err) {
        if (err) {
          logger.error("Unable to decline offer " + offer.id + ": " + err.message);
        } else {
          console.log("Trade was declined from " + offer.partner.getSteamID64() + ", user already went in");
        };
      });

    };

  connection.end();
  });

});
icphoton commented 8 years ago

Sorry I was a little unclear, here is the full code from the file

dougwilson commented 8 years ago

Thanks! So the issue is that in your code, line 139 you are calling the .query() method chronologically after you called the .end() method. You'll have to do a lot of refactoring to correct that (I wouldn't have enough time to rewrite your code), but that's the reason. This is because of how event loop-based systems function, as you are calling market.getItemPrice, which sets a function to run later in time, but then later down the file you call .end() right away. Then later, the market.getItemPrice callback is called and you've already ended the connection, so the inner .query() will fail.

icphoton commented 8 years ago

Do you have any ideas about how to get it to run the function when it is listed in the loop, instead of running after it has ended. Sorry for the hassle I really appreciate your time!

dougwilson commented 8 years ago

Unfortunately to rewrite your code will take a lot of time. I can always put it on my to do list and see if I can get to providing you an example within the next 3 weeks sometime. In the meantime, you may want to read up on how to write Node.js code, how event loops works, and how to manage callback code flows and timings, which would hopefully clear up the answer for you. It's understandable you don't quite know the answer, though the issue is that it's a question regarding basic Node.js programming, rather than anything that has to do with this module specifically.

I'll see when I can find time to provide an exmaple of callback temporal management sometime in the next 3 weeks, stay tuned!

icphoton commented 8 years ago

Thanks so much for the help, i'll read up on this in the meantime and hopefully come up with an alternative at the moment. I'll stay tuned!

b101 commented 8 years ago

I think the error comes up when you put your mysql db authentication stuff in an extra file, to get rid of it within every file, where you connect / query.

I just took my var connection = mysql.createConnection ({.... out of my routes and set up one file (db.js) containing the createConnection function call. I used var connection=require('./db.js'); to import it. after that I got the same error. I guess the connection kind of gets screwed up.. one time I even had Cannot enqueue Handshake after invoking quit

now I've put my createConnection call back in my routes and everything is working - I just don't like the idea that I have to copy that stuff in every route making a query to the db..

dougwilson commented 8 years ago

Hi @icphoton , I still have not yet had time to follow up, I'm sorry.

crh3675 commented 8 years ago

I see logical errors all over the code in regards to Asynchronous programming.

gorkemt commented 8 years ago

the creators of mysql need to check their code again. Once i commented out the connection.connect and connection.end functions, it started working again.

console.log('connection is created');
        //connection.connect();
        connection.query('call getcurrentitems()', function (err, rows, fields) {
            if (err) {
                //connection.end();
                next(err, null);

            } else {
                if (rows.length > 0) {
                    //connection.end();
                    console.log(rows.length);
                    console.log('The solution is: ', rows[0]);

                    next(null, rows);
                }
                else {
                    //connection.end();
                    console.log('No records found')
                }
            }

            //connection.end();
        });
sidorares commented 8 years ago

@gorkemt I think you don't understand what conection.connect() is doing. It does not create new connection, connect() method only kicks off handshake sequence. Once .end() ed the connection object must be disposed, you can't reconnect it ( just create a new one )

robertoibarra commented 5 years ago

Turns out global variables stay with the same value across different Lambda executions, since they may run in the same container, so set the value of variables inside the exports.handler function and not in the global scope. So now I put const connection = mysql.createConnection({ inside the exports.handler function and I don't get this MySQL connection errors.

More on the matter: https://forums.aws.amazon.com/thread.jspa?threadID=223230