0

I am doing a project where I have to continuesly poll the status of a number of devices on the network (local network) and then emit the status of this to the clients on a socket.io connection.

I can get the code up and running when the connectio to the devices are good, but if one of them falls off the network I get:

RangeError: Maximum call stack size exceeded 
    at Function.isBuffer (buffer.js:398:36)
    at hasBinary (c:\Projects\Active\AL10-2.0\al10-server_sqlite\node_modules\has-binary2\index.js:42:87)
    at hasBinary (c:\Projects\Active\AL10-2.0\al10-server_sqlite\node_modules\has-binary2\index.js:56:59)
    at hasBinary (c:\Projects\Active\AL10-2.0\al10-server_sqlite\node_modules\has-binary2\index.js:56:59)
    at hasBinary (c:\Projects\Active\AL10-2.0\al10-server_sqlite\node_modules\has-binary2\index.js:56:59)
    at hasBinary (c:\Projects\Active\AL10-2.0\al10-server_sqlite\node_modules\has-binary2\index.js:56:59)
    at hasBinary (c:\Projects\Active\AL10-2.0\al10-server_sqlite\node_modules\has-binary2\index.js:56:59)
    at hasBinary (c:\Projects\Active\AL10-2.0\al10-server_sqlite\node_modules\has-binary2\index.js:56:59)
    at hasBinary (c:\Projects\Active\AL10-2.0\al10-server_sqlite\node_modules\has-binary2\index.js:56:59)
    at hasBinary (c:\Projects\Active\AL10-2.0\al10-server_sqlite\node_modules\has-binary2\index.js:56:59)

My Code looks like this:

var express = require("express");
var app = express();
var http = require("http").Server(app);
var Client = require("node-rest-client").Client;
var rest = new Client();
var io = require("socket.io")(http);
var sqlite3 = require("sqlite3");

function test(counter=0, list=[]){
    // console.log(counter);
    if(list.length == 0 || counter >= list.length){
        var query = "SELECT url FROM paths";
        db.all(query, (err,data) => {
            if(err){
                throw (err);
            }
            test(0, data);
        });
    }
    else{
        setTimeout( () => {
            GetStatus(list[counter].url, (err, restData) => {
                if(err){
                    /* As soon as one of these are added, 
                    the callstack is exceeded */
                    // setTimeout(io.sockets.emit("error", err),50);
                    // io.sockets.emit("error", err);
                    console.log(err);
                }
                else{
                    /* This one is no problem */
                    io.sockets.emit("status", restData);
                    console.log(counter + " - " + restData.HeartBeat);
                }
                // process.nextTick(test, ++counter, list);
                test(++counter, list);
            });
        },1000);
    }
}

function GetStatus(url, callback){
    var req = rest.get(url, ((data,response) => {
        if(response.statusCode == 200){
            callback(null,data);
        }
        else{
            callback(response.statusCode,null);
        }
    }).bind(this));

    req.on("error",(function(err){
        /* All of the following yeilds the same result */
        callback(err,null);
        // process.nextTick(callback,err,null);
        // setTimeout(callback(err,null),0);
    }).bind(this));
}


var db = new sqlite3.Database(":memory:", (err) => {
    if(err) return;
});

db.serialize(() => {
    var values = ["http://192.168.42.69/restapi/status","http://192.168.42.70/restapi/status"];
    db.run("CREATE TABLE paths(url text)")
    .run("INSERT INTO paths(url) VALUES (?),(?)", values, (err) => {
        if(err){
            throw err;
        }
        test();
    });
});

When I add the io.sockets.emit("error",err); (with or without the setTimeout(...)) The code fails and gives me the RangeError.

The above code is an isolated test to try to figure out why it does not work. In the real application there are a few more urls and they might change over time. In the real application, this routine will also query the database for changes in the urls every now and again...

I have attempted to implement some of the answers in this post but to no avail.

So my question is, Why does the socket.io call give my a stack overflow? And, how do I get it to work?

I am using

node.js - 8.9.4
socket.io - 2.0.3
sqlite3 - 3.1.13
node-rest-client - 3.1.0
express - 4.15.3
ugilten
  • 7
  • 3
  • Is it because of the recursive use of the test function? – Gerard H. Pille Feb 26 '18 at 10:44
  • I suspect it is, but if it runs without the io.sockets.emit() function call in the error handling, then it runs for hours and hours with no problem. It is as soon as I add that call inside the `if(err)` that it crashes.... – ugilten Feb 27 '18 at 08:08
  • It depends on how much you push onto the stack, doesn't it? Can you publish something so that your problem can be reproduced? Or would your code be enough? – Gerard H. Pille Feb 27 '18 at 08:50
  • Had to change "setTimeout(()=>{io.sockets.emit("error", err)},50);" to get it to run. – Gerard H. Pille Feb 27 '18 at 12:05
  • That did not seem to do the trick on my machine. I get the same error with the call stack size exceeded. Could it be something in the socket.io module? I don't have any clients attached to receive the socket.io messages, but I would somehow expect this error to occur in any of the other cases as well... – ugilten Feb 28 '18 at 08:29

1 Answers1

0

I just figured it out.

This post had the same error and the fix also works here.

The issue is the err object from the rest call in GetStatus(list[counter].url, (err, restData) => {...}). When sending this in as is the err object is self-referencing and causes the call stack to be flooded.

Changing this to a non-self referencing object makes it work even without the setTimeout()

ugilten
  • 7
  • 3