Meteor and SQL Anywhere - Problems with NPM Package "SQLAnywhere"

Hi there,

i am really new to meteor and playing around a liitle bit. But now I need some help.

I am considering to use meteor for a project at work. Its something like a todo list with some connections to the clients data. And here is the problem. The information about the clients are stored in a SQL Anywhere database. And this connection is causing my troubles.

I searched a little bit and found the possibility to connect a meteor app with meteohacks:npm and the npm package sqlanywhere. I started testing it and gathered all the requirements for it to run on my Computer (Windows 7). I opened a simple database connection to the SQL Database and access the db just fine. At Work I would like it to be running on a linux server (Ubuntu or Debian) and the client db is on a Windows Server 2008.

Installed Ubuntu on a virtual maschine, gathered all requirements for my setup to run, including client side linux driver for SQL Anywhere 12.

I wrote this litte script to test my connections to the database. Reading data every 5 seconds from the database. On my Windows 7 PC i stopped at like 1000 successfull connections. When I start the app on the linux system. I get one successfull connection and after that an error that crashes the app. Maybe someone can help me.

My little script:

var sql = Meteor.npmRequire('sqlanywhere');
var db = sql.createConnection();
var dbp = {
    Host: '192.168.1.10:49156',
    UserId: 'user',
    Password: 'password'
};

var i = 0;

Meteor.setInterval(function(){
    console.log('Verbindung: ' + i);
    db.connect(dbp, function(err){
        if (err) {
            console.log('DB FEHLER: ' + err);
        } else {
            console.log('DB OK!');
            var sqlstate = 'SELECT * FROM adressen WHERE nr >= 9601 ORDER BY name1 ASC';
            db.exec(sqlstate, function(err, result) {
                if(err) {
                    console.log('QUERY FEHLER: ' + err);
                } else {
                    console.log('QUERY OK');
                    for(var i= 0; i<result.length;i++){
                        console.log(result[i]['nr'] + ' | ' + result[i]['name1']);
                    }
                    db.disconnect();
                }
            });
        }
    });
    i++;
},1000);

This is the error i’am getting:

W20160229-18:41:51.394(1)? (STDERR) 
W20160229-18:41:51.396(1)? (STDERR) /home/malte/test/.meteor/local/build/programs/server/packages/meteor.js:1089
W20160229-18:41:51.400(1)? (STDERR)     Fiber(runWithEnvironment).run();                                          
W20160229-18:41:51.402(1)? (STDERR)                               ^
W20160229-18:41:51.410(1)? (STDERR) Stacktrace (dead0000-dead0001) 0xc2c4ff414b1 0x21fa94604401: 
W20160229-18:41:51.410(1)? (STDERR) ==== Stack trace ============================================
W20160229-18:41:51.411(1)? (STDERR) 
W20160229-18:41:51.411(1)? (STDERR) Security context: 0xc2c4ff57281 <JS Object>#0#
W20160229-18:41:51.412(1)? (STDERR)     1: DefaultString [native runtime.js:646] (this=0xc2c4ff41bf1 <JS Object>#1#,a=0xc2c4ff414b1 <FixedArray[66]>#2#)
W20160229-18:41:51.412(1)? (STDERR)     2: ToString(aka ToString) [native runtime.js:555] (this=0xc2c4ff04121 <undefined>,a=0xc2c4ff414b1 <FixedArray[66]>#2#)
W20160229-18:41:51.413(1)? (STDERR)     3: ToStringCheckErrorObject(aka ToStringCheckErrorObject) [native messages.js:95] (this=0xc2c4ff04121 <undefined>,a=0xc2c4ff414b1 <FixedArray[66]>#2#)
W20160229-18:41:51.413(1)? (STDERR)     4: ToDetailString(aka ToDetailString) [native messages.js:110] (this=0xc2c4ff04121 <undefined>,a=0xc2c4ff414b1 <FixedArray[66]>#2#)
W20160229-18:41:51.414(1)? (STDERR)     5: FormatString(aka FormatString) [native messages.js:51] (this=0xc2c4ff04121 <undefined>,a=0x1a4d0a5781b1 <JS Array[2]>#3#,b=0x1a4d0a576101 <JSMessageObject>#4#)
W20160229-18:41:51.414(1)? (STDERR)     6: FormatMessage(aka FormatMessage) [native messages.js:287] (this=0xc2c4ff04121 <undefined>,a=0x1a4d0a576101 <JSMessageObject>#4#)
W20160229-18:41:51.414(1)? (STDERR)     7: /* anonymous */(aka /* anonymous */) [native messages.js:1169] (this=0xc2c4ff04121 <undefined>,h=0x1a4d0a571e79 <a TypeError>#5#)
W20160229-18:41:51.415(1)? (STDERR)     8: /* anonymous */ [native messages.js:777] (this=0x1a4d0a571e79 <a TypeError>#5#)
W20160229-18:41:51.415(1)? (STDERR)    12: GetPropertyWithoutInvokingMonkeyGetters(aka GetPropertyWithoutInvokingMonkeyGetters) [native messages.js:1224] (this=0xc2c4ff04121 <undefined>,a=0x1a4d0a571e79 <a TypeError>#5#,b=0x1689ff90e0d1 <String[7]: message>)
W20160229-18:41:51.415(1)? (STDERR)    13: ErrorToStringDetectCycle(aka ErrorToStringDetectCycle) [native messages.js:1233] (this=0xc2c4ff04121 <undefined>,a=0x1a4d0a571e79 <a TypeError>#5#)
W20160229-18:41:51.415(1)? (STDERR)    14: toString [native messages.js:1254] (this=0x1a4d0a571e79 <a TypeError>#5#)
W20160229-18:41:51.416(1)? (STDERR)    15: DefaultNumber [native runtime.js:636] (this=0xc2c4ff41bf1 <JS Object>#1#,a=0x1a4d0a571e79 <a TypeError>#5#)
W20160229-18:41:51.416(1)? (STDERR)    16: ToPrimitive [native runtime.js:512] (this=0xc2c4ff41bf1 <JS Object>#1#,a=0x1a4d0a571e79 <a TypeError>#5#,b=1)
W20160229-18:41:51.416(1)? (STDERR)    17: ADD [native runtime.js:169] (this=0x1a4d0a571e79 <a TypeError>#5#,a=0x1689ff904221 <String[0]: >)
W20160229-18:41:51.416(1)? (STDERR)    21: prepareStackTrace [/home/malte/.meteor/packages/meteor-tool/.1.1.10.ki0ccv++os.linux.x86_64+web.browser+web.cordova/mt-os.linux.x86_64/dev_bundle/server-lib/node_modules/source-map-support/source-map-support.js:329] (this=0xc2c4ff49079 <JS Function Error>#6#,error=0x1a4d0a571e79 <a TypeError>#5#,stack=0x1a4d0a575d81 <JS Array[0]>#7#)
W20160229-18:41:51.424(1)? (STDERR)    22: FormatRawStackTrace(aka FormatRawStackTrace) [native messages.js:1093] (this=0xc2c4ff04121 <undefined>,a=0x1a4d0a571e79 <a TypeError>#5#,b=0x1a4d0a5723f9 <JS Array[0]>#8#)
W20160229-18:41:51.424(1)? (STDERR)    23: /* anonymous */(aka /* anonymous */) [native messages.js:1123] (this=0xc2c4ff04121 <undefined>,a=0x1a4d0a571e79 <a TypeError>#5#)
W20160229-18:41:51.424(1)? (STDERR)    24: /* anonymous */ [native messages.js:777] (this=0x1a4d0a571e79 <a TypeError>#5#)
W20160229-18:41:51.424(1)? (STDERR) 
W20160229-18:41:51.433(1)? (STDERR) ==== Details ================================================
W20160229-18:41:51.433(1)? (STDERR) 
W20160229-18:41:51.433(1)? (STDERR) [1]: DefaultString [native runtime.js:646] (this=0xc2c4ff41bf1 <JS Object>#1#,a=0xc2c4ff414b1 <FixedArray[66]>#2#) {
W20160229-18:41:51.434(1)? (STDERR)   // stack-allocated locals
W20160229-18:41:51.434(1)? (STDERR)   var b = 0xc2c4ff04121 <undefined>
W20160229-18:41:51.434(1)? (STDERR)   var c = 0xc2c4ff04121 <undefined>
W20160229-18:41:51.434(1)? (STDERR)   var d = 0xc2c4ff04121 <undefined>
W20160229-18:41:51.439(1)? (STDERR)   var e = 0xc2c4ff04121 <undefined>
W20160229-18:41:51.439(1)? (STDERR)   // expression stack (top to bottom)
W20160229-18:41:51.440(1)? (STDERR)   [05] : 0x1689ff909d21 <String[8]: toString>
W20160229-18:41:51.440(1)? (STDERR)   [04] : 0xc2c4ff414b1 <FixedArray[66]>#2#
W20160229-18:41:51.440(1)? (STDERR) --------- s o u r c e   c o d e ---------
W20160229-18:41:51.440(1)? (STDERR) function DefaultString(a){?var b=a.toString;?if((%_ClassOf(b)==='Function')){?var c=%_CallFunction(a,b);?if(%IsPrimitive(c))return c;?}??var d=a.valueOf;?if((%_ClassOf(d)==='Function')){?var e=%_CallFunction(a,d);?if(%IsPrimitive(e))return e;?}??throw %MakeTypeError('cannot_convert_to_primitive',[]);?}
W20160229-18:41:51.444(1)? (STDERR) -----------------------------------------
W20160229-18:41:51.444(1)? (STDERR) }
W20160229-18:41:51.445(1)? (STDERR) 
W20160229-18:41:51.445(1)? (STDERR) [2]: ToString(aka ToString) [native runtime.js:555] (this=0xc2c4ff04121 <undefined>,a=0xc2c4ff414b1 <FixedArray[66]>#2#) {
W20160229-18:41:51.450(1)? (STDERR)   // expression stack (top to bottom)
W20160229-18:41:51.450(1)? (STDERR)   [02] : 0xc2c4ff414b1 <FixedArray[66]>#2#
W20160229-18:41:51.450(1)? (STDERR)   [01] : 0xc2c4ff41bf1 <JS Object>#1#
W20160229-18:41:51.450(1)? (STDERR)   [00] : 0xc2c4ff41bf1 <JS Object>#1#
W20160229-18:41:51.451(1)? (STDERR) --------- s o u r c e   c o d e ---------
W20160229-18:41:51.451(1)? (STDERR) function ToString(a){?if((typeof(a)==='string'))return a;?if((typeof(a)==='number'))return %_NumberToString(a);?if((typeof(a)==='boolean'))return a?'true':'false';?if((typeof(a)==='undefined'))return'undefined';?return((a===null))?'null':%ToString(%DefaultString(a));?}
W20160229-18:41:51.451(1)? (STDERR) -----------------------------------------
W20160229-18:41:51.453(1)? (STDERR) }
W20160229-18:41:51.453(1)? (STDERR) 
W20160229-18:41:51.453(1)? (STDERR) [3]: ToStringCheckErrorObject(aka ToStringCheckErrorObject) [native messages.js:95] (this=0xc2c4ff04121 <undefined>,a=0xc2c4ff414b1 <FixedArray[66]>#2#) {
W20160229-18:41:51.454(1)? (STDERR)   // expression stack (top to bottom)
W20160229-18:41:51.454(1)? (STDERR)   [01] : 0xc2c4ff414b1 <FixedArray[66]>#2#
W20160229-18:41:51.458(1)? (STDERR)   [00] : 0xc2c4ff04121 <undefined>
W20160229-18:41:51.458(1)? (STDERR) --------- s o u r c e   c o d e ---------
W20160229-18:41:51.458(1)? (STDERR) function ToStringCheckErrorObject(a){?if(IsNativeErrorObject(a)){?return %_CallFunction(a,ErrorToString);?}else{?return ToString(a);?}?}

Take a look at the Handling NPM callbacks section of the Meteor Guide. I think you’ll want to re-work your test a bit using one of the listed NPM callback workarounds.

Thank You @hwillson,

I think you’re right with the handling of the NPM callbacks. I played a little bit around with Meteor.wrapAsync and bind Environment, but having some problems to get a simple test up and running this way. How should a simple sync version of this look? Should i wrap the db.exec commend with wrapAsync and db.connect / db.disconnect with bindEnvironment?

var sql = Meteor.npmRequire('sqlanywhere');
var db = sql.createConnection();
var db_params = {
  Host: '192.168.1.10:49156',
  UserId: 'user',
  Password: 'password'
};

db.connect(db_params, function(err){
  if (err) {
    console.log(err);
  } else {
    db.exec("Some SQL Statement", function(err, result) {
      if (err) {
        console.log(err);
     } else {
        // some code ...
       
       db.disconnect(function(err){
         if (err) console.log(err);
       });
     }
  });
});

Quick example using Meteor.wrapAsync:

const dbConnect = Meteor.wrapAsync(db.connect, db);
const dbExec = Meteor.wrapAsync(db.exec, db);
const dbDisconnect = Meteor.wrapAsync(db.disconnect, db);
try {
  dbConnect(dbParams);
  dbExec('Some SQL Statement');  
  dbDisconnect();  
} catch (error) {
  console.log(error);
}

(You might not need the second parameter db set; it depends on the SQL Anywhere library; just showing as an example.)

Ok, I understand your example and give it a try and i get the following error:

Without the second parameter db I get another error.

How are you calling your code? I don’t have an SQL Anywhere instance to test against, but if I run the following I get a proper SQL anywhere “can’t initialize the database” error (since I don’t have one):

sql-anywhere-test.html:

<head>
  <title>sql-anywhere-test</title>
</head>
<body>
  <h1>SQL Anywhere Test</h1>
  <button>Click Me</button>
</body>

sql-anywhere-test.js:

if (Meteor.isClient) {
  Template.body.events({
    'click button'() {
      Meteor.call('sqlAnywhereTest');
    }
  });
}

Meteor.methods({
  sqlAnywhereTest() {
    if (!this.isSimulation) {
      const sql = Meteor.npmRequire('sqlanywhere');
      const db = sql.createConnection();
      const dbParams = {
        Host: '192.168.1.10:49156',
        UserId: 'user',
        Password: 'password'
      };
      const dbConnect = Meteor.wrapAsync(db.connect, db);
      const dbExec = Meteor.wrapAsync(db.exec, db);
      const dbDisconnect = Meteor.wrapAsync(db.disconnect, db);
      try {
        dbConnect(dbParams);
        dbExec('Some SQL Statement');
        dbDisconnect();
      } catch (error) {
        console.log(error);
      }
    }
  }
});

I just called my code at Server startup.

Now i copied your example and i works. I click on the button and no errors on the server. But when I change:

dbExec('Some SQL Statement');

to

var result = dbExec('Some SQL Statement');
console.log(result[index]['fieldname']);

then my server crashes after clicking the button. On top row you can see “Testkunde”, thats a record out of the database.

What do you see when you console.log(result)?

That would be this with same error:

So it looks like you can query the database and dump the result properly. After dumping the result, is that when you get the error? If so then it’s related to the next line of your code, which is the dbDisconnect part. If you comment out that call for now, do you still see any errors? It’s a bit difficult to troubleshoot this unless I can see your full code.

I think thats the case. I get the data from the database and after that the app crashes! I used exactly your code for this test. This time i run a test withaout the dbDisconnect line!

Here is the Code:

if (Meteor.isClient) {
  Template.body.events({
    'click button'() {
      Meteor.call('sqlAnywhereTest');
    }
  });
}

Meteor.methods({
  sqlAnywhereTest() {
    if (!this.isSimulation) {
      const sql = Meteor.npmRequire('sqlanywhere');
      const db = sql.createConnection();
      const dbParams = {
        Host: '192.168.1.10:49156',
        UserId: 'user',
        Password: 'password'
      };
      const dbConnect = Meteor.wrapAsync(db.connect, db);
      const dbExec = Meteor.wrapAsync(db.exec, db);
      const dbDisconnect = Meteor.wrapAsync(db.disconnect, db);
      try {
        dbConnect(dbParams);
        const result = dbExec('SELECT nr, name1 FROM adressen WHERE nr = 8400 ORDER BY nr');
        console.log(result);
        
        console.log('DB ERFOLGREICH GESCHLOSSEN!');
      } catch (error) {
        console.log(error);
      }
    }
  }
});

Now it is a little bit better, but if i push the button quickly the app crashes.

And i noticed something. When i first startet the test app my database wasn’t running. So when i pushed the button the app gives me an error -100 “no databse found”. Makes sense because there is no database, but if I hit the button a second time i get “no database found” error and app crashes like before.

I tried another approach. There is a npm packkage called node-odbc. Not very different to use and i testet it in our example style. Connecting to the databse is no problem, but when i try to fetch some data or close the db connection the app crashes. Seems familiar to me.

On my Windows setup sqlanywhere and odbc works just fine. So i still think its a linux problem. I found something in the readme file of my linux sql anywhere driver and i think that is the problem i ran into. Am I right?

o Alarm handling - This is of interest only if you are developing
non-threaded applications and use SIGALRM or SIGIO handlers.

SQL Anywhere uses a SIGALRM and a SIGIO handler in non-threaded
clients and starts up a repeating alarm (every 200ms). For correct behavior,
SQL Anywhere must be allowed to handle these signals.

If you define a SIGALRM or SIGIO handler before loading any SQL Anywhere
libraries, then SQL Anywhere chains to these handlers.
If you define a handler after loading any SQL Anywhere libraries,
you need to chain from the SQL Anywhere handlers.

If you use the TCP/IP communications protocol, SQL Anywhere uses
SIGIO handlers in only non-threaded clients. This handler is always
installed, but it is used only if your application makes use of TCP/IP.

EDIT:
I read a liitle bit more and found out, that i have the driver for threaded and non-threaded apps for my sql anywhere database. Now i will try something in that direction. I will come back with the results.

I got in contact with gperrow-SAP, the author of the sqlanywhere package. Very nice conversation and he found a bug with the sqlanywhere package and node 0.10.x. He fixed it and published v1.0.8 of sqlanywhere package on github and npm. And now ist working!

1 Like