node-pg-native icon indicating copy to clipboard operation
node-pg-native copied to clipboard

query function breaks Promises

Open trailsandtribulations opened this issue 10 years ago • 11 comments

Promises work great on regular pg. however, in pg-native, a resolve from a connect works fine, but resolve from query for some reason does not work at all.

here is the simplest test case ( using node --harmony --use_strict test.js):

// test.js

'use strict';

const Client = require('pg-native')

exports.testQuery = function(cl) {
  return new Promise( (resolve,reject) => {
    cl.query( 'select 1 as col', (err,rows) => {
      if( err ) return reject( err );
      console.log( 'selected '+rows[0].col );
      // resolve is called, but does not get to the then()
      resolve( rows[0].col );
    });
  });
}
exports.testing = function() {
  console.log( 'testing' );
  let client = new Client();
  client.connect( 'port = 6432 dbname = react', err => {
    if( err ) return console.log( 'conn err: '+err );
    console.log( 'connected' );
    exports.testQuery(client)
    .then( x => console.log('resolved x='+x) )
    .catch( x => console.log( 'err='+x ) ); 
   });
}

exports.testing();

trailsandtribulations avatar Apr 11 '15 15:04 trailsandtribulations

ran same test as above using prepare instead of query and got same results - the Promise's resolve() not passing to the Promise's then()

trailsandtribulations avatar Apr 12 '15 02:04 trailsandtribulations

here is the same test using pg = require('pg').native - this works fine

// test.pg.js
'use strict';
const pg = require('pg').native;
exports.testQuery = function(cl) {
  return new Promise( (resolve,reject) => {
    cl.query( 'select 1 as col', (err,result) => {
      if( err ) return reject( err );
      console.log( 'selected '+result.rows[0].col );
      resolve( result.rows[0].col );
    });
  });
}
exports.testing = function() {
  console.log( 'testing' );
  const connParams = {... };
  let client = new pg.Client( connParams );
  client.connect( err => {
    if( err ) return console.log( 'conn err: '+err );
    console.log( 'connected' );
    exports.testQuery(client)
      .then( x => { console.log('select x='+x); client.end(); } )
      .catch( err => { console.log( 'err='+err ); client.end(); } ); 
   });
}
exports.testing();

trailsandtribulations avatar Apr 12 '15 02:04 trailsandtribulations

I don't use promises - care to dive into the code a bit here & see if you can figure out what's causing the issue?

brianc avatar May 01 '15 14:05 brianc

There is something definitely wrong with the client.query function; if the client is ended after the query responds then the promise -- and the http server listen -- work. It may be a bug in node-libpq or in the c library itself. I'll try and isolate it some more.

jacott avatar Jun 02 '15 23:06 jacott

calling client.pq.stopReader is enough to stop blocking the promise and the http server. Maybe node-libpq needs to have more of the read logic in another thread?

jacott avatar Jun 02 '15 23:06 jacott

This bug* does not happen on node 0.10.38. It might be some bug in uv_poll_

* At least for the http server example in #21 since promises aren't in node 0.10

jacott avatar Jun 03 '15 01:06 jacott

for what it's worth, after reporting this, I wrote a new Promise-base node module based on node-libpq, https://github.com/trailsandtribulations/PrPq. the uv_poll code lifted from from node-pg.

it works with no problem, howbeit a bit slower than pg.

On Wed, Jun 3, 2015 at 8:14 AM, Geoff Jacobsen [email protected] wrote:

This bug* does not happen on node 0.10.38. It might be some bug in uv_poll_

  • At least for the http server example in #21 https://github.com/brianc/node-pg-native/issues/21 since promises aren't in node 0.10

— Reply to this email directly or view it on GitHub https://github.com/brianc/node-pg-native/issues/16#issuecomment-108149403 .

trailsandtribulations avatar Jun 03 '15 05:06 trailsandtribulations

more testing from my PrPq. Promises no longer work when another service, in this case nodejs-websocket, is enabled. changed consume(): instead of startReader() messaging now using setTimeout() polling. works successfully. heavy load benchmark about the same.

trailsandtribulations avatar Jun 05 '15 04:06 trailsandtribulations

Hmmm...I definitely wouldn't recommend using a setTimeout instead of startReader(). startReader() actually listens on the socket with libuv and gets alerted when the socket actual has data on it. setTimeout is a pretty epic hack....akin to reading of an http server's net socket with setTimeout or something.

Question...does this bug manifest with other versions of promises? Like bluebird or Q or whatever? Not very familiar w/ them but seems strange this hasn't been encountered before.

@jacott are you saying this prevents an http server from binding as well? Do you have an example of this causing an issue without promises?

brianc avatar Jun 05 '15 13:06 brianc

@brianc see issue #21 for an example

jacott avatar Jun 05 '15 18:06 jacott

@brianc - have not tried with other Promise systems - running node --harmony --use_strict. assume that, in the end, this stuff will work.

agree that setTimeout() is epic hack. but in the end, once all working, will revert to sanity.

here's the consume() - this is not to say startReader() etc was impeccable to begin with:

PrPq.prototype._consume = () => {
  let pq = this;
  // if not busy, nothing to consume
  if( !pq._conn.libpq.isBusy() ) return Promise.resolve( pq );
  // KLUDGE
  return new Promise( (resolve,reject) => {
    function consume() {
      if( !pq._conn.libpq.consumeInput() ) return reject( pq._conn.libpq.errorMessage() );
      if( !pq._conn.libpq.isBusy() ) return resolve( pq );
      setTimeout( consume );
    }
    consume();
  } );
  //
  /* startReader() to listen to 'readable' events
  return new Promise( (resolve,reject) => {
    pq._conn.libpq.startReader();
    pq._conn.libpq.on('readable', function() {
      // consume input
      if( !pq._conn.libpq.consumeInput() ) return reject( pq._conn.libpq.errorMessage() );
      // note: consuming a 2nd buffer of input later...
      if(pq._conn.libpq.isBusy()) return;
     
      // release listener, stop watcher 
      //~pq._conn.libpq.removeListener('readable', onReadable);
      pq._conn.libpq.removeAllListeners('readable');
      pq._conn.libpq.stopReader();
      // done
      resolve( pq );
    });
  });
  */
}

trailsandtribulations avatar Jun 06 '15 07:06 trailsandtribulations