brianc / node-pg-native

Native (C/C++) bindings to PostgreSQL with sync and async options.
247 stars 44 forks source link

Unique Index Violation no error thrown #55

Open SampsonCrowley opened 7 years ago

SampsonCrowley commented 7 years ago

In production, Unique Index violations from PG never throw an error, they just timeout;

So the client just sits there indefinitely

Function to get client:

import {native as pg} from 'pg';
....
const config = {
  user: auth[0],
  password: auth[1] || null,
  host: params.hostname,
  port: params.port,
  database: params.pathname.split('/')[1],
  max: 20,
  ssl: false,
  idleTimeoutMillis: 10000
};

const pool = new pg.Pool(config);

class DbConn {

...

transaction() {
    return pool.connect().then(client => {
      var transRB = this.rollback, transCM = this.cm;
      return client.query('BEGIN').then(res => {
        client.commit = async function(){
          return transCM(client)
        }

        client.rollback = async function(){
          return transRB(client)
        }

        return client;
      });
    })
  }

async commit(client) {
    await client.query('COMMIT')
    client.release()
    return true;
  }

  async rollback(client) {
    console.log(client);
    try {
      console.info('Rolling back Transaction');
      await client.query('ROLLBACK')
      return "Rolled Back"
    } catch(e) {
      console.log(e)
    } finally {
      client.release();
    }
  }
}

Function Creating transaction:

this.save = async function save(client = null){
      try {
        const changed = this.checkChanged()
        if(!changed) return this;
        this.client = client || await DB.transaction();
        console.log(this.client, client);
        await _runBeforeSave(); //COMPLETES
        await _save(); //NEVER COMPLETES OR THROWS ERROR IF INDEX VIOLATION
        await _runAfterSave() //NEVER GETS HERE

        if(!client) await this.client.commit();
        this.client = null;
        return this.proxy;
      } catch(e){
//THIS CATCH BLOCK IS NOT CALLED UNTIL THE SERVER CLOSES THE CONNECTION BY TIMEOUT
        console.log(e, 'Caught at save root');
        if(!client) this.client.rollback();
        this.client = null;
        if(typeof e === 'string') e = new Error(e);
        return Promise.reject(e);
      }
    }

    const _save = (async function _runSaveInstance() {
      try {
        await _runBelongsTo();
        (this.attributes.id ? await _update() : await _create())
        return _runHasMany()
      } catch(e){
        console.log('Caught in _save()', e);
        return Promise.reject(e)
      }
      // .catch(err => Promise.reject(err))
    }).bind(this)

    const _create = (async function _create(){
      let values = [],
          keys = Object.keys(this.changedAttributes),
          queryString = `INSERT INTO "${this.table}" (`;

      console.log('creating');

      for(var a = 0; a < keys.length; a++){
        const key = keys[a];
        if(dbAttributes[key]){
          queryString += `${key}${a === keys.length - 1 ? '' : ', '}`;
          values.push(this.attributes[key]);
        }
      }

      queryString += ') VALUES (';

      for(var v = 1; v <= values.length; v++){
        queryString += `$${v}${v === values.length ? '' : ', '}`;
      }
      queryString += `) RETURNING *;`;

      console.log('running save query: ', queryString, values); //ALWAYS MAKES IT HERE WITH THE CORRECT QUERY STRING AND VALUES
      try {
         //HANGS INDEFINITELY IF ERROR; COMPLETES ALMOST INSTANTLY WHEN NOT AN INDEX VIOLATION
        const result = await this.client.query(queryString, values);
      } catch(e){
        // 'e' is ALWAYS 'server unexpectedly closed connection' if there's and index violation
        console.log('Object Not Created', e);
        return Promise.reject(e)
      }
//NEVER MAKES IT HERE IF UNIQUE INDEX VIOLATION
      console.log('finished save query: ', result)

      if(!result || !result.rows) return Promise.reject('Object Not Saved');
      mapAttributes(result.rows[0]);
      setParentIds()

    }).bind(this)
SampsonCrowley commented 7 years ago

Important to note I AM using PgPool-II which i think there have been previous reports of not working with parameterized queries and this library.

important to note that it's only a problem with the pg library on node. Rails/activerecord and a python app I built both have no issues with pgpool

brianc commented 7 years ago

Sorry you're running into this issue. Could you break the code down into a smaller more self-contained example? If you can get me something small & reproducible I can take a look at it.